half-orm-dev 0.16.0a9__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.
Files changed (58) hide show
  1. half_orm_dev/__init__.py +1 -0
  2. half_orm_dev/cli/__init__.py +9 -0
  3. half_orm_dev/cli/commands/__init__.py +56 -0
  4. half_orm_dev/cli/commands/apply.py +13 -0
  5. half_orm_dev/cli/commands/clone.py +102 -0
  6. half_orm_dev/cli/commands/init.py +331 -0
  7. half_orm_dev/cli/commands/new.py +15 -0
  8. half_orm_dev/cli/commands/patch.py +317 -0
  9. half_orm_dev/cli/commands/prepare.py +21 -0
  10. half_orm_dev/cli/commands/prepare_release.py +119 -0
  11. half_orm_dev/cli/commands/promote_to.py +127 -0
  12. half_orm_dev/cli/commands/release.py +344 -0
  13. half_orm_dev/cli/commands/restore.py +14 -0
  14. half_orm_dev/cli/commands/sync.py +13 -0
  15. half_orm_dev/cli/commands/todo.py +73 -0
  16. half_orm_dev/cli/commands/undo.py +17 -0
  17. half_orm_dev/cli/commands/update.py +73 -0
  18. half_orm_dev/cli/commands/upgrade.py +191 -0
  19. half_orm_dev/cli/main.py +103 -0
  20. half_orm_dev/cli_extension.py +38 -0
  21. half_orm_dev/database.py +1389 -0
  22. half_orm_dev/hgit.py +1025 -0
  23. half_orm_dev/hop.py +167 -0
  24. half_orm_dev/manifest.py +43 -0
  25. half_orm_dev/modules.py +456 -0
  26. half_orm_dev/patch.py +281 -0
  27. half_orm_dev/patch_manager.py +1694 -0
  28. half_orm_dev/patch_validator.py +335 -0
  29. half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql +34 -0
  30. half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql +2 -0
  31. half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql +3 -0
  32. half_orm_dev/patches/log +2 -0
  33. half_orm_dev/patches/sql/half_orm_meta.sql +208 -0
  34. half_orm_dev/release_manager.py +2841 -0
  35. half_orm_dev/repo.py +1562 -0
  36. half_orm_dev/templates/.gitignore +15 -0
  37. half_orm_dev/templates/MANIFEST.in +1 -0
  38. half_orm_dev/templates/Pipfile +13 -0
  39. half_orm_dev/templates/README +25 -0
  40. half_orm_dev/templates/conftest_template +42 -0
  41. half_orm_dev/templates/init_module_template +10 -0
  42. half_orm_dev/templates/module_template_1 +12 -0
  43. half_orm_dev/templates/module_template_2 +6 -0
  44. half_orm_dev/templates/module_template_3 +3 -0
  45. half_orm_dev/templates/relation_test +23 -0
  46. half_orm_dev/templates/setup.py +81 -0
  47. half_orm_dev/templates/sql_adapter +9 -0
  48. half_orm_dev/templates/warning +12 -0
  49. half_orm_dev/utils.py +49 -0
  50. half_orm_dev/version.txt +1 -0
  51. half_orm_dev-0.16.0a9.dist-info/METADATA +935 -0
  52. half_orm_dev-0.16.0a9.dist-info/RECORD +58 -0
  53. half_orm_dev-0.16.0a9.dist-info/WHEEL +5 -0
  54. half_orm_dev-0.16.0a9.dist-info/licenses/AUTHORS +3 -0
  55. half_orm_dev-0.16.0a9.dist-info/licenses/LICENSE +14 -0
  56. half_orm_dev-0.16.0a9.dist-info/top_level.txt +2 -0
  57. tests/__init__.py +0 -0
  58. tests/conftest.py +329 -0
@@ -0,0 +1,58 @@
1
+ half_orm_dev/__init__.py,sha256=0JpUPey1gacxXuIFGcpD2nTGso73fkak72qzTHttAJk,18
2
+ half_orm_dev/cli_extension.py,sha256=0F54AZpUfnuj4h1thwUORTSmwCuaoTyG8WqRH8DdWpQ,1109
3
+ half_orm_dev/database.py,sha256=t4zIw9MXghj15Nln28ZBUegrbicCBAQlDHdu0cCSjzY,60084
4
+ half_orm_dev/hgit.py,sha256=b1DeJhy-Y_kbY1L0ZmVRqcQk8rrxDcqJXiP6pmfsx9w,36746
5
+ half_orm_dev/hop.py,sha256=MXae6KV8bRsnXznfItk1V3PaEefLbV3znxgKDD5vCU8,5237
6
+ half_orm_dev/manifest.py,sha256=1BKDQf64baI8KOacW6Jx6IiopUtYitZcM44wrBwQhQw,1265
7
+ half_orm_dev/modules.py,sha256=c6R08hYbatUQMJQ1Wt4-crKWQPvxYprwvnqtSfrZOGc,16791
8
+ half_orm_dev/patch.py,sha256=HgzszYSuay2fXdViiSuG5OGstcZLLZvJJosciLk5OU0,11297
9
+ half_orm_dev/patch_manager.py,sha256=A2L3CV7Gijv6ChqyvGQkow9dK56_ZqUA1QB2Ot7i2PI,61729
10
+ half_orm_dev/patch_validator.py,sha256=x17WZXJtXWs8yRzW3SbitZQyW7YJAGYwIYSUyiYRrms,10842
11
+ half_orm_dev/release_manager.py,sha256=9fPXiGU3_h8xZ5lSEHO1VbnNGFFSkLt0ilA7JkSSjoI,106538
12
+ half_orm_dev/repo.py,sha256=wXa5YLrR08vR5XH9ZgE753PbzIIhOa983wYsGgapGCw,56190
13
+ half_orm_dev/utils.py,sha256=NTGvUZy6RHdgC3Rn7wCY-lSwBdeRV7vlLJ2D7-qiibw,1501
14
+ half_orm_dev/version.txt,sha256=U5aFjIuK_CjhhMNWk5s-Y3zOpurWsjIcNI5AjVfUVY0,10
15
+ half_orm_dev/cli/__init__.py,sha256=0CbMj8OIhZmglWakK7NhYPn302erUTEg2VHOdm1hRTQ,163
16
+ half_orm_dev/cli/main.py,sha256=hrXw3ShhzRCfcaSHjcWmXswWOm-WAzhvhwY4d1MS6x0,3836
17
+ half_orm_dev/cli/commands/__init__.py,sha256=KzaFOfRuNRly67wf5qjgGwVaErDmXvkzYt0phB7iFQs,1363
18
+ half_orm_dev/cli/commands/apply.py,sha256=6Sne5T6DQlPmHzY_9XR6srjAQnDNguB-jbA2IEDnzCk,211
19
+ half_orm_dev/cli/commands/clone.py,sha256=JUDDt-vz_WvGkm5HDFuZ3KZbclLyPaE4h665n8QfEEQ,3989
20
+ half_orm_dev/cli/commands/init.py,sha256=ZLwxlua-5-Nm1o20igAFcW2GAQGMPzD5mecXOe-oSjc,12551
21
+ half_orm_dev/cli/commands/new.py,sha256=OuTIoCG36OzE-3MPMQg6BEjG0IXbgsJgodIdUmKCnkI,365
22
+ half_orm_dev/cli/commands/patch.py,sha256=x6gnAZp8SQTBkuzF1c9DECvGUNYCLzd2AFC7PiKzKfU,11056
23
+ half_orm_dev/cli/commands/prepare.py,sha256=sYCt1p57hR7ob8IOzUiuLAcbIBtdyoZ1iYKSM0IxqRQ,466
24
+ half_orm_dev/cli/commands/prepare_release.py,sha256=ZPf-dSsnB_Vl6iJXT2F8xr-zWrGhoeXsPTapZjp1obs,3934
25
+ half_orm_dev/cli/commands/promote_to.py,sha256=2SsE45gf-rJddEJzEQgr7zX0kc5Odr4B_TWpBOtcCew,4965
26
+ half_orm_dev/cli/commands/release.py,sha256=E6nkP350ZwFK9teu40nWVxvAFMU7R9n1GKYYMIQe6QM,11642
27
+ half_orm_dev/cli/commands/restore.py,sha256=n9SP8n1EQUduvDoA0qxpSUQpphc48X-NovnocyGl98I,236
28
+ half_orm_dev/cli/commands/sync.py,sha256=D0Prr8W1ySYjP3D8H4MB05KHccFbhB8z2qB3Bs00swA,274
29
+ half_orm_dev/cli/commands/todo.py,sha256=av64TpieK0yHcn-gFDQlk-MCMG0Y4b74yur7vhAn4RI,2988
30
+ half_orm_dev/cli/commands/undo.py,sha256=GWUawPd71YPpY5Lx4E7zyoGlY0ZD4c8zIbh-ddo1DXk,343
31
+ half_orm_dev/cli/commands/update.py,sha256=OarpDkC8hF08UUv1l2i-2qpHICgpB6WDn9ziYvRB3I8,2325
32
+ half_orm_dev/cli/commands/upgrade.py,sha256=eOs29Bqpn6j6gi2ysvHLFgeFQYFDJIZeLZn6Ts7Ae3M,6301
33
+ half_orm_dev/patches/log,sha256=n7MNnGR09Obd87gXLzIi6zA76sI4RhOJzC25wb0TbKE,22
34
+ half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql,sha256=gMZ94YlyrftxcqDn0l-ToCTee4A_bnP58DpHcIT_T1w,1074
35
+ half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql,sha256=nhRbDi6sUenvVfOnoRuWSbLEC1cEfzrXbxDof2weq04,183
36
+ half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql,sha256=Bd0lXJ6vC0JNe06yqTWYVSrwVDElCI3McSS5pm-Ijlo,306
37
+ half_orm_dev/patches/sql/half_orm_meta.sql,sha256=Vl2YzEWpWdam-tC0ZE8iNMeTRzEHpxtNhdBThbHb2u4,5864
38
+ half_orm_dev/templates/.gitignore,sha256=-h7-1dAPCvvmejrw27rCiuLDmLeDoKeRNX9A86iJD9U,108
39
+ half_orm_dev/templates/MANIFEST.in,sha256=53BeBuKi8UtBWB6IG3VQZk9Ow8Iye6Zs14sP-gVyVDA,25
40
+ half_orm_dev/templates/Pipfile,sha256=u3lGJSk5HZwz-EOTrOdBYrkhGV6zgVtrrRPivrO5rmA,182
41
+ half_orm_dev/templates/README,sha256=Jb26Qv41KGSfD-6FpyqF7EEiPpI5nmhtpdsCJolg1pA,904
42
+ half_orm_dev/templates/conftest_template,sha256=i1-CR326a-AxYYREXlY_0_5nItwRR7myKH0H6elP40s,1136
43
+ half_orm_dev/templates/init_module_template,sha256=o3RAnhGayYUF7NEyI8bcI6JHmAZb2wPVNF-FdrjOnQU,345
44
+ half_orm_dev/templates/module_template_1,sha256=hRa0PiI6-dpBKNXJ9PuDuGocdrq712ujlSJGfJcXOh8,271
45
+ half_orm_dev/templates/module_template_2,sha256=CUqf-MwVeCc_sU_VScfI2nPsi9a8H_4EwVtjrLiD9Cw,156
46
+ half_orm_dev/templates/module_template_3,sha256=yjpQYT0EsIA6TI8KeyFaw_nHP9zCBma6o7g3P1MDs_M,187
47
+ half_orm_dev/templates/relation_test,sha256=5OMnz39mzYe3eVGsB44rgZckhUWOrDv7dWI8urC6MiI,574
48
+ half_orm_dev/templates/setup.py,sha256=hnnJ4cLnECbxzaTLeVBYd3aR-2kk6KuF1xkAoXWQN5A,2061
49
+ half_orm_dev/templates/sql_adapter,sha256=kAP5y7Qml3DKsbZLUeoVpeXjbQcWltHjkDznEDKNpek,190
50
+ half_orm_dev/templates/warning,sha256=4hlZ_rRdpmkXxOeRoVd9xnXBARYXn95e-iXrD1f2u7k,490
51
+ half_orm_dev-0.16.0a9.dist-info/licenses/AUTHORS,sha256=eWxqzRdLOt2gX0FMQj_wui03Od3jdlwa8xNe9tl84g0,113
52
+ half_orm_dev-0.16.0a9.dist-info/licenses/LICENSE,sha256=ufhxlSi6mttkGQTsGWrEoB3WA_fCPJ6-k07GSVBgyPw,644
53
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
+ tests/conftest.py,sha256=uTtMgXULNB2tjon0y3cwBoEoRu20rHM_aGTreMX8kHk,9604
55
+ half_orm_dev-0.16.0a9.dist-info/METADATA,sha256=xk4b6bMN4A6K1KyhJJuoi9V74hlfa18W209ntIJvErU,27760
56
+ half_orm_dev-0.16.0a9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ half_orm_dev-0.16.0a9.dist-info/top_level.txt,sha256=-BjeqhKatJi7KdYuwzJ2pwpV-tMItvIf7HbY8tmK3ls,19
58
+ half_orm_dev-0.16.0a9.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,3 @@
1
+ # This is the list of half_orm_dev's significant contributors.
2
+ # Hopefully, it will become larger...
3
+ Joël Maizi
@@ -0,0 +1,14 @@
1
+ This file is part of half_orm_dev.
2
+
3
+ half_orm_dev is free software: you can redistribute it and/or modify
4
+ it under the terms of the GNU General Public License as published by
5
+ the Free Software Foundation, either version 3 of the License, or
6
+ (at your option) any later version.
7
+
8
+ half_orm is distributed in the hope that it will be useful,
9
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ GNU General Public License for more details.
12
+
13
+ You should have received a copy of the GNU General Public License
14
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,2 @@
1
+ half_orm_dev
2
+ tests
tests/__init__.py ADDED
File without changes
tests/conftest.py ADDED
@@ -0,0 +1,329 @@
1
+ """
2
+ Shared pytest fixtures for half_orm_dev tests.
3
+ """
4
+
5
+ import pytest
6
+ import tempfile
7
+ import shutil
8
+ from pathlib import Path
9
+ from unittest.mock import Mock, patch
10
+ from half_orm_dev.database import Database
11
+ from half_orm_dev.repo import Repo
12
+
13
+
14
+ @pytest.fixture
15
+ def temp_repo():
16
+ """
17
+ Create temporary directory structure for testing.
18
+ """
19
+ temp_dir = tempfile.mkdtemp()
20
+ patches_dir = Path(temp_dir) / "Patches"
21
+ patches_dir.mkdir()
22
+
23
+ repo = Mock()
24
+ repo.base_dir = temp_dir
25
+ repo.devel = True
26
+ repo.name = "test_database"
27
+ repo.git_origin = "https://github.com/test/repo.git"
28
+
29
+ # Create default HGit mock with tag methods
30
+ mock_hgit = Mock()
31
+ mock_hgit.fetch_tags = Mock()
32
+ mock_hgit.tag_exists = Mock(return_value=False)
33
+ mock_hgit.create_tag = Mock()
34
+ mock_hgit.push_tag = Mock()
35
+ repo.hgit = mock_hgit
36
+
37
+ yield repo, temp_dir, patches_dir
38
+
39
+ shutil.rmtree(temp_dir)
40
+
41
+
42
+ @pytest.fixture
43
+ def patch_manager(temp_repo):
44
+ """
45
+ Create PatchManager instance with temporary repo.
46
+
47
+ The repo.hgit already has default tag mocks configured in temp_repo fixture.
48
+ Tests can override repo.hgit if needed, but should use mock_hgit_complete fixture.
49
+ """
50
+ from half_orm_dev.patch_manager import PatchManager
51
+
52
+ repo, temp_dir, patches_dir = temp_repo
53
+ repo.restore_database_from_schema = Repo.restore_database_from_schema.__get__(repo, type(repo))
54
+ mock_get_version = Mock(return_value=(16, 1))
55
+ repo.database.get_postgres_version = mock_get_version
56
+ patch_mgr = PatchManager(repo)
57
+ return patch_mgr, repo, temp_dir, patches_dir
58
+
59
+
60
+ @pytest.fixture
61
+ def mock_hgit_complete():
62
+ """
63
+ Create complete mock HGit for create_patch workflow tests.
64
+
65
+ Provides all necessary mocks for successful patch creation workflow:
66
+ - Branch validation (on ho-prod)
67
+ - Repository clean check
68
+ - Remote configuration check
69
+ - Remote fetch operations
70
+ - Branch synchronization check (NEW)
71
+ - Tag availability check
72
+ - Git operations (checkout, add, commit, tag, push)
73
+ - Branch operations (create, delete, checkout)
74
+ """
75
+ mock_hgit = Mock()
76
+
77
+ # Branch and repo state
78
+ mock_hgit.branch = "ho-prod"
79
+ mock_hgit.repos_is_clean.return_value = True
80
+ mock_hgit.has_remote.return_value = True
81
+
82
+ # NEW: Branch synchronization check
83
+ # Returns (is_synced, status) tuple
84
+ mock_hgit.is_branch_synced.return_value = (True, "synced")
85
+
86
+ # Fetch operations
87
+ mock_hgit.fetch_from_origin.return_value = None
88
+ mock_hgit.fetch_tags.return_value = None
89
+
90
+ # Tag operations
91
+ mock_hgit.tag_exists.return_value = False # No existing tags
92
+ mock_hgit.create_tag.return_value = None
93
+ mock_hgit.push_tag.return_value = None
94
+ mock_hgit.delete_local_tag.return_value = None
95
+
96
+ # Branch operations
97
+ mock_hgit.checkout.return_value = None
98
+ mock_hgit.delete_local_branch.return_value = None
99
+ mock_hgit.push_branch.return_value = None
100
+
101
+ # Git proxy methods
102
+ mock_hgit.add.return_value = None
103
+ mock_hgit.commit.return_value = None
104
+
105
+ # Git repo access for reset operations
106
+ mock_git_repo = Mock()
107
+ mock_git_repo.git.reset.return_value = None
108
+ mock_hgit._HGit__git_repo = mock_git_repo
109
+
110
+ return mock_hgit
111
+
112
+ @pytest.fixture
113
+ def sample_patch_files():
114
+ """
115
+ Provide sample patch file contents for testing.
116
+ """
117
+ return {
118
+ '01_create_table.sql': 'CREATE TABLE users (id SERIAL PRIMARY KEY);',
119
+ '02_add_indexes.sql': 'CREATE INDEX idx_users_id ON users(id);',
120
+ 'migrate.py': 'print("Running migration")',
121
+ 'cleanup.py': 'print("Cleanup complete")',
122
+ }
123
+
124
+
125
+ @pytest.fixture
126
+ def mock_database():
127
+ """
128
+ Mock database connection for testing.
129
+
130
+ Provides a mock database connection object that can be used
131
+ to test SQL execution without requiring a real database.
132
+
133
+ Returns:
134
+ Mock: Mock database connection with execute_query method
135
+ """
136
+ mock_db = Mock()
137
+ mock_db.execute_query = Mock()
138
+ mock_db.execute = Mock()
139
+ mock_db.cursor = Mock()
140
+
141
+ return mock_db
142
+
143
+ @pytest.fixture
144
+ def mock_database_for_schema_generation():
145
+ """
146
+ Create complete Database mock for _generate_schema_sql() testing.
147
+
148
+ Provides a mock with all necessary attributes and methods for schema
149
+ generation tests, including mangled private attributes.
150
+
151
+ Returns:
152
+ Mock: Configured Database mock with:
153
+ - _Database__name: Database name (mangled private attribute)
154
+ - _collect_connection_params(): Returns connection parameters
155
+ - _get_connection_params(): Returns connection parameters
156
+ - _execute_pg_command(): Mock for pg_dump execution
157
+
158
+ Example:
159
+ def test_something(self, mock_database_for_schema_generation, tmp_path):
160
+ database = mock_database_for_schema_generation
161
+ model_dir = tmp_path / "model"
162
+ model_dir.mkdir()
163
+
164
+ result = Database._generate_schema_sql(database, "1.0.0", model_dir)
165
+ """
166
+ mock_db = Mock(spec=Database)
167
+
168
+ # Set mangled private attribute for database name
169
+ mock_db._Database__name = "test_db"
170
+
171
+ # Mock connection parameter methods
172
+ connection_params = {
173
+ 'user': 'test_user',
174
+ 'password': 'test_pass',
175
+ 'host': 'localhost',
176
+ 'port': 5432,
177
+ 'production': False
178
+ }
179
+
180
+ mock_db._collect_connection_params = Mock(return_value=connection_params)
181
+ mock_db._get_connection_params = Mock(return_value=connection_params)
182
+
183
+ # Mock pg_dump execution
184
+ mock_db._execute_pg_command = Mock()
185
+
186
+ return mock_db
187
+
188
+ """
189
+ Additional fixtures for ReleaseManager tests.
190
+ Add these to tests/conftest.py
191
+ """
192
+
193
+ @pytest.fixture
194
+ def mock_release_manager_basic(tmp_path):
195
+ """
196
+ Create basic ReleaseManager with minimal mocks.
197
+
198
+ Provides:
199
+ - ReleaseManager instance
200
+ - Mock repo with base_dir
201
+ - Temporary releases/ directory
202
+
203
+ Returns:
204
+ Tuple of (release_mgr, mock_repo, tmp_path)
205
+ """
206
+ from unittest.mock import Mock
207
+ from half_orm_dev.release_manager import ReleaseManager
208
+
209
+ mock_repo = Mock()
210
+ mock_repo.base_dir = str(tmp_path)
211
+
212
+ # Create releases/ directory
213
+ releases_dir = tmp_path / "releases"
214
+ releases_dir.mkdir()
215
+
216
+ release_mgr = ReleaseManager(mock_repo)
217
+
218
+ return release_mgr, mock_repo, tmp_path
219
+
220
+
221
+ @pytest.fixture
222
+ def mock_release_manager_with_hgit(mock_release_manager_basic):
223
+ """
224
+ Create ReleaseManager with fully mocked HGit.
225
+
226
+ Provides:
227
+ - ReleaseManager instance
228
+ - Mock repo with HGit configured
229
+ - HGit mocked for all Git operations
230
+ - Default "happy path" configuration
231
+
232
+ Returns:
233
+ Tuple of (release_mgr, mock_repo, mock_hgit, tmp_path)
234
+ """
235
+ from unittest.mock import Mock
236
+
237
+ release_mgr, mock_repo, tmp_path = mock_release_manager_basic
238
+
239
+ # Mock HGit with all required methods
240
+ mock_hgit = Mock()
241
+
242
+ # Branch and repo state
243
+ mock_hgit.branch = "ho-prod"
244
+ mock_hgit.repos_is_clean.return_value = True
245
+
246
+ # Fetch and sync
247
+ mock_hgit.fetch_from_origin.return_value = None
248
+ mock_hgit.is_branch_synced.return_value = (True, "synced")
249
+ mock_hgit.pull.return_value = None
250
+
251
+ # Git operations
252
+ mock_hgit.add.return_value = None
253
+ mock_hgit.commit.return_value = None
254
+ mock_hgit.push.return_value = None
255
+
256
+ mock_repo.hgit = mock_hgit
257
+
258
+ return release_mgr, mock_repo, mock_hgit, tmp_path
259
+
260
+
261
+ @pytest.fixture
262
+ def mock_release_manager_with_production(mock_release_manager_with_hgit):
263
+ """
264
+ Create ReleaseManager with production version mocking.
265
+
266
+ Provides:
267
+ - Everything from mock_release_manager_with_hgit
268
+ - Mock _get_production_version() to return "1.3.5"
269
+ - Mock model/schema.sql symlink (for tests that directly test _get_production_version)
270
+ - Default production version: 1.3.5
271
+
272
+ Returns:
273
+ Tuple of (release_mgr, mock_repo, mock_hgit, tmp_path, prod_version)
274
+ """
275
+ from unittest.mock import Mock, patch
276
+
277
+ release_mgr, mock_repo, mock_hgit, tmp_path = mock_release_manager_with_hgit
278
+
279
+ # Create model/ directory with schema files (for tests that test _get_production_version directly)
280
+ model_dir = tmp_path / "model"
281
+ model_dir.mkdir()
282
+
283
+ # Create versioned schema file
284
+ prod_version = "1.3.5"
285
+ schema_file = model_dir / f"schema-{prod_version}.sql"
286
+ schema_file.write_text("-- Schema version 1.3.5")
287
+
288
+ # Create symlink schema.sql -> schema-1.3.5.sql
289
+ schema_symlink = model_dir / "schema.sql"
290
+ schema_symlink.symlink_to(f"schema-{prod_version}.sql")
291
+
292
+ # Mock database last_release_s
293
+ mock_database = Mock()
294
+ mock_database.last_release_s = prod_version
295
+ mock_repo.database = mock_database
296
+
297
+ # CRITICAL: Mock _get_production_version() to avoid reading symlink in most tests
298
+ # This allows tests to focus on workflow without setting up full file structure
299
+ release_mgr._get_production_version = Mock(return_value=prod_version)
300
+
301
+ return release_mgr, mock_repo, mock_hgit, tmp_path, prod_version
302
+
303
+
304
+ @pytest.fixture
305
+ def sample_release_files(tmp_path):
306
+ """
307
+ Create sample release files in releases/ directory.
308
+
309
+ Creates:
310
+ - releases/1.3.4.txt (production)
311
+ - releases/1.3.5-rc2.txt (rc)
312
+ - releases/1.4.0-stage.txt (stage)
313
+
314
+ Returns:
315
+ Tuple of (releases_dir, dict of created files)
316
+ """
317
+ releases_dir = tmp_path / "releases"
318
+ releases_dir.mkdir()
319
+
320
+ files = {
321
+ '1.3.4.txt': '',
322
+ '1.3.5-rc2.txt': '',
323
+ '1.4.0-stage.txt': '',
324
+ }
325
+
326
+ for filename, content in files.items():
327
+ (releases_dir / filename).write_text(content)
328
+
329
+ return releases_dir, files