mainsequence 6.0.43__tar.gz → 6.0.45__tar.gz

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.
Files changed (131) hide show
  1. {mainsequence-6.0.43/mainsequence.egg-info → mainsequence-6.0.45}/PKG-INFO +1 -1
  2. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/metatables/core.py +7 -0
  3. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/models_helpers.py +8 -0
  4. {mainsequence-6.0.43 → mainsequence-6.0.45/mainsequence.egg-info}/PKG-INFO +1 -1
  5. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence.egg-info/SOURCES.txt +2 -0
  6. {mainsequence-6.0.43 → mainsequence-6.0.45}/pyproject.toml +1 -1
  7. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_cli.py +9 -0
  8. mainsequence-6.0.45/tests/test_environment_response_contracts.py +96 -0
  9. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_filter_normalization.py +6 -0
  10. mainsequence-6.0.45/tests/test_scheduler_client_model.py +122 -0
  11. {mainsequence-6.0.43 → mainsequence-6.0.45}/LICENSE +0 -0
  12. {mainsequence-6.0.43 → mainsequence-6.0.45}/README.md +0 -0
  13. {mainsequence-6.0.43 → mainsequence-6.0.45}/agent_scaffold/AGENTS.md +0 -0
  14. {mainsequence-6.0.43 → mainsequence-6.0.45}/agent_scaffold/skills/a2a_sdk_execution/SKILL.md +0 -0
  15. {mainsequence-6.0.43 → mainsequence-6.0.45}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  16. {mainsequence-6.0.43 → mainsequence-6.0.45}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  17. {mainsequence-6.0.43 → mainsequence-6.0.45}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
  18. {mainsequence-6.0.43 → mainsequence-6.0.45}/agent_scaffold/skills/data_publishing/meta_table_migrations/SKILL.md +0 -0
  19. {mainsequence-6.0.43 → mainsequence-6.0.45}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
  20. {mainsequence-6.0.43 → mainsequence-6.0.45}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  21. {mainsequence-6.0.43 → mainsequence-6.0.45}/agent_scaffold/skills/maintenance/project-maintenance/SKILL.md +0 -0
  22. {mainsequence-6.0.43 → mainsequence-6.0.45}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  23. {mainsequence-6.0.43 → mainsequence-6.0.45}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  24. {mainsequence-6.0.43 → mainsequence-6.0.45}/agent_scaffold/skills/sdk_project_execution/SKILL.md +0 -0
  25. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/__init__.py +0 -0
  26. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/__main__.py +0 -0
  27. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/bootstrap.py +0 -0
  28. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/__init__.py +0 -0
  29. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/api.py +0 -0
  30. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/browser_auth.py +0 -0
  31. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/cli.py +0 -0
  32. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/config.py +0 -0
  33. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/docker_utils.py +0 -0
  34. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/doctor.py +0 -0
  35. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/local_ops.py +0 -0
  36. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/migrations.py +0 -0
  37. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/model_filters.py +0 -0
  38. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/project_status.py +0 -0
  39. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/pydantic_cli.py +0 -0
  40. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/sdk_utils.py +0 -0
  41. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/ssh_utils.py +0 -0
  42. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/cli/ui.py +0 -0
  43. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/__init__.py +0 -0
  44. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/agent_runtime_models.py +0 -0
  45. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/base.py +0 -0
  46. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/client.py +0 -0
  47. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/compute_validation.py +0 -0
  48. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  49. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
  50. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
  51. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
  52. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/dtype_codec.py +0 -0
  53. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/exceptions.py +0 -0
  54. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/metatables/__init__.py +0 -0
  55. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/models_foundry.py +0 -0
  56. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/models_user.py +0 -0
  57. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/client/utils.py +0 -0
  58. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/defaults.py +0 -0
  59. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/instrumentation/__init__.py +0 -0
  60. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/instrumentation/utils.py +0 -0
  61. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/logconf.py +0 -0
  62. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/__init__.py +0 -0
  63. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/__main__.py +0 -0
  64. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
  65. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
  66. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
  67. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
  68. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
  69. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/data_nodes/models.py +0 -0
  70. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
  71. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
  72. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
  73. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
  74. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/future_registry.py +0 -0
  75. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/hashing.py +0 -0
  76. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/migrations/__init__.py +0 -0
  77. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/migrations/alembic.py +0 -0
  78. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/migrations/env.py +0 -0
  79. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/migrations/provider.py +0 -0
  80. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/migrations/registry.py +0 -0
  81. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/migrations/scaffold.py +0 -0
  82. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/migrations/templates/__init__.py +0 -0
  83. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/migrations/templates/env.py.mako +0 -0
  84. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/migrations/templates/script.py.mako +0 -0
  85. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
  86. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/schema_names.py +0 -0
  87. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
  88. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/project_context.py +0 -0
  89. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/project_skills.py +0 -0
  90. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/runtime_flags.py +0 -0
  91. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence/scaffold_skills.py +0 -0
  92. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence.egg-info/dependency_links.txt +0 -0
  93. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence.egg-info/entry_points.txt +0 -0
  94. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence.egg-info/requires.txt +0 -0
  95. {mainsequence-6.0.43 → mainsequence-6.0.45}/mainsequence.egg-info/top_level.txt +0 -0
  96. {mainsequence-6.0.43 → mainsequence-6.0.45}/setup.cfg +0 -0
  97. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_auth_precedence.py +0 -0
  98. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_build_operations_hashing.py +0 -0
  99. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_canonical_api_route_contract.py +0 -0
  100. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_cli_browser_auth.py +0 -0
  101. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_cli_migrations.py +0 -0
  102. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_client.py +0 -0
  103. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_data_access_mixin_dimension_audit.py +0 -0
  104. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_data_node_storage_dimension_queries.py +0 -0
  105. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_data_node_update_flow.py +0 -0
  106. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_dependency_extras.py +0 -0
  107. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_duckdb_interface_dimensions.py +0 -0
  108. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_environment_resource_context.py +0 -0
  109. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_instrumentation.py +0 -0
  110. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_local_ops.py +0 -0
  111. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_logconf.py +0 -0
  112. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_meta_table_migrations.py +0 -0
  113. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_meta_tables_client_models.py +0 -0
  114. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
  115. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_models_user_request_bound_auth.py +0 -0
  116. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_notification_uid_contract.py +0 -0
  117. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_pod_project_resolution.py +0 -0
  118. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_project_batch_jobs_from_file.py +0 -0
  119. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_project_branch_contract.py +0 -0
  120. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_project_runtime_context.py +0 -0
  121. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_project_skills.py +0 -0
  122. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_run_configuration.py +0 -0
  123. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_scaffold_skills.py +0 -0
  124. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_schema_names.py +0 -0
  125. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_secret_client_model.py +0 -0
  126. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_source_table_configuration.py +0 -0
  127. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_sqlite_interface_dimensions.py +0 -0
  128. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_ssh_utils.py +0 -0
  129. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_update_runner_uid_runtime.py +0 -0
  130. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_update_statistics.py +0 -0
  131. {mainsequence-6.0.43 → mainsequence-6.0.45}/tests/test_update_uid_guards.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 6.0.43
3
+ Version: 6.0.45
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License-Expression: LicenseRef-MainSequence-Proprietary
@@ -3719,6 +3719,13 @@ class TimeIndexMetaTable(MetaTable):
3719
3719
 
3720
3720
  class Scheduler(BasePydanticModel, BaseObjectOrm):
3721
3721
  uid: str | None = Field(None, description="Public uid of this scheduler")
3722
+ organization_project_environment_uid: str = Field(
3723
+ ...,
3724
+ description=(
3725
+ "Read-only public UID of the Organization Environment derived by the "
3726
+ "backend from the scheduler's update nodes."
3727
+ ),
3728
+ )
3722
3729
  name: str
3723
3730
  is_running: bool
3724
3731
  running_process_pid: int | None
@@ -760,6 +760,14 @@ class JobRun(BaseObjectOrm, BasePydanticModel):
760
760
  description="Read-only repository branch name associated with the run, or null for an unscoped run.",
761
761
  examples=["main"],
762
762
  )
763
+ organization_project_environment_uid: str = Field(
764
+ ...,
765
+ description=(
766
+ "Read-only public UID of the Organization Environment resolved from the "
767
+ "JobRun's owning project branch."
768
+ ),
769
+ examples=["58218213-5e4e-43de-a5bd-6757f4e1c8f6"],
770
+ )
763
771
 
764
772
  execution_start: datetime.datetime | None = Field(
765
773
  default=None,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 6.0.43
3
+ Version: 6.0.45
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License-Expression: LicenseRef-MainSequence-Proprietary
@@ -101,6 +101,7 @@ tests/test_data_node_update_flow.py
101
101
  tests/test_dependency_extras.py
102
102
  tests/test_duckdb_interface_dimensions.py
103
103
  tests/test_environment_resource_context.py
104
+ tests/test_environment_response_contracts.py
104
105
  tests/test_filter_normalization.py
105
106
  tests/test_instrumentation.py
106
107
  tests/test_local_ops.py
@@ -117,6 +118,7 @@ tests/test_project_runtime_context.py
117
118
  tests/test_project_skills.py
118
119
  tests/test_run_configuration.py
119
120
  tests/test_scaffold_skills.py
121
+ tests/test_scheduler_client_model.py
120
122
  tests/test_schema_names.py
121
123
  tests/test_secret_client_model.py
122
124
  tests/test_source_table_configuration.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mainsequence"
7
- version = "6.0.43"
7
+ version = "6.0.45"
8
8
  description = "Main Sequence SDK "
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -5803,6 +5803,9 @@ def test_get_project_job_run_logs_uses_client_model(cli_mod, monkeypatch):
5803
5803
  "project_name": "market-data-service",
5804
5804
  "project_branch_uid": "5a28020a-0f1b-47ee-aab8-334286234bea",
5805
5805
  "project_branch_name": "main",
5806
+ "organization_project_environment_uid": (
5807
+ "58218213-5e4e-43de-a5bd-6757f4e1c8f6"
5808
+ ),
5806
5809
  "status": "RUNNING",
5807
5810
  "runtime_image_uid": "6cfdb152-923e-45b9-a150-c4541c68b0d1",
5808
5811
  "runtime_image_digest": "sha256:" + "b" * 64,
@@ -5811,6 +5814,9 @@ def test_get_project_job_run_logs_uses_client_model(cli_mod, monkeypatch):
5811
5814
 
5812
5815
  def get_logs(self, *, timeout=None):
5813
5816
  captured["get_logs_timeout"] = timeout
5817
+ captured["organization_project_environment_uid"] = (
5818
+ self.organization_project_environment_uid
5819
+ )
5814
5820
  return {
5815
5821
  "job_run_uid": self.uid,
5816
5822
  "status": self.status,
@@ -5829,6 +5835,9 @@ def test_get_project_job_run_logs_uses_client_model(cli_mod, monkeypatch):
5829
5835
  out = api_mod.get_project_job_run_logs("4c1d77c8-8a42-42b8-a9c1-06be9a336e5d")
5830
5836
  assert captured["job_run_uid_arg"] == "4c1d77c8-8a42-42b8-a9c1-06be9a336e5d"
5831
5837
  assert captured["get_logs_timeout"] is None
5838
+ assert captured["organization_project_environment_uid"] == (
5839
+ "58218213-5e4e-43de-a5bd-6757f4e1c8f6"
5840
+ )
5832
5841
  assert captured["jwt"] == ("acc", "ref")
5833
5842
  assert out == {
5834
5843
  "job_run_uid": "4c1d77c8-8a42-42b8-a9c1-06be9a336e5d",
@@ -0,0 +1,96 @@
1
+ import pytest
2
+
3
+ import mainsequence.client.agent_runtime_models as agent_models
4
+ import mainsequence.client.metatables.core as metatable_models
5
+ import mainsequence.client.models_foundry as foundry_models
6
+ import mainsequence.client.models_helpers as helper_models
7
+
8
+ ENVIRONMENT_UID_FIELD = "organization_project_environment_uid"
9
+ ENVIRONMENT_NAME_FIELD = "organization_project_environment_name"
10
+
11
+
12
+ @pytest.mark.parametrize(
13
+ ("serializer_names", "model", "expected_fields"),
14
+ [
15
+ pytest.param(
16
+ ("AgentSerializer",),
17
+ agent_models.Agent,
18
+ {ENVIRONMENT_UID_FIELD, ENVIRONMENT_NAME_FIELD},
19
+ id="agent",
20
+ ),
21
+ pytest.param(
22
+ ("AgentSemanticSearchResultSerializer",),
23
+ agent_models.AgentSemanticSearchResult,
24
+ {ENVIRONMENT_UID_FIELD, ENVIRONMENT_NAME_FIELD},
25
+ id="agent-semantic-search-result",
26
+ ),
27
+ pytest.param(
28
+ ("JobSerializer",),
29
+ helper_models.Job,
30
+ {ENVIRONMENT_UID_FIELD},
31
+ id="job",
32
+ ),
33
+ pytest.param(
34
+ ("JobRunSerializer", "JobRunLightSerializer"),
35
+ helper_models.JobRun,
36
+ {ENVIRONMENT_UID_FIELD},
37
+ id="job-run",
38
+ ),
39
+ pytest.param(
40
+ ("ProjectBranchSerializer",),
41
+ foundry_models.ProjectBranch,
42
+ {ENVIRONMENT_UID_FIELD, ENVIRONMENT_NAME_FIELD},
43
+ id="project-branch",
44
+ ),
45
+ pytest.param(
46
+ ("BucketSerializer",),
47
+ foundry_models.Bucket,
48
+ {ENVIRONMENT_UID_FIELD, ENVIRONMENT_NAME_FIELD},
49
+ id="bucket",
50
+ ),
51
+ pytest.param(
52
+ ("ArtifactSerializer",),
53
+ foundry_models.Artifact,
54
+ {ENVIRONMENT_UID_FIELD, ENVIRONMENT_NAME_FIELD},
55
+ id="artifact",
56
+ ),
57
+ pytest.param(
58
+ ("SecretNameSerializer", "SecretValueSerializer"),
59
+ foundry_models.Secret,
60
+ {ENVIRONMENT_UID_FIELD, ENVIRONMENT_NAME_FIELD},
61
+ id="secret",
62
+ ),
63
+ pytest.param(
64
+ ("ConstantSerializer",),
65
+ foundry_models.Constant,
66
+ {ENVIRONMENT_UID_FIELD, ENVIRONMENT_NAME_FIELD},
67
+ id="constant",
68
+ ),
69
+ pytest.param(
70
+ ("MetaTableSerializer",),
71
+ metatable_models.MetaTable,
72
+ {ENVIRONMENT_UID_FIELD, ENVIRONMENT_NAME_FIELD},
73
+ id="meta-table",
74
+ ),
75
+ pytest.param(
76
+ ("MetaTableSerializer",),
77
+ metatable_models.TimeIndexMetaTable,
78
+ {ENVIRONMENT_UID_FIELD, ENVIRONMENT_NAME_FIELD},
79
+ id="time-index-meta-table",
80
+ ),
81
+ pytest.param(
82
+ ("SchedulerSerializer", "SchedulerMinimalSerializer"),
83
+ metatable_models.Scheduler,
84
+ {ENVIRONMENT_UID_FIELD},
85
+ id="scheduler",
86
+ ),
87
+ ],
88
+ )
89
+ def test_backend_environment_response_contract_is_declared_by_strict_sdk_model(
90
+ serializer_names,
91
+ model,
92
+ expected_fields,
93
+ ):
94
+ """Keep SDK response models aligned with ADR-0036/ADR-0037 projections."""
95
+ assert model.model_config.get("extra") == "forbid", serializer_names
96
+ assert expected_fields <= set(model.model_fields), serializer_names
@@ -180,6 +180,7 @@ def test_job_run_status_uses_status_detail_endpoint(monkeypatch):
180
180
  project_name=None,
181
181
  project_branch_uid=None,
182
182
  project_branch_name=None,
183
+ organization_project_environment_uid=ENVIRONMENT_UID,
183
184
  runtime_image_uid="6cfdb152-923e-45b9-a150-c4541c68b0d1",
184
185
  runtime_image_digest="sha256:" + "b" * 64,
185
186
  )
@@ -271,6 +272,7 @@ def test_job_run_deserializes_uid_payload_without_id():
271
272
  project_name="market-data-service",
272
273
  project_branch_uid="5a28020a-0f1b-47ee-aab8-334286234bea",
273
274
  project_branch_name="main",
275
+ organization_project_environment_uid=ENVIRONMENT_UID,
274
276
  status="RUNNING",
275
277
  cpu_request="1",
276
278
  cpu_limit="2",
@@ -286,6 +288,7 @@ def test_job_run_deserializes_uid_payload_without_id():
286
288
  dumped = job_run.model_dump()
287
289
  assert dumped["uid"] == "4c1d77c8-8a42-42b8-a9c1-06be9a336e5d"
288
290
  assert dumped["job_uid"] == "ab6a5d50-8a3e-4f0d-a9bb-7e84180bd50e"
291
+ assert dumped["organization_project_environment_uid"] == ENVIRONMENT_UID
289
292
  assert "id" not in dumped
290
293
 
291
294
 
@@ -2464,6 +2467,7 @@ def test_job_run_requires_immutable_runtime_image_snapshot():
2464
2467
  "project_name": "market-data-service",
2465
2468
  "project_branch_uid": "5a28020a-0f1b-47ee-aab8-334286234bea",
2466
2469
  "project_branch_name": "main",
2470
+ "organization_project_environment_uid": ENVIRONMENT_UID,
2467
2471
  "commit_hash": "a" * 40,
2468
2472
  "runtime_image_uid": "6cfdb152-923e-45b9-a150-c4541c68b0d1",
2469
2473
  "runtime_image_digest": "sha256:" + "b" * 64,
@@ -2477,6 +2481,7 @@ def test_job_run_requires_immutable_runtime_image_snapshot():
2477
2481
  assert run.project_name == payload["project_name"]
2478
2482
  assert run.project_branch_uid == payload["project_branch_uid"]
2479
2483
  assert run.project_branch_name == payload["project_branch_name"]
2484
+ assert run.organization_project_environment_uid == ENVIRONMENT_UID
2480
2485
 
2481
2486
  missing_project_context = dict(payload)
2482
2487
  missing_project_context.pop("project_uid")
@@ -2492,6 +2497,7 @@ def test_job_run_requires_immutable_runtime_image_snapshot():
2492
2497
  "project_name": None,
2493
2498
  "project_branch_uid": None,
2494
2499
  "project_branch_name": None,
2500
+ "organization_project_environment_uid": ENVIRONMENT_UID,
2495
2501
  "runtime_image_digest": "sha256:" + "b" * 64,
2496
2502
  }
2497
2503
  )
@@ -0,0 +1,122 @@
1
+ import pytest
2
+ from pydantic import ValidationError
3
+
4
+ import mainsequence.client.metatables.core as core_mod
5
+
6
+ ENVIRONMENT_UID = "58218213-5e4e-43de-a5bd-6757f4e1c8f6"
7
+ SCHEDULER_UID = "00000000-0000-4000-8000-000000000001"
8
+ UPDATE_NODE_UID = "00000000-0000-4000-8000-000000000002"
9
+
10
+
11
+ def _scheduler_payload():
12
+ return {
13
+ "uid": SCHEDULER_UID,
14
+ "organization_project_environment_uid": ENVIRONMENT_UID,
15
+ "name": "tutorial",
16
+ "is_running": False,
17
+ "running_process_pid": None,
18
+ "running_in_debug_mode": True,
19
+ "updates_halted": False,
20
+ "host": None,
21
+ "api_address": None,
22
+ "api_port": None,
23
+ "last_heart_beat": None,
24
+ "pre_loads_in_tree": [],
25
+ "in_active_tree": [],
26
+ "schedules_to": [],
27
+ }
28
+
29
+
30
+ class _SchedulerResponse:
31
+ status_code = 200
32
+ text = ""
33
+
34
+ @staticmethod
35
+ def json():
36
+ return _scheduler_payload()
37
+
38
+ @staticmethod
39
+ def raise_for_status():
40
+ return None
41
+
42
+
43
+ def test_scheduler_strict_model_accepts_environment_projection():
44
+ scheduler = core_mod.Scheduler.model_validate(_scheduler_payload())
45
+
46
+ assert scheduler.organization_project_environment_uid == ENVIRONMENT_UID
47
+
48
+ with pytest.raises(ValidationError, match="unexpected_projection"):
49
+ core_mod.Scheduler.model_validate(
50
+ {**_scheduler_payload(), "unexpected_projection": "rejected"}
51
+ )
52
+
53
+
54
+ def test_scheduler_build_derives_environment_without_request_override(monkeypatch):
55
+ captured = {}
56
+
57
+ def _fake_make_request(**kwargs):
58
+ captured.update(kwargs)
59
+ return _SchedulerResponse()
60
+
61
+ monkeypatch.setattr(
62
+ core_mod.Scheduler,
63
+ "build_session",
64
+ classmethod(lambda cls: object()),
65
+ )
66
+ monkeypatch.setattr(core_mod, "make_request", _fake_make_request)
67
+
68
+ scheduler = core_mod.Scheduler.build_and_assign_to_update_nodes(
69
+ scheduler_name="tutorial",
70
+ update_node_uids=[UPDATE_NODE_UID],
71
+ timeout=11,
72
+ )
73
+
74
+ request_body = captured["payload"]["json"]
75
+ assert "organization_project_environment_uid" not in request_body
76
+ assert "organization_project_environment_uid" not in request_body["scheduler_kwargs"]
77
+ assert request_body["update_node_uids"] == [UPDATE_NODE_UID]
78
+ assert captured["time_out"] == 11
79
+ assert scheduler.organization_project_environment_uid == ENVIRONMENT_UID
80
+
81
+
82
+ def test_scheduler_get_for_update_node_accepts_environment_projection(monkeypatch):
83
+ captured = {}
84
+
85
+ def _fake_make_request(**kwargs):
86
+ captured.update(kwargs)
87
+ return _SchedulerResponse()
88
+
89
+ monkeypatch.setattr(
90
+ core_mod.Scheduler,
91
+ "build_session",
92
+ classmethod(lambda cls: object()),
93
+ )
94
+ monkeypatch.setattr(core_mod, "make_request", _fake_make_request)
95
+
96
+ scheduler = core_mod.Scheduler.get_scheduler_for_update_node(UPDATE_NODE_UID)
97
+
98
+ assert captured["r_type"] == "GET"
99
+ assert captured["payload"] == {"params": {"update_node_uid": UPDATE_NODE_UID}}
100
+ assert scheduler.organization_project_environment_uid == ENVIRONMENT_UID
101
+
102
+
103
+ def test_scheduler_assign_accepts_environment_projection(monkeypatch):
104
+ captured = {}
105
+ scheduler = core_mod.Scheduler.model_validate(_scheduler_payload())
106
+
107
+ def _fake_make_request(**kwargs):
108
+ captured.update(kwargs)
109
+ return _SchedulerResponse()
110
+
111
+ monkeypatch.setattr(
112
+ core_mod.Scheduler,
113
+ "build_session",
114
+ classmethod(lambda cls: object()),
115
+ )
116
+ monkeypatch.setattr(core_mod, "make_request", _fake_make_request)
117
+
118
+ assigned = scheduler.assign_to_scheduler([UPDATE_NODE_UID])
119
+
120
+ assert captured["r_type"] == "PATCH"
121
+ assert captured["payload"] == {"json": {"update_node_uids": [UPDATE_NODE_UID]}}
122
+ assert assigned.organization_project_environment_uid == ENVIRONMENT_UID
File without changes
File without changes
File without changes