mainsequence 8.1.19__tar.gz → 8.1.20.dev2__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 (141) hide show
  1. {mainsequence-8.1.19/mainsequence.egg-info → mainsequence-8.1.20.dev2}/PKG-INFO +1 -1
  2. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2/mainsequence.egg-info}/PKG-INFO +1 -1
  3. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence.egg-info/SOURCES.txt +1 -0
  4. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/pyproject.toml +18 -1
  5. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_client.py +5 -0
  6. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_code_repository_batch_jobs_from_file.py +26 -0
  7. mainsequence-8.1.20.dev2/tests/test_dev_release_version.py +118 -0
  8. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_meta_tables_client_models.py +45 -1
  9. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_time_index_table_update_configuration_client.py +43 -0
  10. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/LICENSE +0 -0
  11. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/MANIFEST.in +0 -0
  12. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/README.md +0 -0
  13. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/agent_scaffold/AGENTS.md +0 -0
  14. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/agent_scaffold/skills/a2a_sdk_execution/SKILL.md +0 -0
  15. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  16. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  17. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/agent_scaffold/skills/data_publishing/meta_table_migrations/SKILL.md +0 -0
  18. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
  19. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/agent_scaffold/skills/data_publishing/time_index_table_updates/SKILL.md +0 -0
  20. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  21. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/agent_scaffold/skills/maintenance/code_repository_maintenance/SKILL.md +0 -0
  22. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  23. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  24. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/agent_scaffold/skills/sdk_code_repository_execution/SKILL.md +0 -0
  25. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/__init__.py +0 -0
  26. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/__main__.py +0 -0
  27. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/bootstrap.py +0 -0
  28. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/__init__.py +0 -0
  29. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/api.py +0 -0
  30. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/browser_auth.py +0 -0
  31. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/cli.py +0 -0
  32. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/code_repository_status.py +0 -0
  33. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/config.py +0 -0
  34. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/docker_utils.py +0 -0
  35. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/doctor.py +0 -0
  36. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/local_ops.py +0 -0
  37. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/migrations.py +0 -0
  38. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/model_filters.py +0 -0
  39. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/pydantic_cli.py +0 -0
  40. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/sdk_utils.py +0 -0
  41. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/ssh_utils.py +0 -0
  42. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/cli/ui.py +0 -0
  43. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/__init__.py +0 -0
  44. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/agent_runtime_models.py +0 -0
  45. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/base.py +0 -0
  46. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/client.py +0 -0
  47. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/compute_validation.py +0 -0
  48. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  49. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
  50. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
  51. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
  52. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/dtype_codec.py +0 -0
  53. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/exceptions.py +0 -0
  54. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/github_issues.py +0 -0
  55. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/metatables/__init__.py +0 -0
  56. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/metatables/core.py +0 -0
  57. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/models_foundry.py +0 -0
  58. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/models_helpers.py +0 -0
  59. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/models_user.py +0 -0
  60. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/observability.py +0 -0
  61. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/client/utils.py +0 -0
  62. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/code_repository_context.py +0 -0
  63. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/code_repository_skills.py +0 -0
  64. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/defaults.py +0 -0
  65. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/instrumentation/__init__.py +0 -0
  66. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/instrumentation/utils.py +0 -0
  67. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/logconf.py +0 -0
  68. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/__init__.py +0 -0
  69. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/__main__.py +0 -0
  70. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
  71. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
  72. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/future_registry.py +0 -0
  73. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/hashing.py +0 -0
  74. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/migrations/__init__.py +0 -0
  75. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/migrations/alembic.py +0 -0
  76. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/migrations/env.py +0 -0
  77. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/migrations/provider.py +0 -0
  78. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/migrations/registry.py +0 -0
  79. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/migrations/scaffold.py +0 -0
  80. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/migrations/templates/__init__.py +0 -0
  81. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/migrations/templates/env.py.mako +0 -0
  82. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/migrations/templates/script.py.mako +0 -0
  83. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
  84. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/schema_names.py +0 -0
  85. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
  86. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/time_index_table_refs.py +0 -0
  87. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/time_index_table_updates/__init__.py +0 -0
  88. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/time_index_table_updates/configuration.py +0 -0
  89. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/time_index_table_updates/dependencies.py +0 -0
  90. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/time_index_table_updates/managers.py +0 -0
  91. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/time_index_table_updates/namespacing.py +0 -0
  92. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/time_index_table_updates/runner.py +0 -0
  93. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/time_index_table_updates/updaters.py +0 -0
  94. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/meta_tables/time_index_table_updates/utils.py +0 -0
  95. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/repository_identity_security.py +0 -0
  96. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/runtime_flags.py +0 -0
  97. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence/scaffold_skills.py +0 -0
  98. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence.egg-info/dependency_links.txt +0 -0
  99. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence.egg-info/entry_points.txt +0 -0
  100. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence.egg-info/requires.txt +0 -0
  101. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/mainsequence.egg-info/top_level.txt +0 -0
  102. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/setup.cfg +0 -0
  103. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/fixtures/time_index_table_update_hash_v2_golden.json +0 -0
  104. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_auth_precedence.py +0 -0
  105. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_canonical_api_route_contract.py +0 -0
  106. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_cli.py +0 -0
  107. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_cli_browser_auth.py +0 -0
  108. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_cli_migrations.py +0 -0
  109. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_code_repository_branch_contract.py +0 -0
  110. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_code_repository_documentation_contract.py +0 -0
  111. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_code_repository_runtime_context.py +0 -0
  112. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_code_repository_skills.py +0 -0
  113. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_dependency_extras.py +0 -0
  114. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_duckdb_interface_dimensions.py +0 -0
  115. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_environment_resource_context.py +0 -0
  116. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_environment_response_contracts.py +0 -0
  117. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_filter_normalization.py +0 -0
  118. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_github_issues_client.py +0 -0
  119. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_instrumentation.py +0 -0
  120. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_local_ops.py +0 -0
  121. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_logconf.py +0 -0
  122. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_meta_table_migrations.py +0 -0
  123. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
  124. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_models_user_request_bound_auth.py +0 -0
  125. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_notification_uid_contract.py +0 -0
  126. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_observability_client.py +0 -0
  127. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_pod_project_resolution.py +0 -0
  128. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_scaffold_skills.py +0 -0
  129. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_schema_names.py +0 -0
  130. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_secret_client_model.py +0 -0
  131. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_source_table_configuration.py +0 -0
  132. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_sqlite_interface_dimensions.py +0 -0
  133. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_ssh_utils.py +0 -0
  134. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_time_index_table_access_dimension_audit.py +0 -0
  135. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_time_index_table_dimension_queries.py +0 -0
  136. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_time_index_table_update_configuration.py +0 -0
  137. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_time_index_table_update_flow.py +0 -0
  138. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_time_index_table_update_hard_cut.py +0 -0
  139. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_update_runner_uid_runtime.py +0 -0
  140. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_update_statistics.py +0 -0
  141. {mainsequence-8.1.19 → mainsequence-8.1.20.dev2}/tests/test_update_uid_guards.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 8.1.19
3
+ Version: 8.1.20.dev2
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License-Expression: LicenseRef-MainSequence-Proprietary
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 8.1.19
3
+ Version: 8.1.20.dev2
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License-Expression: LicenseRef-MainSequence-Proprietary
@@ -105,6 +105,7 @@ tests/test_code_repository_documentation_contract.py
105
105
  tests/test_code_repository_runtime_context.py
106
106
  tests/test_code_repository_skills.py
107
107
  tests/test_dependency_extras.py
108
+ tests/test_dev_release_version.py
108
109
  tests/test_duckdb_interface_dimensions.py
109
110
  tests/test_environment_resource_context.py
110
111
  tests/test_environment_response_contracts.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mainsequence"
7
- version = "8.1.19"
7
+ version = "8.1.20.dev2"
8
8
  description = "Main Sequence SDK "
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -95,6 +95,23 @@ publish-url = "https://test.pypi.org/legacy/"
95
95
  explicit = true
96
96
 
97
97
 
98
+ # --------------------
99
+ # Testing (pytest)
100
+ # --------------------
101
+ [tool.pytest.ini_options]
102
+ testpaths = ["tests"]
103
+ # Put the repository root on sys.path so tests can import `scripts`. `pytest`
104
+ # invoked as `python -m pytest` gets this from the CWD, but the `pytest`
105
+ # entrypoint (what `uv run pytest` and CI use) does not.
106
+ pythonpath = ["."]
107
+ # `live` tests talk to a real backend and need credentials, so they are
108
+ # deselected by default. CI and a plain `pytest` therefore run offline.
109
+ # Run them explicitly with `pytest -m live`.
110
+ addopts = "-m 'not live'"
111
+ markers = [
112
+ "live: talks to a live Main Sequence backend and needs credentials; deselected unless `-m live` is passed",
113
+ ]
114
+
98
115
  # --------------------
99
116
  # Formatting (Black)
100
117
  # --------------------
@@ -4,6 +4,11 @@ import pytest
4
4
 
5
5
  import mainsequence.client as msc
6
6
 
7
+ # Every test in this module drives a real backend over the network and needs
8
+ # SDK credentials, so none of them can run in CI. See `[tool.pytest.ini_options]`
9
+ # in pyproject.toml: `pytest` deselects them, `pytest -m live` runs them.
10
+ pytestmark = pytest.mark.live
11
+
7
12
 
8
13
  def test_create_code_repository():
9
14
  ds = msc.DataSource.filter(status=msc.DataSource.STATUS_AVAILABLE)[0]
@@ -6,6 +6,32 @@ import types
6
6
  import pytest
7
7
 
8
8
 
9
+ def _snapshot_mainsequence_modules() -> dict[str, types.ModuleType]:
10
+ return {
11
+ name: module
12
+ for name, module in sys.modules.items()
13
+ if name == "mainsequence" or name.startswith("mainsequence.")
14
+ }
15
+
16
+
17
+ @pytest.fixture(autouse=True)
18
+ def _restore_mainsequence_modules():
19
+ """Undo the stub packages this module installs into ``sys.modules``.
20
+
21
+ ``_load_models_helpers_module`` replaces the real ``mainsequence`` packages
22
+ with stubs so ``models_helpers`` can be imported without the heavy client
23
+ stack. Without this fixture the stubs leak into every test module that runs
24
+ afterwards, and ``monkeypatch.setattr("mainsequence.client....")`` there
25
+ fails with ``module 'mainsequence' has no attribute 'client'``.
26
+ """
27
+ snapshot = _snapshot_mainsequence_modules()
28
+ yield
29
+ for name in tuple(sys.modules):
30
+ if name == "mainsequence" or name.startswith("mainsequence."):
31
+ sys.modules.pop(name, None)
32
+ sys.modules.update(snapshot)
33
+
34
+
9
35
  def _load_models_helpers_module():
10
36
  repo_root = pathlib.Path(__file__).resolve().parents[1]
11
37
  pkg_root = repo_root / "mainsequence"
@@ -0,0 +1,118 @@
1
+ """Contract for the development-release version computed by the publish workflow.
2
+
3
+ The standard these tests pin down is documented in
4
+ `docs/knowledge/release_process.md`.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import pytest
10
+ from packaging.version import Version
11
+
12
+ from scripts import dev_release_version
13
+
14
+
15
+ def _files(*, yanked: bool = False) -> list[dict[str, object]]:
16
+ return [{"filename": "mainsequence-x-py3-none-any.whl", "yanked": yanked}]
17
+
18
+
19
+ def test_latest_final_ignores_development_and_pre_releases():
20
+ releases = {
21
+ "8.1.19": _files(),
22
+ "8.1.20.dev41": _files(),
23
+ "8.2.0rc1": _files(),
24
+ "8.1.19.post1": _files(),
25
+ }
26
+
27
+ assert dev_release_version.latest_final_version(releases) == Version("8.1.19")
28
+
29
+
30
+ def test_latest_final_ignores_releases_with_no_installable_file():
31
+ releases = {
32
+ "8.1.19": _files(),
33
+ "9.0.0": [],
34
+ "9.1.0": _files(yanked=True),
35
+ }
36
+
37
+ assert dev_release_version.latest_final_version(releases) == Version("8.1.19")
38
+
39
+
40
+ def test_latest_final_keeps_a_release_with_one_file_left():
41
+ releases = {
42
+ "8.1.19": _files(),
43
+ "8.2.0": [
44
+ {"filename": "mainsequence-8.2.0.tar.gz", "yanked": True},
45
+ {"filename": "mainsequence-8.2.0-py3-none-any.whl", "yanked": False},
46
+ ],
47
+ }
48
+
49
+ assert dev_release_version.latest_final_version(releases) == Version("8.2.0")
50
+
51
+
52
+ def test_latest_final_orders_numerically_not_lexically():
53
+ releases = {"8.1.9": _files(), "8.1.10": _files(), "8.1.2": _files()}
54
+
55
+ assert dev_release_version.latest_final_version(releases) == Version("8.1.10")
56
+
57
+
58
+ def test_latest_final_skips_unparseable_versions():
59
+ releases = {"8.1.19": _files(), "not-a-version": _files()}
60
+
61
+ assert dev_release_version.latest_final_version(releases) == Version("8.1.19")
62
+
63
+
64
+ def test_latest_final_requires_at_least_one_installable_release():
65
+ with pytest.raises(dev_release_version.DevVersionError):
66
+ dev_release_version.latest_final_version({"9.0.0": []})
67
+
68
+
69
+ def test_dev_version_carries_the_next_patch_number():
70
+ assert dev_release_version.dev_version(Version("8.1.19"), 41) == "8.1.20.dev41"
71
+
72
+
73
+ def test_dev_version_sorts_between_the_current_and_the_next_release():
74
+ computed = Version(dev_release_version.dev_version(Version("8.1.19"), 41))
75
+
76
+ assert Version("8.1.19") < computed < Version("8.1.20")
77
+
78
+
79
+ def test_dev_version_is_a_development_release_so_pip_skips_it_by_default():
80
+ computed = Version(dev_release_version.dev_version(Version("8.1.19"), 41))
81
+
82
+ assert computed.is_devrelease
83
+
84
+
85
+ def test_dev_version_rejects_a_negative_run_number():
86
+ with pytest.raises(dev_release_version.DevVersionError):
87
+ dev_release_version.dev_version(Version("8.1.19"), -1)
88
+
89
+
90
+ def test_apply_version_rewrites_only_the_project_version():
91
+ pyproject = '[project]\nname = "mainsequence"\nversion = "8.1.19"\n\n[tool.other]\nversion = "1.0.0"\n'
92
+
93
+ rewritten = dev_release_version.apply_version(pyproject, "8.1.20.dev41")
94
+
95
+ assert 'version = "8.1.20.dev41"' in rewritten
96
+ assert rewritten.count('version = "1.0.0"') == 1
97
+
98
+
99
+ def test_apply_version_refuses_a_pyproject_without_a_project_version():
100
+ with pytest.raises(dev_release_version.DevVersionError):
101
+ dev_release_version.apply_version('[project]\nname = "mainsequence"\n', "8.1.20.dev41")
102
+
103
+
104
+ def test_read_declared_version_reports_the_repository_version():
105
+ text = dev_release_version.PYPROJECT.read_text(encoding="utf-8")
106
+
107
+ declared = dev_release_version.read_declared_version(text)
108
+
109
+ assert declared is not None
110
+ Version(declared)
111
+
112
+
113
+ def test_the_repository_pyproject_accepts_a_rewritten_version():
114
+ text = dev_release_version.PYPROJECT.read_text(encoding="utf-8")
115
+
116
+ rewritten = dev_release_version.apply_version(text, "8.1.20.dev41")
117
+
118
+ assert dev_release_version.read_declared_version(rewritten) == "8.1.20.dev41"
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import datetime
4
+ import pathlib
4
5
  from types import SimpleNamespace
5
6
 
6
7
  import pytest
@@ -12,6 +13,8 @@ from mainsequence.client.exceptions import ConflictError, PermissionDeniedError
12
13
  from mainsequence.meta_tables.compiled_sql.v1 import build_operation, compile_sqlalchemy_statement
13
14
 
14
15
  ENVIRONMENT_UID = "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"
16
+ CODE_REPOSITORY_UID = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"
17
+ CODE_REPOSITORY_BRANCH_UID = "22222222-2222-4222-8222-222222222222"
15
18
 
16
19
 
17
20
  @pytest.fixture(autouse=True)
@@ -23,6 +26,47 @@ def _resolved_environment(monkeypatch):
23
26
  )
24
27
 
25
28
 
29
+ @pytest.fixture(autouse=True)
30
+ def _resolved_code_repository_context(monkeypatch):
31
+ """Lock a Git CodeRepository context so client calls never reach the backend.
32
+
33
+ ``MetaTable`` write paths ask ``get_code_repository_context()`` whether they
34
+ run on an authenticated runtime before they build a payload. Left alone that
35
+ resolves the real Git context by POSTing to the backend, so these tests hang
36
+ on request retries instead of exercising the payload they assert on.
37
+ """
38
+ code_repository_context._reset_code_repository_context()
39
+ source = code_repository_context.GitCodeRepositorySourceContext(
40
+ repository_root=pathlib.Path.cwd().resolve(),
41
+ canonical_repository_identity="github.com/mainsequence-sdk/meta-tables-client-models",
42
+ repository_branch="main",
43
+ repository_ref="refs/heads/main",
44
+ commit_sha="a" * 40,
45
+ )
46
+ monkeypatch.setattr(
47
+ code_repository_context,
48
+ "_resolve_git_source_context",
49
+ lambda code_repository_dir: source,
50
+ )
51
+ code_repository_context.get_code_repository_context(
52
+ _code_repository_branch_context_loader=lambda resolved_source: SimpleNamespace(
53
+ canonical_repository_identity=resolved_source.canonical_repository_identity,
54
+ repository_branch=resolved_source.repository_branch,
55
+ repository_ref=resolved_source.repository_ref,
56
+ commit_sha=resolved_source.commit_sha,
57
+ code_repository_branch=SimpleNamespace(
58
+ uid=CODE_REPOSITORY_BRANCH_UID,
59
+ code_repository_uid=CODE_REPOSITORY_UID,
60
+ repository_branch=resolved_source.repository_branch,
61
+ organization_environment_uid=None,
62
+ metatables_data_source=None,
63
+ ),
64
+ ),
65
+ )
66
+ yield
67
+ code_repository_context._reset_code_repository_context()
68
+
69
+
26
70
  class _Response:
27
71
  def __init__(self, payload, status_code: int = 200):
28
72
  self._payload = payload
@@ -85,7 +129,7 @@ def _meta_table_response(**overrides):
85
129
 
86
130
  def _code_repository_context():
87
131
  return meta_table_models.MetaTableCodeRepositoryContextRequest(
88
- code_repository_branch_uid="22222222-2222-4222-8222-222222222222",
132
+ code_repository_branch_uid=CODE_REPOSITORY_BRANCH_UID,
89
133
  )
90
134
 
91
135
 
@@ -1,5 +1,6 @@
1
1
  import inspect
2
2
  import os
3
+ import pathlib
3
4
  import threading
4
5
  from types import SimpleNamespace
5
6
 
@@ -12,6 +13,7 @@ from pydantic import ValidationError
12
13
 
13
14
  import mainsequence.client.metatables as models_metatables
14
15
  import mainsequence.client.models_foundry as models_foundry
16
+ import mainsequence.code_repository_context as code_repository_context
15
17
  import mainsequence.meta_tables.time_index_table_updates.updaters as updaters_module
16
18
  from mainsequence.client.metatables import (
17
19
  MetaTable,
@@ -33,6 +35,47 @@ from mainsequence.meta_tables.time_index_table_updates.managers import (
33
35
  from mainsequence.meta_tables.time_index_table_updates.runner import UpdateRunner
34
36
 
35
37
 
38
+ @pytest.fixture(autouse=True)
39
+ def _resolved_code_repository_context(monkeypatch):
40
+ """Lock a Git CodeRepository context so these tests never reach the backend.
41
+
42
+ Building an update manager asks ``get_code_repository_context()`` for the
43
+ current Git context, which otherwise POSTs to the real backend and retries.
44
+ The resolution is cached per process, so exactly one test paid for it — but
45
+ that one test made a live outbound request from an offline suite.
46
+ """
47
+ code_repository_context._reset_code_repository_context()
48
+ source = code_repository_context.GitCodeRepositorySourceContext(
49
+ repository_root=pathlib.Path.cwd().resolve(),
50
+ canonical_repository_identity="github.com/mainsequence-sdk/time-index-table-updates",
51
+ repository_branch="main",
52
+ repository_ref="refs/heads/main",
53
+ commit_sha="a" * 40,
54
+ )
55
+ monkeypatch.setattr(
56
+ code_repository_context,
57
+ "_resolve_git_source_context",
58
+ lambda code_repository_dir: source,
59
+ )
60
+ code_repository_context.get_code_repository_context(
61
+ _code_repository_branch_context_loader=lambda resolved_source: SimpleNamespace(
62
+ canonical_repository_identity=resolved_source.canonical_repository_identity,
63
+ repository_branch=resolved_source.repository_branch,
64
+ repository_ref=resolved_source.repository_ref,
65
+ commit_sha=resolved_source.commit_sha,
66
+ code_repository_branch=SimpleNamespace(
67
+ uid="22222222-2222-4222-8222-222222222222",
68
+ code_repository_uid="aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
69
+ repository_branch=resolved_source.repository_branch,
70
+ organization_environment_uid=None,
71
+ metatables_data_source=None,
72
+ ),
73
+ ),
74
+ )
75
+ yield
76
+ code_repository_context._reset_code_repository_context()
77
+
78
+
36
79
  def _canonical_build_configuration(**values: object) -> dict[str, object]:
37
80
  configuration: dict[str, object] = {
38
81
  "configuration_schema_version": 2,