render 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- render/__init__.py +98 -0
- render/client/__init__.py +119 -0
- render/client/client.py +110 -0
- render/client/errors.py +39 -0
- render/client/sse.py +90 -0
- render/client/sse_sync.py +20 -0
- render/client/tests/__init__.py +0 -0
- render/client/tests/test_client.py +379 -0
- render/client/tests/test_method_parity.py +118 -0
- render/client/tests/test_render.py +52 -0
- render/client/tests/test_sse.py +67 -0
- render/client/tests/test_util.py +303 -0
- render/client/tests/test_util_sync.py +73 -0
- render/client/tests/test_workflows_sync.py +313 -0
- render/client/types.py +52 -0
- render/client/util.py +285 -0
- render/client/util_sync.py +83 -0
- render/client/workflows.py +363 -0
- render/client/workflows_sync.py +298 -0
- render/experimental/__init__.py +37 -0
- render/experimental/experimental.py +93 -0
- render/experimental/experimental_sync.py +78 -0
- render/experimental/key_value/__init__.py +33 -0
- render/experimental/key_value/api.py +243 -0
- render/experimental/key_value/compare.py +83 -0
- render/experimental/key_value/provider.py +285 -0
- render/experimental/key_value/tests/__init__.py +0 -0
- render/experimental/key_value/tests/test_api.py +371 -0
- render/experimental/key_value/tests/test_compare.py +136 -0
- render/experimental/key_value/tests/test_provider.py +1016 -0
- render/experimental/key_value/types.py +130 -0
- render/experimental/key_value/utils.py +11 -0
- render/experimental/object/__init__.py +38 -0
- render/experimental/object/api.py +250 -0
- render/experimental/object/api_sync.py +250 -0
- render/experimental/object/client.py +545 -0
- render/experimental/object/client_sync.py +547 -0
- render/experimental/object/e2e_helpers.py +78 -0
- render/experimental/object/tests/__init__.py +0 -0
- render/experimental/object/tests/test_client.py +228 -0
- render/experimental/object/tests/test_e2e.py +125 -0
- render/experimental/object/types.py +87 -0
- render/experimental/sandbox/__init__.py +31 -0
- render/experimental/sandbox/_tar.py +278 -0
- render/experimental/sandbox/api.py +518 -0
- render/experimental/sandbox/api_sync.py +514 -0
- render/experimental/sandbox/client.py +168 -0
- render/experimental/sandbox/client_sync.py +170 -0
- render/experimental/sandbox/errors.py +37 -0
- render/experimental/sandbox/files.py +378 -0
- render/experimental/sandbox/tests/__init__.py +0 -0
- render/experimental/sandbox/tests/test_client.py +802 -0
- render/experimental/sandbox/tests/test_download.py +728 -0
- render/experimental/sandbox/tests/test_e2e.py +252 -0
- render/experimental/sandbox/tests/test_tar.py +153 -0
- render/experimental/sandbox/tests/test_types.py +57 -0
- render/experimental/sandbox/types.py +46 -0
- render/public_api/__init__.py +8 -0
- render/public_api/api/__init__.py +1 -0
- render/public_api/api/artifact_sources/__init__.py +1 -0
- render/public_api/api/artifact_sources/add_or_update_artifact_source_secret_file.py +252 -0
- render/public_api/api/artifact_sources/create_artifact_source.py +243 -0
- render/public_api/api/artifact_sources/delete_artifact_source.py +188 -0
- render/public_api/api/artifact_sources/delete_artifact_source_env_var.py +223 -0
- render/public_api/api/artifact_sources/delete_artifact_source_secret_file.py +223 -0
- render/public_api/api/artifact_sources/get_artifact_source.py +185 -0
- render/public_api/api/artifact_sources/get_env_vars_for_artifact_source.py +241 -0
- render/public_api/api/artifact_sources/link_artifact_source_to_env_group.py +215 -0
- render/public_api/api/artifact_sources/list_artifact_sources.py +338 -0
- render/public_api/api/artifact_sources/list_artifacts_in_artifact_source.py +241 -0
- render/public_api/api/artifact_sources/list_secret_files_for_artifact_source.py +241 -0
- render/public_api/api/artifact_sources/retrieve_artifact_source_env_var.py +225 -0
- render/public_api/api/artifact_sources/retrieve_artifact_source_secret_file.py +225 -0
- render/public_api/api/artifact_sources/unlink_artifact_source_from_env_group.py +209 -0
- render/public_api/api/artifact_sources/update_artifact_source.py +244 -0
- render/public_api/api/artifact_sources/update_artifact_source_env_var.py +257 -0
- render/public_api/api/artifact_sources/update_env_vars_for_artifact_source.py +245 -0
- render/public_api/api/artifact_sources/update_secret_files_for_artifact_source.py +255 -0
- render/public_api/api/audit_logs/__init__.py +1 -0
- render/public_api/api/audit_logs/list_organization_audit_logs.py +303 -0
- render/public_api/api/audit_logs/list_owner_audit_logs.py +303 -0
- render/public_api/api/blueprints/__init__.py +1 -0
- render/public_api/api/blueprints/disconnect_blueprint.py +210 -0
- render/public_api/api/blueprints/list_blueprint_syncs.py +241 -0
- render/public_api/api/blueprints/list_blueprints.py +251 -0
- render/public_api/api/blueprints/retrieve_blueprint.py +200 -0
- render/public_api/api/blueprints/update_blueprint.py +222 -0
- render/public_api/api/blueprints/validate_blueprint.py +217 -0
- render/public_api/api/custom_domains/__init__.py +1 -0
- render/public_api/api/custom_domains/create_custom_domain.py +242 -0
- render/public_api/api/custom_domains/delete_custom_domain.py +216 -0
- render/public_api/api/custom_domains/list_custom_domains.py +346 -0
- render/public_api/api/custom_domains/refresh_custom_domain.py +216 -0
- render/public_api/api/custom_domains/retrieve_custom_domain.py +218 -0
- render/public_api/api/dedicated_i_ps/__init__.py +1 -0
- render/public_api/api/dedicated_i_ps/create_dedicated_ip.py +234 -0
- render/public_api/api/dedicated_i_ps/delete_dedicated_ip.py +198 -0
- render/public_api/api/dedicated_i_ps/list_dedicated_ips.py +223 -0
- render/public_api/api/dedicated_i_ps/retrieve_dedicated_ip.py +200 -0
- render/public_api/api/dedicated_i_ps/update_dedicated_ip.py +244 -0
- render/public_api/api/deploys/__init__.py +1 -0
- render/public_api/api/deploys/cancel_deploy.py +216 -0
- render/public_api/api/deploys/create_deploy.py +231 -0
- render/public_api/api/deploys/list_deploys.py +377 -0
- render/public_api/api/deploys/retrieve_deploy.py +213 -0
- render/public_api/api/deploys/rollback_deploy.py +241 -0
- render/public_api/api/disks/__init__.py +1 -0
- render/public_api/api/disks/add_disk.py +222 -0
- render/public_api/api/disks/delete_disk.py +210 -0
- render/public_api/api/disks/list_disks.py +381 -0
- render/public_api/api/disks/list_snapshots.py +214 -0
- render/public_api/api/disks/restore_snapshot.py +255 -0
- render/public_api/api/disks/retrieve_disk.py +200 -0
- render/public_api/api/disks/update_disk.py +243 -0
- render/public_api/api/environment_groups/__init__.py +1 -0
- render/public_api/api/environment_groups/create_env_group.py +199 -0
- render/public_api/api/environment_groups/delete_env_group.py +192 -0
- render/public_api/api/environment_groups/delete_env_group_env_var.py +201 -0
- render/public_api/api/environment_groups/delete_env_group_secret_file.py +201 -0
- render/public_api/api/environment_groups/link_service_to_env_group.py +211 -0
- render/public_api/api/environment_groups/list_env_groups.py +352 -0
- render/public_api/api/environment_groups/retrieve_env_group.py +190 -0
- render/public_api/api/environment_groups/retrieve_env_group_env_var.py +203 -0
- render/public_api/api/environment_groups/retrieve_env_group_secret_file.py +203 -0
- render/public_api/api/environment_groups/unlink_service_from_env_group.py +209 -0
- render/public_api/api/environment_groups/update_env_group.py +212 -0
- render/public_api/api/environment_groups/update_env_group_env_var.py +230 -0
- render/public_api/api/environment_groups/update_env_group_secret_file.py +225 -0
- render/public_api/api/events/__init__.py +1 -0
- render/public_api/api/events/retrieve_event.py +205 -0
- render/public_api/api/key_value/__init__.py +1 -0
- render/public_api/api/key_value/create_key_value.py +199 -0
- render/public_api/api/key_value/delete_key_value.py +188 -0
- render/public_api/api/key_value/list_key_value.py +380 -0
- render/public_api/api/key_value/resume_key_value.py +203 -0
- render/public_api/api/key_value/retrieve_key_value.py +190 -0
- render/public_api/api/key_value/retrieve_key_value_connection_info.py +194 -0
- render/public_api/api/key_value/suspend_key_value.py +203 -0
- render/public_api/api/key_value/update_key_value.py +221 -0
- render/public_api/api/logs/__init__.py +1 -0
- render/public_api/api/logs/delete_owner_log_stream.py +188 -0
- render/public_api/api/logs/delete_resource_log_stream.py +192 -0
- render/public_api/api/logs/get_owner_log_stream.py +205 -0
- render/public_api/api/logs/get_resource_log_stream.py +209 -0
- render/public_api/api/logs/list_logs.py +535 -0
- render/public_api/api/logs/list_logs_values.py +527 -0
- render/public_api/api/logs/list_resource_log_streams.py +317 -0
- render/public_api/api/logs/subscribe_logs.py +527 -0
- render/public_api/api/logs/update_owner_log_stream.py +231 -0
- render/public_api/api/logs/update_resource_log_stream.py +231 -0
- render/public_api/api/maintenance/__init__.py +1 -0
- render/public_api/api/maintenance/list_maintenance.py +249 -0
- render/public_api/api/maintenance/retrieve_maintenance.py +190 -0
- render/public_api/api/maintenance/trigger_maintenance.py +212 -0
- render/public_api/api/maintenance/update_maintenance.py +222 -0
- render/public_api/api/metrics/__init__.py +1 -0
- render/public_api/api/metrics/delete_owner_metrics_stream.py +172 -0
- render/public_api/api/metrics/get_active_connections.py +237 -0
- render/public_api/api/metrics/get_bandwidth.py +237 -0
- render/public_api/api/metrics/get_bandwidth_sources.py +250 -0
- render/public_api/api/metrics/get_cpu.py +287 -0
- render/public_api/api/metrics/get_cpu_limit.py +267 -0
- render/public_api/api/metrics/get_cpu_target.py +267 -0
- render/public_api/api/metrics/get_disk_capacity.py +252 -0
- render/public_api/api/metrics/get_disk_usage.py +252 -0
- render/public_api/api/metrics/get_http_latency.py +297 -0
- render/public_api/api/metrics/get_http_requests.py +302 -0
- render/public_api/api/metrics/get_instance_count.py +252 -0
- render/public_api/api/metrics/get_memory.py +267 -0
- render/public_api/api/metrics/get_memory_limit.py +267 -0
- render/public_api/api/metrics/get_memory_target.py +267 -0
- render/public_api/api/metrics/get_owner_metrics_stream.py +174 -0
- render/public_api/api/metrics/get_replication_lag.py +237 -0
- render/public_api/api/metrics/get_task_runs_completed.py +281 -0
- render/public_api/api/metrics/get_task_runs_queued.py +237 -0
- render/public_api/api/metrics/list_application_filter_values.py +254 -0
- render/public_api/api/metrics/list_http_filter_values.py +282 -0
- render/public_api/api/metrics/list_path_filter_values.py +289 -0
- render/public_api/api/metrics/upsert_owner_metrics_stream.py +192 -0
- render/public_api/api/notification_settings/__init__.py +1 -0
- render/public_api/api/notification_settings/list_notification_overrides.py +255 -0
- render/public_api/api/notification_settings/patch_owner_notification_settings.py +207 -0
- render/public_api/api/notification_settings/patch_service_notification_overrides.py +207 -0
- render/public_api/api/notification_settings/retrieve_owner_notification_settings.py +193 -0
- render/public_api/api/notification_settings/retrieve_service_notification_overrides.py +193 -0
- render/public_api/api/object_storage/__init__.py +1 -0
- render/public_api/api/object_storage/delete_object.py +215 -0
- render/public_api/api/object_storage/get_object.py +221 -0
- render/public_api/api/object_storage/list_objects.py +240 -0
- render/public_api/api/object_storage/put_object.py +248 -0
- render/public_api/api/one_off_jobs/__init__.py +1 -0
- render/public_api/api/one_off_jobs/cancel_job.py +203 -0
- render/public_api/api/one_off_jobs/list_job.py +367 -0
- render/public_api/api/one_off_jobs/post_job.py +216 -0
- render/public_api/api/one_off_jobs/retrieve_job.py +203 -0
- render/public_api/api/postgres/__init__.py +1 -0
- render/public_api/api/postgres/create_postgres.py +199 -0
- render/public_api/api/postgres/create_postgres_export.py +207 -0
- render/public_api/api/postgres/create_postgres_user.py +229 -0
- render/public_api/api/postgres/delete_postgres.py +192 -0
- render/public_api/api/postgres/delete_postgres_user.py +201 -0
- render/public_api/api/postgres/failover_postgres.py +207 -0
- render/public_api/api/postgres/list_available_postgres_parameter_overrides.py +190 -0
- render/public_api/api/postgres/list_postgres.py +418 -0
- render/public_api/api/postgres/list_postgres_export.py +199 -0
- render/public_api/api/postgres/list_postgres_parameter_overrides.py +190 -0
- render/public_api/api/postgres/list_postgres_processes.py +190 -0
- render/public_api/api/postgres/list_postgres_sizes.py +190 -0
- render/public_api/api/postgres/list_postgres_table_scans.py +190 -0
- render/public_api/api/postgres/list_postgres_top_queries.py +194 -0
- render/public_api/api/postgres/list_postgres_users.py +195 -0
- render/public_api/api/postgres/promote_postgres.py +203 -0
- render/public_api/api/postgres/recover_postgres.py +231 -0
- render/public_api/api/postgres/restart_postgres.py +203 -0
- render/public_api/api/postgres/resume_postgres.py +203 -0
- render/public_api/api/postgres/retrieve_postgres.py +190 -0
- render/public_api/api/postgres/retrieve_postgres_connection_info.py +194 -0
- render/public_api/api/postgres/retrieve_postgres_recovery_info.py +194 -0
- render/public_api/api/postgres/setup_postgres_replication.py +231 -0
- render/public_api/api/postgres/suspend_postgres.py +203 -0
- render/public_api/api/postgres/update_postgres.py +217 -0
- render/public_api/api/postgres/update_postgres_parameter_overrides.py +217 -0
- render/public_api/api/projects_environments/__init__.py +1 -0
- render/public_api/api/projects_environments/add_resources_to_environment.py +212 -0
- render/public_api/api/projects_environments/create_environment.py +199 -0
- render/public_api/api/projects_environments/create_project.py +199 -0
- render/public_api/api/projects_environments/delete_environment.py +216 -0
- render/public_api/api/projects_environments/delete_project.py +216 -0
- render/public_api/api/projects_environments/list_environments.py +369 -0
- render/public_api/api/projects_environments/list_projects.py +329 -0
- render/public_api/api/projects_environments/remove_resources_from_environment.py +211 -0
- render/public_api/api/projects_environments/retrieve_environment.py +190 -0
- render/public_api/api/projects_environments/retrieve_project.py +190 -0
- render/public_api/api/projects_environments/update_environment.py +212 -0
- render/public_api/api/projects_environments/update_project.py +224 -0
- render/public_api/api/redis_deprecated/__init__.py +1 -0
- render/public_api/api/redis_deprecated/create_redis.py +199 -0
- render/public_api/api/redis_deprecated/delete_redis.py +188 -0
- render/public_api/api/redis_deprecated/list_redis.py +384 -0
- render/public_api/api/redis_deprecated/resume_redis.py +203 -0
- render/public_api/api/redis_deprecated/retrieve_redis.py +190 -0
- render/public_api/api/redis_deprecated/retrieve_redis_connection_info.py +194 -0
- render/public_api/api/redis_deprecated/suspend_redis.py +203 -0
- render/public_api/api/redis_deprecated/update_redis.py +217 -0
- render/public_api/api/registry_credentials/__init__.py +1 -0
- render/public_api/api/registry_credentials/create_registry_credential.py +209 -0
- render/public_api/api/registry_credentials/delete_registry_credential.py +198 -0
- render/public_api/api/registry_credentials/list_registry_credentials.py +370 -0
- render/public_api/api/registry_credentials/retrieve_registry_credential.py +200 -0
- render/public_api/api/registry_credentials/update_registry_credential.py +241 -0
- render/public_api/api/sandboxes/__init__.py +1 -0
- render/public_api/api/sandboxes/connect_sandbox_files.py +281 -0
- render/public_api/api/sandboxes/connect_sandbox_run.py +271 -0
- render/public_api/api/sandboxes/create_sandbox.py +199 -0
- render/public_api/api/sandboxes/list_sandbox_files.py +244 -0
- render/public_api/api/sandboxes/list_sandbox_groups.py +216 -0
- render/public_api/api/sandboxes/list_sandboxes.py +269 -0
- render/public_api/api/sandboxes/retrieve_sandbox.py +211 -0
- render/public_api/api/sandboxes/retrieve_sandbox_execution.py +224 -0
- render/public_api/api/sandboxes/stream_sandbox_logs.py +274 -0
- render/public_api/api/sandboxes/terminate_sandbox.py +217 -0
- render/public_api/api/sandboxes/update_sandbox_exec.py +258 -0
- render/public_api/api/services/__init__.py +1 -0
- render/public_api/api/services/add_or_update_secret_file.py +252 -0
- render/public_api/api/services/autoscale_service.py +230 -0
- render/public_api/api/services/create_ephemeral_shell.py +217 -0
- render/public_api/api/services/create_service.py +204 -0
- render/public_api/api/services/delete_autoscaling_config.py +198 -0
- render/public_api/api/services/delete_env_var.py +223 -0
- render/public_api/api/services/delete_secret_file.py +223 -0
- render/public_api/api/services/delete_service.py +198 -0
- render/public_api/api/services/get_env_vars_for_service.py +241 -0
- render/public_api/api/services/list_events.py +288 -0
- render/public_api/api/services/list_instances.py +190 -0
- render/public_api/api/services/list_secret_files_for_service.py +241 -0
- render/public_api/api/services/list_services.py +439 -0
- render/public_api/api/services/preview_service.py +227 -0
- render/public_api/api/services/purge_cache.py +203 -0
- render/public_api/api/services/restart_service.py +211 -0
- render/public_api/api/services/resume_service.py +203 -0
- render/public_api/api/services/retrieve_env_var.py +225 -0
- render/public_api/api/services/retrieve_secret_file.py +225 -0
- render/public_api/api/services/retrieve_service.py +190 -0
- render/public_api/api/services/retrieve_service_outbound_ips.py +207 -0
- render/public_api/api/services/scale_service.py +237 -0
- render/public_api/api/services/suspend_service.py +203 -0
- render/public_api/api/services/update_env_var.py +257 -0
- render/public_api/api/services/update_env_vars_for_service.py +241 -0
- render/public_api/api/services/update_secret_files_for_service.py +255 -0
- render/public_api/api/services/update_service.py +227 -0
- render/public_api/api/services_cron_jobs/__init__.py +1 -0
- render/public_api/api/services_cron_jobs/cancel_cron_job_run.py +183 -0
- render/public_api/api/services_cron_jobs/run_cron_job.py +185 -0
- render/public_api/api/services_headers/__init__.py +1 -0
- render/public_api/api/services_headers/add_headers.py +222 -0
- render/public_api/api/services_headers/delete_header.py +211 -0
- render/public_api/api/services_headers/list_headers.py +302 -0
- render/public_api/api/services_headers/update_headers.py +243 -0
- render/public_api/api/services_routes/__init__.py +1 -0
- render/public_api/api/services_routes/add_route.py +227 -0
- render/public_api/api/services_routes/delete_route.py +211 -0
- render/public_api/api/services_routes/list_routes.py +306 -0
- render/public_api/api/services_routes/patch_route.py +259 -0
- render/public_api/api/services_routes/put_routes.py +259 -0
- render/public_api/api/users/__init__.py +1 -0
- render/public_api/api/users/get_user.py +161 -0
- render/public_api/api/webhooks/__init__.py +1 -0
- render/public_api/api/webhooks/create_webhook.py +199 -0
- render/public_api/api/webhooks/delete_webhook.py +183 -0
- render/public_api/api/webhooks/list_webhook_events.py +268 -0
- render/public_api/api/webhooks/list_webhooks.py +237 -0
- render/public_api/api/webhooks/retrieve_webhook.py +185 -0
- render/public_api/api/webhooks/update_webhook.py +212 -0
- render/public_api/api/workflow_tasks_beta/__init__.py +1 -0
- render/public_api/api/workflow_tasks_beta/cancel_task_run.py +188 -0
- render/public_api/api/workflow_tasks_beta/create_task.py +204 -0
- render/public_api/api/workflow_tasks_beta/get_task.py +190 -0
- render/public_api/api/workflow_tasks_beta/get_task_run.py +190 -0
- render/public_api/api/workflow_tasks_beta/list_task_runs.py +317 -0
- render/public_api/api/workflow_tasks_beta/list_tasks.py +298 -0
- render/public_api/api/workflow_tasks_beta/stream_task_runs_events.py +230 -0
- render/public_api/api/workflows_beta/__init__.py +1 -0
- render/public_api/api/workflows_beta/create_workflow.py +199 -0
- render/public_api/api/workflows_beta/create_workflow_version.py +197 -0
- render/public_api/api/workflows_beta/delete_workflow.py +188 -0
- render/public_api/api/workflows_beta/get_workflow.py +190 -0
- render/public_api/api/workflows_beta/get_workflow_version.py +190 -0
- render/public_api/api/workflows_beta/list_workflow_versions.py +275 -0
- render/public_api/api/workflows_beta/list_workflows.py +298 -0
- render/public_api/api/workflows_beta/update_workflow.py +212 -0
- render/public_api/api/workspaces/__init__.py +1 -0
- render/public_api/api/workspaces/list_owners.py +255 -0
- render/public_api/api/workspaces/remove_workspace_member.py +206 -0
- render/public_api/api/workspaces/retrieve_owner.py +207 -0
- render/public_api/api/workspaces/retrieve_owner_members.py +200 -0
- render/public_api/api/workspaces/update_workspace_member.py +235 -0
- render/public_api/client.py +268 -0
- render/public_api/errors.py +16 -0
- render/public_api/models/__init__.py +861 -0
- render/public_api/models/add_headers_response_201.py +72 -0
- render/public_api/models/add_or_update_artifact_source_secret_file_body.py +59 -0
- render/public_api/models/add_or_update_secret_file_body.py +59 -0
- render/public_api/models/artifact.py +145 -0
- render/public_api/models/artifact_fetch_failed.py +67 -0
- render/public_api/models/artifact_source.py +162 -0
- render/public_api/models/artifact_source_changed.py +70 -0
- render/public_api/models/artifact_source_git.py +167 -0
- render/public_api/models/artifact_source_git_region.py +12 -0
- render/public_api/models/artifact_source_git_runtime.py +14 -0
- render/public_api/models/artifact_source_image.py +78 -0
- render/public_api/models/artifact_source_patch_git.py +175 -0
- render/public_api/models/artifact_source_patch_git_region.py +12 -0
- render/public_api/models/artifact_source_patch_git_runtime.py +14 -0
- render/public_api/models/artifact_source_patch_image.py +71 -0
- render/public_api/models/artifact_source_patch_input.py +101 -0
- render/public_api/models/artifact_source_post_input.py +213 -0
- render/public_api/models/artifact_source_service_link.py +67 -0
- render/public_api/models/artifact_source_with_cursor.py +73 -0
- render/public_api/models/audit_log.py +113 -0
- render/public_api/models/audit_log_actor.py +80 -0
- render/public_api/models/audit_log_actor_type.py +10 -0
- render/public_api/models/audit_log_event.py +87 -0
- render/public_api/models/audit_log_metadata.py +49 -0
- render/public_api/models/audit_log_status.py +9 -0
- render/public_api/models/audit_log_with_cursor.py +73 -0
- render/public_api/models/auto_deploy.py +9 -0
- render/public_api/models/auto_deploy_disabled.py +79 -0
- render/public_api/models/auto_deploy_enabled.py +68 -0
- render/public_api/models/auto_deploy_trigger.py +10 -0
- render/public_api/models/autoscaling_config.py +89 -0
- render/public_api/models/autoscaling_config_changed.py +83 -0
- render/public_api/models/autoscaling_criteria.py +73 -0
- render/public_api/models/autoscaling_criteria_percentage.py +68 -0
- render/public_api/models/autoscaling_ended.py +67 -0
- render/public_api/models/autoscaling_started.py +105 -0
- render/public_api/models/background_worker_details.py +256 -0
- render/public_api/models/background_worker_details_patch.py +184 -0
- render/public_api/models/background_worker_details_post.py +266 -0
- render/public_api/models/blueprint.py +128 -0
- render/public_api/models/blueprint_detail.py +150 -0
- render/public_api/models/blueprint_patch.py +77 -0
- render/public_api/models/blueprint_with_cursor.py +74 -0
- render/public_api/models/branch_deleted.py +67 -0
- render/public_api/models/build.py +148 -0
- render/public_api/models/build_config.py +96 -0
- render/public_api/models/build_deploy_end_reason.py +122 -0
- render/public_api/models/build_deploy_end_reason_id.py +59 -0
- render/public_api/models/build_deploy_trigger.py +150 -0
- render/public_api/models/build_ended.py +91 -0
- render/public_api/models/build_filter.py +67 -0
- render/public_api/models/build_plan.py +9 -0
- render/public_api/models/build_runtime.py +14 -0
- render/public_api/models/build_started.py +73 -0
- render/public_api/models/build_status.py +13 -0
- render/public_api/models/cache.py +61 -0
- render/public_api/models/cache_profile.py +10 -0
- render/public_api/models/cidr_block_and_description.py +67 -0
- render/public_api/models/commit_ignored.py +67 -0
- render/public_api/models/commit_ref.py +59 -0
- render/public_api/models/connect_sandbox_files_operation.py +9 -0
- render/public_api/models/connect_sandbox_run_operation.py +9 -0
- render/public_api/models/create_custom_domain_body.py +59 -0
- render/public_api/models/create_deploy_body.py +130 -0
- render/public_api/models/create_deploy_body_clear_cache.py +9 -0
- render/public_api/models/create_ephemeral_shell_body.py +69 -0
- render/public_api/models/create_ephemeral_shell_response_201.py +59 -0
- render/public_api/models/create_registry_credential_body.py +93 -0
- render/public_api/models/create_version.py +70 -0
- render/public_api/models/credential_create_input.py +59 -0
- render/public_api/models/cron_job_details.py +163 -0
- render/public_api/models/cron_job_details_patch.py +138 -0
- render/public_api/models/cron_job_details_post.py +172 -0
- render/public_api/models/cron_job_run.py +123 -0
- render/public_api/models/cron_job_run_ended.py +110 -0
- render/public_api/models/cron_job_run_started.py +59 -0
- render/public_api/models/cron_job_run_status.py +11 -0
- render/public_api/models/custom_domain.py +135 -0
- render/public_api/models/custom_domain_domain_type.py +9 -0
- render/public_api/models/custom_domain_server.py +68 -0
- render/public_api/models/custom_domain_verification_status.py +9 -0
- render/public_api/models/custom_domain_with_cursor.py +73 -0
- render/public_api/models/database_role.py +9 -0
- render/public_api/models/database_status.py +18 -0
- render/public_api/models/dedicated_ip.py +146 -0
- render/public_api/models/dedicated_ip_status.py +14 -0
- render/public_api/models/dedicated_ippatch.py +81 -0
- render/public_api/models/dedicated_ippost.py +100 -0
- render/public_api/models/deploy.py +211 -0
- render/public_api/models/deploy_commit.py +86 -0
- render/public_api/models/deploy_ended.py +102 -0
- render/public_api/models/deploy_image.py +78 -0
- render/public_api/models/deploy_mode.py +9 -0
- render/public_api/models/deploy_started.py +85 -0
- render/public_api/models/deploy_status.py +18 -0
- render/public_api/models/deploy_trigger.py +17 -0
- render/public_api/models/deploy_with_cursor.py +81 -0
- render/public_api/models/disk.py +83 -0
- render/public_api/models/disk_created.py +67 -0
- render/public_api/models/disk_deleted.py +59 -0
- render/public_api/models/disk_details.py +112 -0
- render/public_api/models/disk_patch.py +77 -0
- render/public_api/models/disk_post.py +83 -0
- render/public_api/models/disk_snapshot.py +88 -0
- render/public_api/models/disk_updated.py +75 -0
- render/public_api/models/disk_with_cursor.py +73 -0
- render/public_api/models/docker_details.py +108 -0
- render/public_api/models/docker_details_patch.py +86 -0
- render/public_api/models/docker_details_post.py +86 -0
- render/public_api/models/edge_cache_disabled.py +65 -0
- render/public_api/models/edge_cache_enabled.py +65 -0
- render/public_api/models/edge_cache_purged.py +65 -0
- render/public_api/models/edge_cache_trigger.py +91 -0
- render/public_api/models/env_group.py +162 -0
- render/public_api/models/env_group_link.py +77 -0
- render/public_api/models/env_group_meta.py +126 -0
- render/public_api/models/env_group_patch_input.py +59 -0
- render/public_api/models/env_group_post_input.py +161 -0
- render/public_api/models/env_var.py +67 -0
- render/public_api/models/env_var_generate_value.py +59 -0
- render/public_api/models/env_var_key_generate_value.py +68 -0
- render/public_api/models/env_var_key_value.py +67 -0
- render/public_api/models/env_var_value.py +59 -0
- render/public_api/models/env_var_with_cursor.py +73 -0
- render/public_api/models/environment.py +152 -0
- render/public_api/models/environment_patch_input.py +112 -0
- render/public_api/models/environment_post_input.py +122 -0
- render/public_api/models/environment_resources_post_input.py +59 -0
- render/public_api/models/environment_with_cursor.py +74 -0
- render/public_api/models/error.py +79 -0
- render/public_api/models/error_code.py +10 -0
- render/public_api/models/event.py +1122 -0
- render/public_api/models/event_status.py +10 -0
- render/public_api/models/event_type.py +74 -0
- render/public_api/models/execution.py +144 -0
- render/public_api/models/execution_operation.py +11 -0
- render/public_api/models/execution_type.py +9 -0
- render/public_api/models/failure_reason.py +138 -0
- render/public_api/models/filter_application_values_collection_item.py +78 -0
- render/public_api/models/filter_application_values_collection_item_filter.py +8 -0
- render/public_api/models/filter_http_values_collection_item.py +78 -0
- render/public_api/models/filter_http_values_collection_item_filter.py +9 -0
- render/public_api/models/get_bandwidth_sources_response_200.py +75 -0
- render/public_api/models/get_bandwidth_sources_response_200_data_item.py +101 -0
- render/public_api/models/get_bandwidth_sources_response_200_data_item_labels.py +78 -0
- render/public_api/models/get_bandwidth_sources_response_200_data_item_labels_traffic_source.py +12 -0
- render/public_api/models/get_bandwidth_sources_response_200_data_item_values_item.py +68 -0
- render/public_api/models/get_cpu_aggregation_method.py +10 -0
- render/public_api/models/get_http_requests_aggregate_by.py +9 -0
- render/public_api/models/get_object_output.py +71 -0
- render/public_api/models/get_task_runs_completed_aggregate_by.py +8 -0
- render/public_api/models/get_task_runs_completed_state.py +9 -0
- render/public_api/models/header.py +83 -0
- render/public_api/models/header_input.py +76 -0
- render/public_api/models/header_with_cursor.py +73 -0
- render/public_api/models/image.py +80 -0
- render/public_api/models/image_pull_failed.py +67 -0
- render/public_api/models/initial_deploy_hook_ended.py +59 -0
- render/public_api/models/initial_deploy_hook_started.py +59 -0
- render/public_api/models/instance_count_changed.py +67 -0
- render/public_api/models/instance_type_changed.py +67 -0
- render/public_api/models/internal_routing.py +8 -0
- render/public_api/models/job.py +144 -0
- render/public_api/models/job_run_ended.py +92 -0
- render/public_api/models/job_status.py +12 -0
- render/public_api/models/job_with_cursor.py +73 -0
- render/public_api/models/key_value.py +182 -0
- render/public_api/models/key_value_available.py +44 -0
- render/public_api/models/key_value_config_restart.py +67 -0
- render/public_api/models/key_value_connection_info.py +76 -0
- render/public_api/models/key_value_detail.py +192 -0
- render/public_api/models/key_value_options.py +79 -0
- render/public_api/models/key_value_patch_input.py +138 -0
- render/public_api/models/key_value_plan.py +13 -0
- render/public_api/models/key_value_post_input.py +166 -0
- render/public_api/models/key_value_unhealthy.py +44 -0
- render/public_api/models/key_value_with_cursor.py +73 -0
- render/public_api/models/label.py +68 -0
- render/public_api/models/list_custom_domains_domain_type.py +9 -0
- render/public_api/models/list_custom_domains_verification_status.py +9 -0
- render/public_api/models/list_logs_response_200.py +102 -0
- render/public_api/models/list_logs_values_label.py +13 -0
- render/public_api/models/list_objects_response.py +92 -0
- render/public_api/models/list_postgres_suspended_item.py +9 -0
- render/public_api/models/list_postgres_users_response_200_item.py +86 -0
- render/public_api/models/list_routes_type_item.py +9 -0
- render/public_api/models/list_services_suspended_item.py +9 -0
- render/public_api/models/log.py +100 -0
- render/public_api/models/log_direction.py +9 -0
- render/public_api/models/log_label.py +70 -0
- render/public_api/models/log_label_name.py +22 -0
- render/public_api/models/log_stream_owner_update.py +80 -0
- render/public_api/models/log_stream_preview_setting.py +9 -0
- render/public_api/models/log_stream_resource_update.py +80 -0
- render/public_api/models/log_stream_setting.py +9 -0
- render/public_api/models/maintenance_ended.py +44 -0
- render/public_api/models/maintenance_mode.py +68 -0
- render/public_api/models/maintenance_mode_enabled.py +59 -0
- render/public_api/models/maintenance_mode_uri_updated.py +67 -0
- render/public_api/models/maintenance_run.py +105 -0
- render/public_api/models/maintenance_run_patch.py +69 -0
- render/public_api/models/maintenance_run_with_resource.py +114 -0
- render/public_api/models/maintenance_started.py +65 -0
- render/public_api/models/maintenance_state.py +13 -0
- render/public_api/models/maintenance_trigger.py +91 -0
- render/public_api/models/maxmemory_policy.py +15 -0
- render/public_api/models/metrics_stream.py +77 -0
- render/public_api/models/metrics_stream_input.py +86 -0
- render/public_api/models/native_environment_details.py +78 -0
- render/public_api/models/native_environment_details_patch.py +68 -0
- render/public_api/models/native_environment_details_post.py +68 -0
- render/public_api/models/notification_override.py +78 -0
- render/public_api/models/notification_override_with_cursor.py +73 -0
- render/public_api/models/notification_service_override.py +78 -0
- render/public_api/models/notification_service_override_patch.py +84 -0
- render/public_api/models/notification_setting.py +93 -0
- render/public_api/models/notification_setting_patch.py +85 -0
- render/public_api/models/notify_override.py +11 -0
- render/public_api/models/notify_preview_override.py +10 -0
- render/public_api/models/notify_setting.py +10 -0
- render/public_api/models/notify_setting_v2.py +10 -0
- render/public_api/models/object_metadata.py +77 -0
- render/public_api/models/object_with_cursor.py +73 -0
- render/public_api/models/oom_killed.py +59 -0
- render/public_api/models/otel_provider_type.py +16 -0
- render/public_api/models/outbound_ips.py +81 -0
- render/public_api/models/outbound_ips_type.py +9 -0
- render/public_api/models/owner.py +121 -0
- render/public_api/models/owner_log_stream_setting.py +86 -0
- render/public_api/models/owner_type.py +9 -0
- render/public_api/models/owner_with_cursor.py +81 -0
- render/public_api/models/paid_plan.py +13 -0
- render/public_api/models/patch_route_response_200.py +72 -0
- render/public_api/models/persistence_mode.py +10 -0
- render/public_api/models/pipeline_minutes_exhausted.py +73 -0
- render/public_api/models/plan.py +22 -0
- render/public_api/models/post_job_body.py +70 -0
- render/public_api/models/postgres.py +300 -0
- render/public_api/models/postgres_available.py +44 -0
- render/public_api/models/postgres_available_parameter_override.py +106 -0
- render/public_api/models/postgres_available_parameter_overrides_result.py +75 -0
- render/public_api/models/postgres_backup_completed.py +44 -0
- render/public_api/models/postgres_backup_failed.py +44 -0
- render/public_api/models/postgres_backup_started.py +44 -0
- render/public_api/models/postgres_cluster_leader_changed.py +59 -0
- render/public_api/models/postgres_connection_info.py +103 -0
- render/public_api/models/postgres_connection_pool_changed.py +91 -0
- render/public_api/models/postgres_connection_pool_enabled_changed.py +83 -0
- render/public_api/models/postgres_created.py +72 -0
- render/public_api/models/postgres_detail.py +336 -0
- render/public_api/models/postgres_detail_suspended.py +9 -0
- render/public_api/models/postgres_disk_size_changed.py +91 -0
- render/public_api/models/postgres_export.py +80 -0
- render/public_api/models/postgres_ha_status_changed.py +67 -0
- render/public_api/models/postgres_parameter_overrides.py +44 -0
- render/public_api/models/postgres_parameter_overrides_wrapper.py +65 -0
- render/public_api/models/postgres_patch_input.py +199 -0
- render/public_api/models/postgres_pitr_checkpoint_completed.py +44 -0
- render/public_api/models/postgres_pitr_checkpoint_failed.py +44 -0
- render/public_api/models/postgres_pitr_checkpoint_started.py +44 -0
- render/public_api/models/postgres_plans.py +35 -0
- render/public_api/models/postgres_post_input.py +254 -0
- render/public_api/models/postgres_process.py +185 -0
- render/public_api/models/postgres_processes_result.py +73 -0
- render/public_api/models/postgres_put_parameter_overrides_result.py +82 -0
- render/public_api/models/postgres_query_statistic.py +222 -0
- render/public_api/models/postgres_read_replica_stale.py +44 -0
- render/public_api/models/postgres_read_replicas_changed.py +67 -0
- render/public_api/models/postgres_replication_setup_input.py +96 -0
- render/public_api/models/postgres_restarted.py +72 -0
- render/public_api/models/postgres_size.py +96 -0
- render/public_api/models/postgres_sizes_result.py +73 -0
- render/public_api/models/postgres_suspended.py +9 -0
- render/public_api/models/postgres_table_scan.py +87 -0
- render/public_api/models/postgres_table_scans_result.py +73 -0
- render/public_api/models/postgres_top_queries_result.py +73 -0
- render/public_api/models/postgres_unavailable.py +44 -0
- render/public_api/models/postgres_upgrade_failed.py +67 -0
- render/public_api/models/postgres_upgrade_started.py +91 -0
- render/public_api/models/postgres_upgrade_succeeded.py +67 -0
- render/public_api/models/postgres_version.py +15 -0
- render/public_api/models/postgres_with_cursor.py +73 -0
- render/public_api/models/pre_deploy_ended.py +99 -0
- render/public_api/models/pre_deploy_started.py +67 -0
- render/public_api/models/preview_input.py +94 -0
- render/public_api/models/previews.py +67 -0
- render/public_api/models/previews_generation.py +10 -0
- render/public_api/models/private_service_details.py +282 -0
- render/public_api/models/private_service_details_patch.py +184 -0
- render/public_api/models/private_service_details_post.py +266 -0
- render/public_api/models/project.py +108 -0
- render/public_api/models/project_patch_input.py +60 -0
- render/public_api/models/project_post_environment_input.py +114 -0
- render/public_api/models/project_post_input.py +89 -0
- render/public_api/models/project_with_cursor.py +73 -0
- render/public_api/models/protected_status.py +9 -0
- render/public_api/models/pull_request_previews_enabled.py +9 -0
- render/public_api/models/put_object_input.py +59 -0
- render/public_api/models/put_object_output.py +79 -0
- render/public_api/models/read_replica.py +91 -0
- render/public_api/models/read_replica_input.py +83 -0
- render/public_api/models/recovery_info.py +80 -0
- render/public_api/models/recovery_info_recovery_status.py +10 -0
- render/public_api/models/recovery_input.py +114 -0
- render/public_api/models/redis.py +182 -0
- render/public_api/models/redis_connection_info.py +76 -0
- render/public_api/models/redis_detail.py +192 -0
- render/public_api/models/redis_options.py +79 -0
- render/public_api/models/redis_patch_input.py +138 -0
- render/public_api/models/redis_plan.py +13 -0
- render/public_api/models/redis_post_input.py +166 -0
- render/public_api/models/redis_with_cursor.py +73 -0
- render/public_api/models/region.py +12 -0
- render/public_api/models/registry_credential.py +95 -0
- render/public_api/models/registry_credential_registry.py +12 -0
- render/public_api/models/registry_credential_summary.py +67 -0
- render/public_api/models/render_subdomain_policy.py +9 -0
- render/public_api/models/resource.py +67 -0
- render/public_api/models/resource_log_stream_setting.py +87 -0
- render/public_api/models/resource_ref.py +77 -0
- render/public_api/models/resource_ref_type.py +17 -0
- render/public_api/models/rollback_deploy_body.py +59 -0
- render/public_api/models/route.py +93 -0
- render/public_api/models/route_patch.py +60 -0
- render/public_api/models/route_post.py +88 -0
- render/public_api/models/route_put.py +77 -0
- render/public_api/models/route_type.py +9 -0
- render/public_api/models/route_with_cursor.py +73 -0
- render/public_api/models/run_task.py +96 -0
- render/public_api/models/runtime.py +12 -0
- render/public_api/models/sandbox.py +149 -0
- render/public_api/models/sandbox_connect_request.py +62 -0
- render/public_api/models/sandbox_connect_response.py +98 -0
- render/public_api/models/sandbox_directory_listing.py +82 -0
- render/public_api/models/sandbox_exec_update_request.py +60 -0
- render/public_api/models/sandbox_exec_update_response.py +130 -0
- render/public_api/models/sandbox_file_entry.py +98 -0
- render/public_api/models/sandbox_file_entry_type.py +10 -0
- render/public_api/models/sandbox_group.py +141 -0
- render/public_api/models/sandbox_group_with_cursor.py +74 -0
- render/public_api/models/sandbox_network_policy.py +61 -0
- render/public_api/models/sandbox_network_policy_default.py +9 -0
- render/public_api/models/sandbox_plan.py +10 -0
- render/public_api/models/sandbox_post.py +138 -0
- render/public_api/models/sandbox_post_env.py +44 -0
- render/public_api/models/sandbox_status.py +13 -0
- render/public_api/models/sandbox_with_cursor.py +74 -0
- render/public_api/models/scale_service_body.py +59 -0
- render/public_api/models/schemas_build_filter.py +70 -0
- render/public_api/models/schemas_image.py +96 -0
- render/public_api/models/schemas_user.py +68 -0
- render/public_api/models/secret_file.py +67 -0
- render/public_api/models/secret_file_input.py +67 -0
- render/public_api/models/secret_file_with_cursor.py +73 -0
- render/public_api/models/server_available.py +44 -0
- render/public_api/models/server_failed.py +81 -0
- render/public_api/models/server_hardware_failure.py +44 -0
- render/public_api/models/server_port.py +69 -0
- render/public_api/models/server_port_protocol.py +9 -0
- render/public_api/models/server_restarted.py +66 -0
- render/public_api/models/service_disk.py +78 -0
- render/public_api/models/service_env.py +15 -0
- render/public_api/models/service_event.py +770 -0
- render/public_api/models/service_event_type.py +48 -0
- render/public_api/models/service_event_with_cursor.py +66 -0
- render/public_api/models/service_instance.py +69 -0
- render/public_api/models/service_patch.py +273 -0
- render/public_api/models/service_post.py +357 -0
- render/public_api/models/service_resumed.py +44 -0
- render/public_api/models/service_runtime.py +15 -0
- render/public_api/models/service_suspended.py +44 -0
- render/public_api/models/service_type.py +12 -0
- render/public_api/models/service_type_short.py +12 -0
- render/public_api/models/setup_postgres_replication_response_201.py +44 -0
- render/public_api/models/snapshot_restore_post.py +72 -0
- render/public_api/models/static_site_details.py +183 -0
- render/public_api/models/static_site_details_patch.py +147 -0
- render/public_api/models/static_site_details_post.py +189 -0
- render/public_api/models/status.py +12 -0
- render/public_api/models/stream_sandbox_logs_accept.py +8 -0
- render/public_api/models/stream_task_runs_events_accept.py +8 -0
- render/public_api/models/suspender_added.py +83 -0
- render/public_api/models/suspender_removed.py +83 -0
- render/public_api/models/suspender_type.py +14 -0
- render/public_api/models/sync.py +118 -0
- render/public_api/models/sync_state.py +12 -0
- render/public_api/models/sync_with_cursor.py +74 -0
- render/public_api/models/task.py +97 -0
- render/public_api/models/task_attempt.py +121 -0
- render/public_api/models/task_attempt_details.py +141 -0
- render/public_api/models/task_data_type_1.py +44 -0
- render/public_api/models/task_run.py +168 -0
- render/public_api/models/task_run_details.py +216 -0
- render/public_api/models/task_run_status.py +14 -0
- render/public_api/models/task_run_with_cursor.py +73 -0
- render/public_api/models/task_with_cursor.py +73 -0
- render/public_api/models/team_member.py +102 -0
- render/public_api/models/team_member_role.py +12 -0
- render/public_api/models/team_member_status.py +9 -0
- render/public_api/models/time_series.py +100 -0
- render/public_api/models/time_series_value.py +70 -0
- render/public_api/models/update_env_group_secret_file_body.py +59 -0
- render/public_api/models/update_registry_credential_body.py +85 -0
- render/public_api/models/update_workspace_member_body.py +61 -0
- render/public_api/models/user.py +67 -0
- render/public_api/models/validate_blueprint_request.py +84 -0
- render/public_api/models/validate_blueprint_response.py +105 -0
- render/public_api/models/validation_error.py +88 -0
- render/public_api/models/validation_plan_summary.py +107 -0
- render/public_api/models/web_service_details.py +385 -0
- render/public_api/models/web_service_details_patch.py +270 -0
- render/public_api/models/web_service_details_post.py +354 -0
- render/public_api/models/webhook.py +110 -0
- render/public_api/models/webhook_event.py +115 -0
- render/public_api/models/webhook_event_with_cursor.py +73 -0
- render/public_api/models/webhook_patch_input.py +98 -0
- render/public_api/models/webhook_post_input.py +102 -0
- render/public_api/models/webhook_with_cursor.py +73 -0
- render/public_api/models/workflow.py +163 -0
- render/public_api/models/workflow_create.py +169 -0
- render/public_api/models/workflow_update.py +109 -0
- render/public_api/models/workflow_version.py +95 -0
- render/public_api/models/workflow_version_status.py +13 -0
- render/public_api/models/workflow_version_with_cursor.py +73 -0
- render/public_api/models/workflow_with_cursor.py +73 -0
- render/public_api/models/zero_downtime_redeploy_ended.py +44 -0
- render/public_api/models/zero_downtime_redeploy_started.py +59 -0
- render/public_api/py.typed +1 -0
- render/public_api/types.py +54 -0
- render/py.typed +0 -0
- render/render.py +81 -0
- render/render_async.py +80 -0
- render/version.py +27 -0
- render/workflows/__init__.py +25 -0
- render/workflows/_callback_models.py +194 -0
- render/workflows/_uds_http.py +128 -0
- render/workflows/app.py +263 -0
- render/workflows/cli.py +62 -0
- render/workflows/client.py +354 -0
- render/workflows/context.py +75 -0
- render/workflows/executor.py +86 -0
- render/workflows/runner.py +174 -0
- render/workflows/task.py +348 -0
- render/workflows/tests/__init__.py +0 -0
- render/workflows/tests/test_app.py +343 -0
- render/workflows/tests/test_cli.py +134 -0
- render/workflows/tests/test_client.py +59 -0
- render/workflows/tests/test_context.py +118 -0
- render/workflows/tests/test_end_to_end.py +307 -0
- render/workflows/tests/test_executor.py +221 -0
- render/workflows/tests/test_lazy_imports.py +100 -0
- render/workflows/tests/test_registration.py +293 -0
- render/workflows/tests/test_translate_errors.py +126 -0
- render/workflows/tests/test_uds_http.py +199 -0
- render/workflows/tests/test_version.py +15 -0
- render-1.0.1.dist-info/METADATA +382 -0
- render-1.0.1.dist-info/RECORD +799 -0
- render-1.0.1.dist-info/WHEEL +4 -0
- render-1.0.1.dist-info/entry_points.txt +2 -0
- render-1.0.1.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from render.client.errors import ClientError, RenderError, ServerError, TimeoutError
|
|
7
|
+
from render.client.util import (
|
|
8
|
+
_handle_wrapper_exception,
|
|
9
|
+
handle_api_error,
|
|
10
|
+
handle_http_error,
|
|
11
|
+
handle_httpx_exception,
|
|
12
|
+
handle_storage_http_error,
|
|
13
|
+
retry_with_backoff,
|
|
14
|
+
)
|
|
15
|
+
from render.public_api.models.error import Error
|
|
16
|
+
from render.public_api.types import UNSET, Response
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TestException(Exception):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.asyncio
|
|
24
|
+
async def test_retry_with_backoff_success():
|
|
25
|
+
async def fn() -> int:
|
|
26
|
+
return 1
|
|
27
|
+
|
|
28
|
+
result = await retry_with_backoff(fn)
|
|
29
|
+
assert result == 1
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.mark.asyncio
|
|
33
|
+
async def test_retry_with_backoff_failure():
|
|
34
|
+
async def fn() -> int:
|
|
35
|
+
raise TestException("Test failure")
|
|
36
|
+
|
|
37
|
+
with pytest.raises(TestException):
|
|
38
|
+
result = await retry_with_backoff(
|
|
39
|
+
fn, max_retries=2, poll_interval=0.001, backoff_factor=1.0
|
|
40
|
+
)
|
|
41
|
+
assert result is None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@pytest.mark.asyncio
|
|
45
|
+
async def test_retry_with_backoff_success_after_failure():
|
|
46
|
+
count = 0
|
|
47
|
+
|
|
48
|
+
async def fn() -> int:
|
|
49
|
+
nonlocal count
|
|
50
|
+
count += 1
|
|
51
|
+
if count == 1:
|
|
52
|
+
raise TestException("Test failure")
|
|
53
|
+
else:
|
|
54
|
+
return 1
|
|
55
|
+
|
|
56
|
+
result = await retry_with_backoff(
|
|
57
|
+
fn, max_retries=2, poll_interval=0.001, backoff_factor=1.0
|
|
58
|
+
)
|
|
59
|
+
assert result == 1
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@pytest.mark.asyncio
|
|
63
|
+
async def test_retry_with_backoff_success_after_failure_with_exempted_exception():
|
|
64
|
+
count = 0
|
|
65
|
+
|
|
66
|
+
async def fn() -> int:
|
|
67
|
+
nonlocal count
|
|
68
|
+
count += 1
|
|
69
|
+
raise TestException("Test failure")
|
|
70
|
+
|
|
71
|
+
with pytest.raises(TestException):
|
|
72
|
+
result = await retry_with_backoff(
|
|
73
|
+
fn,
|
|
74
|
+
max_retries=2,
|
|
75
|
+
poll_interval=0.001,
|
|
76
|
+
backoff_factor=1.0,
|
|
77
|
+
exempted_exceptions=(TestException,),
|
|
78
|
+
)
|
|
79
|
+
assert result is None
|
|
80
|
+
assert count == 1
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_handle_http_error_4xx():
|
|
84
|
+
response = httpx.Response(
|
|
85
|
+
status_code=404, json={"message": "Not found"}, text="Not found"
|
|
86
|
+
)
|
|
87
|
+
with pytest.raises(
|
|
88
|
+
ClientError, match="API request failed with status 404: Not found"
|
|
89
|
+
):
|
|
90
|
+
handle_http_error(response, "API request")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_handle_http_error_5xx():
|
|
94
|
+
response = httpx.Response(
|
|
95
|
+
status_code=500,
|
|
96
|
+
json={"error": "Internal server error"},
|
|
97
|
+
text="Internal server error",
|
|
98
|
+
)
|
|
99
|
+
with pytest.raises(
|
|
100
|
+
ServerError, match="API request failed with status 500: Internal server error"
|
|
101
|
+
):
|
|
102
|
+
handle_http_error(response, "API request")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_handle_httpx_exception_timeout():
|
|
106
|
+
exception = httpx.TimeoutException("Request timed out")
|
|
107
|
+
with pytest.raises(TimeoutError, match="HTTP request timed out"):
|
|
108
|
+
handle_httpx_exception(exception, "HTTP request")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_handle_httpx_exception_remote_protocol_error():
|
|
112
|
+
exception = httpx.RemoteProtocolError(
|
|
113
|
+
"Server disconnected without sending a response."
|
|
114
|
+
)
|
|
115
|
+
with pytest.raises(ServerError, match="failed due to network error"):
|
|
116
|
+
handle_httpx_exception(exception, "get task result")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_handle_api_error_client_error():
|
|
120
|
+
response = Response(
|
|
121
|
+
status_code=400, content=b"", headers={}, parsed=Error(message="Bad request")
|
|
122
|
+
)
|
|
123
|
+
with pytest.raises(ClientError, match="API request failed: Bad request"):
|
|
124
|
+
handle_api_error(response, "API request")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_handle_api_error_server_error():
|
|
128
|
+
response = Response(
|
|
129
|
+
status_code=500,
|
|
130
|
+
content=b"",
|
|
131
|
+
headers={},
|
|
132
|
+
parsed=Error(message="Internal server error"),
|
|
133
|
+
)
|
|
134
|
+
with pytest.raises(ServerError, match="API request failed: Internal server error"):
|
|
135
|
+
handle_api_error(response, "API request")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_handle_api_error_with_unset_message():
|
|
139
|
+
"""When Error has Unset message, should fallback to 'Unknown error occurred'."""
|
|
140
|
+
response = Response(
|
|
141
|
+
status_code=400,
|
|
142
|
+
content=b"",
|
|
143
|
+
headers={},
|
|
144
|
+
parsed=Error(message=UNSET),
|
|
145
|
+
)
|
|
146
|
+
with pytest.raises(ClientError, match="API request failed: Unknown error occurred"):
|
|
147
|
+
handle_api_error(response, "API request")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_handle_api_error_extracts_message_from_content_when_parsed_is_none():
|
|
151
|
+
"""When parsed is None, should extract error message from response content."""
|
|
152
|
+
response = Response(
|
|
153
|
+
status_code=400,
|
|
154
|
+
content=b'{"message": "limit must be a positive integer"}',
|
|
155
|
+
headers={},
|
|
156
|
+
parsed=None,
|
|
157
|
+
)
|
|
158
|
+
with pytest.raises(ClientError, match="limit must be a positive integer"):
|
|
159
|
+
handle_api_error(response, "API request")
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def test_handle_api_error_fallback_when_content_is_not_json():
|
|
163
|
+
"""When parsed is None and content is not JSON, should use fallback message."""
|
|
164
|
+
response = Response(
|
|
165
|
+
status_code=400,
|
|
166
|
+
content=b"Not a JSON response",
|
|
167
|
+
headers={},
|
|
168
|
+
parsed=None,
|
|
169
|
+
)
|
|
170
|
+
with pytest.raises(ClientError, match="API request failed: Unknown error occurred"):
|
|
171
|
+
handle_api_error(response, "API request")
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def test_handle_wrapper_exception_reraises_render_error():
|
|
175
|
+
with pytest.raises(RenderError, match="already a render error"):
|
|
176
|
+
_handle_wrapper_exception(RenderError("already a render error"), "op")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def test_handle_wrapper_exception_httpx_request_error():
|
|
180
|
+
exc = httpx.TimeoutException("timed out")
|
|
181
|
+
with pytest.raises(TimeoutError, match="op timed out"):
|
|
182
|
+
_handle_wrapper_exception(exc, "op")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def test_handle_wrapper_exception_json_decode_error():
|
|
186
|
+
exc = json.JSONDecodeError("Expecting value", "Unauthorized\n", 0)
|
|
187
|
+
with pytest.raises(
|
|
188
|
+
RenderError, match="server returned a non-JSON response: Unauthorized"
|
|
189
|
+
):
|
|
190
|
+
_handle_wrapper_exception(exc, "create task")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def test_handle_wrapper_exception_json_decode_error_empty_body():
|
|
194
|
+
exc = json.JSONDecodeError("Expecting value", "", 0)
|
|
195
|
+
with pytest.raises(
|
|
196
|
+
RenderError, match="server returned a non-JSON response: empty response"
|
|
197
|
+
):
|
|
198
|
+
_handle_wrapper_exception(exc, "create task")
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def test_handle_wrapper_exception_unexpected_error():
|
|
202
|
+
exc = ValueError("something weird")
|
|
203
|
+
with pytest.raises(
|
|
204
|
+
RenderError, match="failed with unexpected error: something weird"
|
|
205
|
+
):
|
|
206
|
+
_handle_wrapper_exception(exc, "op")
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class TestHandleStorageHttpError:
|
|
210
|
+
"""Tests for handle_storage_http_error function."""
|
|
211
|
+
|
|
212
|
+
def test_404_with_s3_xml_error_does_not_expose_raw_content(self):
|
|
213
|
+
"""Verify that raw S3 XML error content is not exposed in error message."""
|
|
214
|
+
s3_xml_error = (
|
|
215
|
+
'<?xml version="1.0" encoding="UTF-8"?>'
|
|
216
|
+
"<Error><Code>NoSuchKey</Code>"
|
|
217
|
+
"<Message>The specified key does not exist.</Message>"
|
|
218
|
+
"<Key>tea-d3tc3fuuk2gs73d0paug/foo/bar/test.txt</Key>"
|
|
219
|
+
"<RequestId>95N55HR7H0QBF3X9</RequestId>"
|
|
220
|
+
"<HostId>QfLXA55SGkqZ6VEKV97lgMjiFNWRFhpTj29FAylq2SOh2LJFMyvHuRdUjDu1IaZ"
|
|
221
|
+
"/NmQR0znt4/0=</HostId></Error>"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
response = httpx.Response(status_code=404, text=s3_xml_error)
|
|
225
|
+
|
|
226
|
+
with pytest.raises(ClientError) as exc_info:
|
|
227
|
+
handle_storage_http_error(response, "download object")
|
|
228
|
+
|
|
229
|
+
error_message = str(exc_info.value)
|
|
230
|
+
|
|
231
|
+
# Should contain sanitized message
|
|
232
|
+
assert "failed with status 404" in error_message
|
|
233
|
+
assert "object not found" in error_message
|
|
234
|
+
|
|
235
|
+
# Should NOT contain sensitive/raw content
|
|
236
|
+
assert "NoSuchKey" not in error_message
|
|
237
|
+
assert "tea-d3tc3fuuk2gs73d0paug" not in error_message
|
|
238
|
+
assert "RequestId" not in error_message
|
|
239
|
+
assert "HostId" not in error_message
|
|
240
|
+
|
|
241
|
+
def test_403_access_denied_does_not_expose_bucket_info(self):
|
|
242
|
+
"""Verify that storage bucket info is not exposed in error message."""
|
|
243
|
+
gcs_error = (
|
|
244
|
+
"<?xml version='1.0' encoding='UTF-8'?>"
|
|
245
|
+
"<Error><Code>AccessDenied</Code><Message>Access denied.</Message>"
|
|
246
|
+
"<Details>render-objects-bucket/some/path</Details></Error>"
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
response = httpx.Response(status_code=403, text=gcs_error)
|
|
250
|
+
|
|
251
|
+
with pytest.raises(ClientError) as exc_info:
|
|
252
|
+
handle_storage_http_error(response, "upload object")
|
|
253
|
+
|
|
254
|
+
error_message = str(exc_info.value)
|
|
255
|
+
|
|
256
|
+
# Should contain sanitized message
|
|
257
|
+
assert "failed with status 403" in error_message
|
|
258
|
+
assert "access denied" in error_message
|
|
259
|
+
|
|
260
|
+
# Should NOT contain sensitive info
|
|
261
|
+
assert "render-objects-bucket" not in error_message
|
|
262
|
+
assert "AccessDenied" not in error_message
|
|
263
|
+
|
|
264
|
+
def test_500_server_error(self):
|
|
265
|
+
"""Verify 5xx errors raise ServerError with sanitized message."""
|
|
266
|
+
response = httpx.Response(
|
|
267
|
+
status_code=500,
|
|
268
|
+
text="Internal Server Error: connection to storage backend failed",
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
with pytest.raises(ServerError) as exc_info:
|
|
272
|
+
handle_storage_http_error(response, "download object")
|
|
273
|
+
|
|
274
|
+
error_message = str(exc_info.value)
|
|
275
|
+
|
|
276
|
+
# Should contain sanitized message
|
|
277
|
+
assert "failed with status 500" in error_message
|
|
278
|
+
assert "storage service temporarily unavailable" in error_message
|
|
279
|
+
|
|
280
|
+
# Should NOT contain raw error content
|
|
281
|
+
assert "Internal Server Error" not in error_message
|
|
282
|
+
assert "storage backend" not in error_message
|
|
283
|
+
|
|
284
|
+
def test_413_payload_too_large(self):
|
|
285
|
+
"""Verify 413 errors are handled with appropriate message."""
|
|
286
|
+
xml_error = '<?xml version="1.0"?><Error><Code>EntityTooLarge</Code><MaxSizeAllowed>5368709120</MaxSizeAllowed></Error>'
|
|
287
|
+
response = httpx.Response(status_code=413, text=xml_error)
|
|
288
|
+
|
|
289
|
+
with pytest.raises(ClientError) as exc_info:
|
|
290
|
+
handle_storage_http_error(response, "upload object")
|
|
291
|
+
|
|
292
|
+
error_message = str(exc_info.value)
|
|
293
|
+
|
|
294
|
+
assert "failed with status 413" in error_message
|
|
295
|
+
assert "object too large" in error_message
|
|
296
|
+
assert "EntityTooLarge" not in error_message
|
|
297
|
+
assert "MaxSizeAllowed" not in error_message
|
|
298
|
+
|
|
299
|
+
def test_success_response_does_not_raise(self):
|
|
300
|
+
"""Verify successful responses don't raise exceptions."""
|
|
301
|
+
response = httpx.Response(status_code=200, text="OK")
|
|
302
|
+
# Should not raise
|
|
303
|
+
handle_storage_http_error(response, "download object")
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Unit tests for synchronous utility functions."""
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from render.client.util_sync import retry_with_backoff
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class _TestException(Exception):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_retry_with_backoff_success():
|
|
14
|
+
def fn() -> int:
|
|
15
|
+
return 1
|
|
16
|
+
|
|
17
|
+
result = retry_with_backoff(fn)
|
|
18
|
+
assert result == 1
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_retry_with_backoff_failure():
|
|
22
|
+
def fn() -> int:
|
|
23
|
+
raise _TestException("Test failure")
|
|
24
|
+
|
|
25
|
+
with pytest.raises(_TestException):
|
|
26
|
+
retry_with_backoff(fn, max_retries=2, poll_interval=0.001, backoff_factor=1.0)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_retry_with_backoff_success_after_failure():
|
|
30
|
+
count = 0
|
|
31
|
+
|
|
32
|
+
def fn() -> int:
|
|
33
|
+
nonlocal count
|
|
34
|
+
count += 1
|
|
35
|
+
if count == 1:
|
|
36
|
+
raise _TestException("Test failure")
|
|
37
|
+
return 1
|
|
38
|
+
|
|
39
|
+
result = retry_with_backoff(
|
|
40
|
+
fn, max_retries=2, poll_interval=0.001, backoff_factor=1.0
|
|
41
|
+
)
|
|
42
|
+
assert result == 1
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_retry_with_backoff_exempted_exception():
|
|
46
|
+
count = 0
|
|
47
|
+
|
|
48
|
+
def fn() -> int:
|
|
49
|
+
nonlocal count
|
|
50
|
+
count += 1
|
|
51
|
+
raise _TestException("Test failure")
|
|
52
|
+
|
|
53
|
+
with pytest.raises(_TestException):
|
|
54
|
+
retry_with_backoff(
|
|
55
|
+
fn,
|
|
56
|
+
max_retries=5,
|
|
57
|
+
poll_interval=0.001,
|
|
58
|
+
backoff_factor=1.0,
|
|
59
|
+
exempted_exceptions=(_TestException,),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Should have raised on the first attempt without retrying
|
|
63
|
+
assert count == 1
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_retry_with_backoff_returns_none_when_all_none():
|
|
67
|
+
def fn():
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
result = retry_with_backoff(
|
|
71
|
+
fn, max_retries=3, poll_interval=0.001, backoff_factor=1.0
|
|
72
|
+
)
|
|
73
|
+
assert result is None
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Unit tests for the synchronous Render REST API client functionality."""
|
|
3
|
+
|
|
4
|
+
import datetime
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from render.client import Client, ListTaskRunsParams
|
|
10
|
+
from render.client.errors import ClientError, ServerError
|
|
11
|
+
from render.client.workflows_sync import SyncWorkflowsService
|
|
12
|
+
from render.public_api.models.error import Error
|
|
13
|
+
from render.public_api.models.task_run import TaskRun
|
|
14
|
+
from render.public_api.models.task_run_details import TaskRunDetails
|
|
15
|
+
from render.public_api.models.task_run_status import TaskRunStatus
|
|
16
|
+
from render.public_api.types import Response
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Fixtures
|
|
20
|
+
@pytest.fixture
|
|
21
|
+
def mock_task_run(mocker):
|
|
22
|
+
return TaskRun(
|
|
23
|
+
id="trn-test123",
|
|
24
|
+
status=TaskRunStatus.RUNNING,
|
|
25
|
+
completed_at=None,
|
|
26
|
+
parent_task_run_id=None,
|
|
27
|
+
root_task_run_id=None,
|
|
28
|
+
retries=0,
|
|
29
|
+
attempts=[],
|
|
30
|
+
started_at=None,
|
|
31
|
+
task_id=None,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.fixture
|
|
36
|
+
def mock_task_run_details(mocker):
|
|
37
|
+
return TaskRunDetails(
|
|
38
|
+
id="trn-test123",
|
|
39
|
+
status=TaskRunStatus.COMPLETED,
|
|
40
|
+
error=None,
|
|
41
|
+
completed_at=datetime.datetime.now(),
|
|
42
|
+
results=[42],
|
|
43
|
+
input_=[42],
|
|
44
|
+
parent_task_run_id=None,
|
|
45
|
+
root_task_run_id=None,
|
|
46
|
+
retries=0,
|
|
47
|
+
attempts=[],
|
|
48
|
+
started_at=None,
|
|
49
|
+
task_id=None,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@pytest.fixture
|
|
54
|
+
def mock_authenticated_client(mocker):
|
|
55
|
+
"""Create a mock authenticated client."""
|
|
56
|
+
client = mocker.Mock()
|
|
57
|
+
client._base_url = "https://api.test.com/v1"
|
|
58
|
+
return client
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@pytest.fixture
|
|
62
|
+
def client(mocker, mock_authenticated_client):
|
|
63
|
+
"""Create a Client instance with mocked dependencies."""
|
|
64
|
+
mocker.patch(
|
|
65
|
+
"render.client.client.AuthenticatedClient",
|
|
66
|
+
return_value=mock_authenticated_client,
|
|
67
|
+
)
|
|
68
|
+
return Client("test-token", base_url="https://api.test.com")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@pytest.fixture
|
|
72
|
+
def sync_workflows_service(client):
|
|
73
|
+
"""Create a SyncWorkflowsService instance."""
|
|
74
|
+
return SyncWorkflowsService(client)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@pytest.fixture
|
|
78
|
+
def mock_cancel_task_run_sync(mocker):
|
|
79
|
+
return mocker.patch(
|
|
80
|
+
"render.public_api.api.workflow_tasks_beta.cancel_task_run.sync_detailed"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@pytest.fixture
|
|
85
|
+
def mock_list_task_runs_sync(mocker):
|
|
86
|
+
return mocker.patch(
|
|
87
|
+
"render.public_api.api.workflow_tasks_beta.list_task_runs.sync_detailed"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@pytest.fixture
|
|
92
|
+
def mock_create_task_sync(mocker):
|
|
93
|
+
return mocker.patch(
|
|
94
|
+
"render.public_api.api.workflow_tasks_beta.create_task.sync_detailed"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@pytest.fixture
|
|
99
|
+
def mock_get_task_run_sync(mocker):
|
|
100
|
+
return mocker.patch(
|
|
101
|
+
"render.public_api.api.workflow_tasks_beta.get_task_run.sync_detailed"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_start_task_success(
|
|
106
|
+
mock_create_task_sync, sync_workflows_service, mock_task_run
|
|
107
|
+
):
|
|
108
|
+
"""Test successful task start."""
|
|
109
|
+
mock_create_task_sync.return_value = Response(
|
|
110
|
+
status_code=202, content=b"", headers={}, parsed=mock_task_run
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
result = sync_workflows_service.start_task("test-task", {"input": "data"})
|
|
114
|
+
|
|
115
|
+
assert isinstance(result, TaskRun)
|
|
116
|
+
assert result.id == "trn-test123"
|
|
117
|
+
mock_create_task_sync.assert_called_once()
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_start_task_failure(mock_create_task_sync, sync_workflows_service):
|
|
121
|
+
"""Test task start failure."""
|
|
122
|
+
|
|
123
|
+
error = Error(message="Task creation failed")
|
|
124
|
+
mock_create_task_sync.return_value = Response(
|
|
125
|
+
status_code=400, content=b"", headers={}, parsed=error
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
with pytest.raises(ClientError, match="create task failed: Task creation failed"):
|
|
129
|
+
sync_workflows_service.start_task("test-task", {"input": "data"})
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_run_task_returns_task_run_details(
|
|
133
|
+
mocker,
|
|
134
|
+
mock_create_task_sync,
|
|
135
|
+
sync_workflows_service,
|
|
136
|
+
mock_task_run,
|
|
137
|
+
mock_task_run_details,
|
|
138
|
+
):
|
|
139
|
+
"""Test that run_task starts a task and waits for completion."""
|
|
140
|
+
mock_create_task_sync.return_value = Response(
|
|
141
|
+
status_code=202, content=b"", headers={}, parsed=mock_task_run
|
|
142
|
+
)
|
|
143
|
+
mocker.patch.object(
|
|
144
|
+
sync_workflows_service,
|
|
145
|
+
"_task_run_completed_with_sse",
|
|
146
|
+
return_value=mock_task_run_details,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
result = sync_workflows_service.run_task("test-task", {"input": "data"})
|
|
150
|
+
|
|
151
|
+
assert isinstance(result, TaskRunDetails)
|
|
152
|
+
assert result.id == "trn-test123"
|
|
153
|
+
assert result.status.value == TaskRunStatus.COMPLETED
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def test_run_task_failure(mock_create_task_sync, sync_workflows_service):
|
|
157
|
+
"""Test that run_task propagates errors from start_task."""
|
|
158
|
+
error = Error(message="Task creation failed")
|
|
159
|
+
mock_create_task_sync.return_value = Response(
|
|
160
|
+
status_code=400, content=b"", headers={}, parsed=error
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
with pytest.raises(ClientError, match="create task failed: Task creation failed"):
|
|
164
|
+
sync_workflows_service.run_task("test-task", {"input": "data"})
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def test_get_task_run_success(
|
|
168
|
+
mock_get_task_run_sync, sync_workflows_service, mock_task_run_details
|
|
169
|
+
):
|
|
170
|
+
"""Test successful task run retrieval."""
|
|
171
|
+
mock_get_task_run_sync.return_value = Response(
|
|
172
|
+
status_code=200, content=b"", headers={}, parsed=mock_task_run_details
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
result = sync_workflows_service.get_task_run("trn-test123")
|
|
176
|
+
|
|
177
|
+
assert result.id == "trn-test123"
|
|
178
|
+
assert result.status.value == TaskRunStatus.COMPLETED
|
|
179
|
+
mock_get_task_run_sync.assert_called_once_with(
|
|
180
|
+
client=sync_workflows_service.client.internal, task_run_id="trn-test123"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def test_get_task_run_failure(mock_get_task_run_sync, sync_workflows_service):
|
|
185
|
+
"""Test task run retrieval failure."""
|
|
186
|
+
error = Error(message="Task not found")
|
|
187
|
+
mock_get_task_run_sync.return_value = Response(
|
|
188
|
+
status_code=400, content=b"", headers={}, parsed=error
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
with pytest.raises(ClientError, match="get task run failed: Task not found"):
|
|
192
|
+
sync_workflows_service.get_task_run("trn-test123")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def test_cancel_task_run_success(mock_cancel_task_run_sync, sync_workflows_service):
|
|
196
|
+
"""Test successful task run cancellation."""
|
|
197
|
+
mock_cancel_task_run_sync.return_value = Response(
|
|
198
|
+
status_code=204, content=b"", headers={}, parsed=None
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
sync_workflows_service.cancel_task_run("trn-test123")
|
|
202
|
+
|
|
203
|
+
mock_cancel_task_run_sync.assert_called_once_with(
|
|
204
|
+
client=sync_workflows_service.client.internal, task_run_id="trn-test123"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def test_cancel_task_run_failure(mock_cancel_task_run_sync, sync_workflows_service):
|
|
209
|
+
"""Test task run cancellation failure."""
|
|
210
|
+
error = Error(message="Cannot cancel task")
|
|
211
|
+
mock_cancel_task_run_sync.return_value = Response(
|
|
212
|
+
status_code=400, content=b"", headers={}, parsed=error
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
with pytest.raises(ClientError, match="cancel task run failed: Cannot cancel task"):
|
|
216
|
+
sync_workflows_service.cancel_task_run("trn-test123")
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def test_list_task_runs_success(
|
|
220
|
+
mocker, mock_list_task_runs_sync, sync_workflows_service, mock_task_run
|
|
221
|
+
):
|
|
222
|
+
"""Test successful task runs listing."""
|
|
223
|
+
task_runs = [mock_task_run]
|
|
224
|
+
mock_list_task_runs_sync.return_value = Response(
|
|
225
|
+
status_code=200, content=b"", headers={}, parsed=task_runs
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
params = ListTaskRunsParams(limit=5, owner_id="test-owner")
|
|
229
|
+
result = sync_workflows_service.list_task_runs(params)
|
|
230
|
+
|
|
231
|
+
assert len(result) == 1
|
|
232
|
+
assert result[0].id == "trn-test123"
|
|
233
|
+
mock_list_task_runs_sync.assert_called_once_with(
|
|
234
|
+
client=sync_workflows_service.client.internal,
|
|
235
|
+
limit=5,
|
|
236
|
+
cursor=mocker.ANY,
|
|
237
|
+
owner_id="test-owner",
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@pytest.fixture
|
|
242
|
+
def mock_httpx_stream(mocker):
|
|
243
|
+
"""Mock httpx.Client to return a controllable SSE stream.
|
|
244
|
+
|
|
245
|
+
Returns a list that tests populate with bytes chunks before calling
|
|
246
|
+
task_run_events(). The mock wires up the nested context managers
|
|
247
|
+
(Client → stream → response.iter_bytes).
|
|
248
|
+
"""
|
|
249
|
+
chunks: list[bytes] = []
|
|
250
|
+
|
|
251
|
+
def iter_bytes():
|
|
252
|
+
yield from chunks
|
|
253
|
+
|
|
254
|
+
mock_response = mocker.Mock()
|
|
255
|
+
mock_response.status_code = 200
|
|
256
|
+
mock_response.iter_bytes = iter_bytes
|
|
257
|
+
|
|
258
|
+
stream_cm = mocker.MagicMock()
|
|
259
|
+
stream_cm.__enter__ = mocker.Mock(return_value=mock_response)
|
|
260
|
+
stream_cm.__exit__ = mocker.Mock(return_value=False)
|
|
261
|
+
|
|
262
|
+
mock_http_client = mocker.Mock()
|
|
263
|
+
mock_http_client.stream.return_value = stream_cm
|
|
264
|
+
|
|
265
|
+
client_cm = mocker.MagicMock()
|
|
266
|
+
client_cm.__enter__ = mocker.Mock(return_value=mock_http_client)
|
|
267
|
+
client_cm.__exit__ = mocker.Mock(return_value=False)
|
|
268
|
+
|
|
269
|
+
mocker.patch("render.client.workflows_sync.httpx.Client", return_value=client_cm)
|
|
270
|
+
|
|
271
|
+
return chunks, mock_response
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def test_task_run_events_yields_events(sync_workflows_service, mock_httpx_stream):
|
|
275
|
+
"""Test that task_run_events streams and yields TaskRunDetails."""
|
|
276
|
+
chunks, _ = mock_httpx_stream
|
|
277
|
+
|
|
278
|
+
details = TaskRunDetails(
|
|
279
|
+
id="trn-test123",
|
|
280
|
+
task_id="tsk-test123",
|
|
281
|
+
status=TaskRunStatus.COMPLETED,
|
|
282
|
+
results=[42],
|
|
283
|
+
input_=[],
|
|
284
|
+
parent_task_run_id=None,
|
|
285
|
+
root_task_run_id=None,
|
|
286
|
+
retries=0,
|
|
287
|
+
attempts=[],
|
|
288
|
+
)
|
|
289
|
+
sse_payload = json.dumps(details.to_dict()).encode()
|
|
290
|
+
chunks.append(b"event: task.completed\ndata: " + sse_payload + b"\n\n")
|
|
291
|
+
|
|
292
|
+
events = []
|
|
293
|
+
for event in sync_workflows_service.task_run_events(["trn-test123"]):
|
|
294
|
+
events.append(event)
|
|
295
|
+
|
|
296
|
+
assert len(events) == 1
|
|
297
|
+
assert events[0].id == "trn-test123"
|
|
298
|
+
assert events[0].status.value == TaskRunStatus.COMPLETED
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def test_task_run_events_raises_on_http_error(
|
|
302
|
+
sync_workflows_service, mock_httpx_stream
|
|
303
|
+
):
|
|
304
|
+
"""Test that task_run_events raises on HTTP errors."""
|
|
305
|
+
_, mock_response = mock_httpx_stream
|
|
306
|
+
|
|
307
|
+
mock_response.status_code = 500
|
|
308
|
+
mock_response.text = "Internal Server Error"
|
|
309
|
+
mock_response.json.return_value = {"message": "Internal Server Error"}
|
|
310
|
+
|
|
311
|
+
with pytest.raises(ServerError):
|
|
312
|
+
for _ in sync_workflows_service.task_run_events(["trn-test123"]):
|
|
313
|
+
pass
|