julee 0.1.5__py3-none-any.whl → 0.1.6__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 (105) hide show
  1. julee/docs/sphinx_hcd/__init__.py +146 -13
  2. julee/docs/sphinx_hcd/domain/__init__.py +5 -0
  3. julee/docs/sphinx_hcd/domain/models/__init__.py +32 -0
  4. julee/docs/sphinx_hcd/domain/models/accelerator.py +152 -0
  5. julee/docs/sphinx_hcd/domain/models/app.py +151 -0
  6. julee/docs/sphinx_hcd/domain/models/code_info.py +121 -0
  7. julee/docs/sphinx_hcd/domain/models/epic.py +79 -0
  8. julee/docs/sphinx_hcd/domain/models/integration.py +230 -0
  9. julee/docs/sphinx_hcd/domain/models/journey.py +222 -0
  10. julee/docs/sphinx_hcd/domain/models/persona.py +106 -0
  11. julee/docs/sphinx_hcd/domain/models/story.py +128 -0
  12. julee/docs/sphinx_hcd/domain/repositories/__init__.py +25 -0
  13. julee/docs/sphinx_hcd/domain/repositories/accelerator.py +98 -0
  14. julee/docs/sphinx_hcd/domain/repositories/app.py +57 -0
  15. julee/docs/sphinx_hcd/domain/repositories/base.py +89 -0
  16. julee/docs/sphinx_hcd/domain/repositories/code_info.py +69 -0
  17. julee/docs/sphinx_hcd/domain/repositories/epic.py +62 -0
  18. julee/docs/sphinx_hcd/domain/repositories/integration.py +79 -0
  19. julee/docs/sphinx_hcd/domain/repositories/journey.py +106 -0
  20. julee/docs/sphinx_hcd/domain/repositories/story.py +68 -0
  21. julee/docs/sphinx_hcd/domain/use_cases/__init__.py +64 -0
  22. julee/docs/sphinx_hcd/domain/use_cases/derive_personas.py +166 -0
  23. julee/docs/sphinx_hcd/domain/use_cases/resolve_accelerator_references.py +236 -0
  24. julee/docs/sphinx_hcd/domain/use_cases/resolve_app_references.py +144 -0
  25. julee/docs/sphinx_hcd/domain/use_cases/resolve_story_references.py +121 -0
  26. julee/docs/sphinx_hcd/parsers/__init__.py +48 -0
  27. julee/docs/sphinx_hcd/parsers/ast.py +150 -0
  28. julee/docs/sphinx_hcd/parsers/gherkin.py +155 -0
  29. julee/docs/sphinx_hcd/parsers/yaml.py +184 -0
  30. julee/docs/sphinx_hcd/repositories/__init__.py +4 -0
  31. julee/docs/sphinx_hcd/repositories/memory/__init__.py +25 -0
  32. julee/docs/sphinx_hcd/repositories/memory/accelerator.py +86 -0
  33. julee/docs/sphinx_hcd/repositories/memory/app.py +45 -0
  34. julee/docs/sphinx_hcd/repositories/memory/base.py +106 -0
  35. julee/docs/sphinx_hcd/repositories/memory/code_info.py +59 -0
  36. julee/docs/sphinx_hcd/repositories/memory/epic.py +54 -0
  37. julee/docs/sphinx_hcd/repositories/memory/integration.py +70 -0
  38. julee/docs/sphinx_hcd/repositories/memory/journey.py +96 -0
  39. julee/docs/sphinx_hcd/repositories/memory/story.py +63 -0
  40. julee/docs/sphinx_hcd/sphinx/__init__.py +28 -0
  41. julee/docs/sphinx_hcd/sphinx/adapters.py +116 -0
  42. julee/docs/sphinx_hcd/sphinx/context.py +163 -0
  43. julee/docs/sphinx_hcd/sphinx/directives/__init__.py +160 -0
  44. julee/docs/sphinx_hcd/sphinx/directives/accelerator.py +576 -0
  45. julee/docs/sphinx_hcd/sphinx/directives/app.py +349 -0
  46. julee/docs/sphinx_hcd/sphinx/directives/base.py +211 -0
  47. julee/docs/sphinx_hcd/sphinx/directives/epic.py +434 -0
  48. julee/docs/sphinx_hcd/sphinx/directives/integration.py +220 -0
  49. julee/docs/sphinx_hcd/sphinx/directives/journey.py +642 -0
  50. julee/docs/sphinx_hcd/sphinx/directives/persona.py +345 -0
  51. julee/docs/sphinx_hcd/sphinx/directives/story.py +575 -0
  52. julee/docs/sphinx_hcd/sphinx/event_handlers/__init__.py +16 -0
  53. julee/docs/sphinx_hcd/sphinx/event_handlers/builder_inited.py +31 -0
  54. julee/docs/sphinx_hcd/sphinx/event_handlers/doctree_read.py +27 -0
  55. julee/docs/sphinx_hcd/sphinx/event_handlers/doctree_resolved.py +43 -0
  56. julee/docs/sphinx_hcd/sphinx/event_handlers/env_purge_doc.py +42 -0
  57. julee/docs/sphinx_hcd/sphinx/initialization.py +139 -0
  58. julee/docs/sphinx_hcd/tests/__init__.py +9 -0
  59. julee/docs/sphinx_hcd/tests/conftest.py +6 -0
  60. julee/docs/sphinx_hcd/tests/domain/__init__.py +1 -0
  61. julee/docs/sphinx_hcd/tests/domain/models/__init__.py +1 -0
  62. julee/docs/sphinx_hcd/tests/domain/models/test_accelerator.py +266 -0
  63. julee/docs/sphinx_hcd/tests/domain/models/test_app.py +258 -0
  64. julee/docs/sphinx_hcd/tests/domain/models/test_code_info.py +231 -0
  65. julee/docs/sphinx_hcd/tests/domain/models/test_epic.py +163 -0
  66. julee/docs/sphinx_hcd/tests/domain/models/test_integration.py +327 -0
  67. julee/docs/sphinx_hcd/tests/domain/models/test_journey.py +249 -0
  68. julee/docs/sphinx_hcd/tests/domain/models/test_persona.py +172 -0
  69. julee/docs/sphinx_hcd/tests/domain/models/test_story.py +216 -0
  70. julee/docs/sphinx_hcd/tests/domain/use_cases/__init__.py +1 -0
  71. julee/docs/sphinx_hcd/tests/domain/use_cases/test_derive_personas.py +314 -0
  72. julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_accelerator_references.py +476 -0
  73. julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_app_references.py +265 -0
  74. julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_story_references.py +229 -0
  75. julee/docs/sphinx_hcd/tests/integration/__init__.py +1 -0
  76. julee/docs/sphinx_hcd/tests/parsers/__init__.py +1 -0
  77. julee/docs/sphinx_hcd/tests/parsers/test_ast.py +298 -0
  78. julee/docs/sphinx_hcd/tests/parsers/test_gherkin.py +282 -0
  79. julee/docs/sphinx_hcd/tests/parsers/test_yaml.py +496 -0
  80. julee/docs/sphinx_hcd/tests/repositories/__init__.py +1 -0
  81. julee/docs/sphinx_hcd/tests/repositories/test_accelerator.py +298 -0
  82. julee/docs/sphinx_hcd/tests/repositories/test_app.py +218 -0
  83. julee/docs/sphinx_hcd/tests/repositories/test_base.py +151 -0
  84. julee/docs/sphinx_hcd/tests/repositories/test_code_info.py +253 -0
  85. julee/docs/sphinx_hcd/tests/repositories/test_epic.py +237 -0
  86. julee/docs/sphinx_hcd/tests/repositories/test_integration.py +268 -0
  87. julee/docs/sphinx_hcd/tests/repositories/test_journey.py +294 -0
  88. julee/docs/sphinx_hcd/tests/repositories/test_story.py +236 -0
  89. julee/docs/sphinx_hcd/tests/sphinx/__init__.py +1 -0
  90. julee/docs/sphinx_hcd/tests/sphinx/directives/__init__.py +1 -0
  91. julee/docs/sphinx_hcd/tests/sphinx/directives/test_base.py +160 -0
  92. julee/docs/sphinx_hcd/tests/sphinx/test_adapters.py +176 -0
  93. julee/docs/sphinx_hcd/tests/sphinx/test_context.py +257 -0
  94. {julee-0.1.5.dist-info → julee-0.1.6.dist-info}/METADATA +2 -1
  95. {julee-0.1.5.dist-info → julee-0.1.6.dist-info}/RECORD +98 -13
  96. julee/docs/sphinx_hcd/accelerators.py +0 -1175
  97. julee/docs/sphinx_hcd/apps.py +0 -518
  98. julee/docs/sphinx_hcd/epics.py +0 -453
  99. julee/docs/sphinx_hcd/integrations.py +0 -310
  100. julee/docs/sphinx_hcd/journeys.py +0 -797
  101. julee/docs/sphinx_hcd/personas.py +0 -457
  102. julee/docs/sphinx_hcd/stories.py +0 -960
  103. {julee-0.1.5.dist-info → julee-0.1.6.dist-info}/WHEEL +0 -0
  104. {julee-0.1.5.dist-info → julee-0.1.6.dist-info}/licenses/LICENSE +0 -0
  105. {julee-0.1.5.dist-info → julee-0.1.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,236 @@
1
+ """Tests for MemoryStoryRepository."""
2
+
3
+ import pytest
4
+ import pytest_asyncio
5
+
6
+ from julee.docs.sphinx_hcd.domain.models.story import Story
7
+ from julee.docs.sphinx_hcd.repositories.memory.story import MemoryStoryRepository
8
+
9
+
10
+ def create_story(
11
+ slug: str = "test",
12
+ feature_title: str = "Test Feature",
13
+ persona: str = "User",
14
+ app_slug: str = "app",
15
+ ) -> Story:
16
+ """Helper to create test stories."""
17
+ return Story(
18
+ slug=slug,
19
+ feature_title=feature_title,
20
+ persona=persona,
21
+ app_slug=app_slug,
22
+ file_path=f"tests/e2e/{app_slug}/features/{slug}.feature",
23
+ )
24
+
25
+
26
+ class TestMemoryStoryRepositoryBasicOperations:
27
+ """Test basic CRUD operations."""
28
+
29
+ @pytest.fixture
30
+ def repo(self) -> MemoryStoryRepository:
31
+ """Create a fresh repository."""
32
+ return MemoryStoryRepository()
33
+
34
+ @pytest.mark.asyncio
35
+ async def test_save_and_get(self, repo: MemoryStoryRepository) -> None:
36
+ """Test saving and retrieving a story."""
37
+ story = create_story(slug="submit-order")
38
+ await repo.save(story)
39
+
40
+ retrieved = await repo.get("submit-order")
41
+ assert retrieved is not None
42
+ assert retrieved.slug == "submit-order"
43
+
44
+ @pytest.mark.asyncio
45
+ async def test_get_nonexistent(self, repo: MemoryStoryRepository) -> None:
46
+ """Test getting a nonexistent story returns None."""
47
+ result = await repo.get("nonexistent")
48
+ assert result is None
49
+
50
+ @pytest.mark.asyncio
51
+ async def test_list_all(self, repo: MemoryStoryRepository) -> None:
52
+ """Test listing all stories."""
53
+ await repo.save(create_story(slug="story-1"))
54
+ await repo.save(create_story(slug="story-2"))
55
+ await repo.save(create_story(slug="story-3"))
56
+
57
+ all_stories = await repo.list_all()
58
+ assert len(all_stories) == 3
59
+ slugs = {s.slug for s in all_stories}
60
+ assert slugs == {"story-1", "story-2", "story-3"}
61
+
62
+ @pytest.mark.asyncio
63
+ async def test_delete(self, repo: MemoryStoryRepository) -> None:
64
+ """Test deleting a story."""
65
+ await repo.save(create_story(slug="to-delete"))
66
+ assert await repo.get("to-delete") is not None
67
+
68
+ result = await repo.delete("to-delete")
69
+ assert result is True
70
+ assert await repo.get("to-delete") is None
71
+
72
+ @pytest.mark.asyncio
73
+ async def test_delete_nonexistent(self, repo: MemoryStoryRepository) -> None:
74
+ """Test deleting a nonexistent story."""
75
+ result = await repo.delete("nonexistent")
76
+ assert result is False
77
+
78
+ @pytest.mark.asyncio
79
+ async def test_clear(self, repo: MemoryStoryRepository) -> None:
80
+ """Test clearing all stories."""
81
+ await repo.save(create_story(slug="story-1"))
82
+ await repo.save(create_story(slug="story-2"))
83
+ assert len(await repo.list_all()) == 2
84
+
85
+ await repo.clear()
86
+ assert len(await repo.list_all()) == 0
87
+
88
+
89
+ class TestMemoryStoryRepositoryQueries:
90
+ """Test story-specific query methods."""
91
+
92
+ @pytest.fixture
93
+ def repo(self) -> MemoryStoryRepository:
94
+ """Create a repository with sample data."""
95
+ return MemoryStoryRepository()
96
+
97
+ @pytest_asyncio.fixture
98
+ async def populated_repo(
99
+ self, repo: MemoryStoryRepository
100
+ ) -> MemoryStoryRepository:
101
+ """Create a repository with sample stories."""
102
+ stories = [
103
+ create_story(
104
+ slug="upload-doc",
105
+ feature_title="Upload Document",
106
+ persona="Staff Member",
107
+ app_slug="staff-portal",
108
+ ),
109
+ create_story(
110
+ slug="view-report",
111
+ feature_title="View Report",
112
+ persona="Staff Member",
113
+ app_slug="staff-portal",
114
+ ),
115
+ create_story(
116
+ slug="submit-order",
117
+ feature_title="Submit Order",
118
+ persona="Customer",
119
+ app_slug="checkout-app",
120
+ ),
121
+ create_story(
122
+ slug="track-order",
123
+ feature_title="Track Order",
124
+ persona="Customer",
125
+ app_slug="checkout-app",
126
+ ),
127
+ create_story(
128
+ slug="admin-config",
129
+ feature_title="Admin Configuration",
130
+ persona="Admin",
131
+ app_slug="admin-portal",
132
+ ),
133
+ ]
134
+ for story in stories:
135
+ await repo.save(story)
136
+ return repo
137
+
138
+ @pytest.mark.asyncio
139
+ async def test_get_by_app(self, populated_repo: MemoryStoryRepository) -> None:
140
+ """Test getting stories by app."""
141
+ stories = await populated_repo.get_by_app("staff-portal")
142
+ assert len(stories) == 2
143
+ assert all(s.app_slug == "staff-portal" for s in stories)
144
+
145
+ @pytest.mark.asyncio
146
+ async def test_get_by_app_case_insensitive(
147
+ self, populated_repo: MemoryStoryRepository
148
+ ) -> None:
149
+ """Test app matching is case-insensitive."""
150
+ stories = await populated_repo.get_by_app("Staff-Portal")
151
+ assert len(stories) == 2
152
+
153
+ @pytest.mark.asyncio
154
+ async def test_get_by_app_no_results(
155
+ self, populated_repo: MemoryStoryRepository
156
+ ) -> None:
157
+ """Test getting stories for app with no stories."""
158
+ stories = await populated_repo.get_by_app("nonexistent-app")
159
+ assert len(stories) == 0
160
+
161
+ @pytest.mark.asyncio
162
+ async def test_get_by_persona(self, populated_repo: MemoryStoryRepository) -> None:
163
+ """Test getting stories by persona."""
164
+ stories = await populated_repo.get_by_persona("Customer")
165
+ assert len(stories) == 2
166
+ assert all(s.persona == "Customer" for s in stories)
167
+
168
+ @pytest.mark.asyncio
169
+ async def test_get_by_persona_case_insensitive(
170
+ self, populated_repo: MemoryStoryRepository
171
+ ) -> None:
172
+ """Test persona matching is case-insensitive."""
173
+ stories = await populated_repo.get_by_persona("staff member")
174
+ assert len(stories) == 2
175
+
176
+ @pytest.mark.asyncio
177
+ async def test_get_by_feature_title(
178
+ self, populated_repo: MemoryStoryRepository
179
+ ) -> None:
180
+ """Test getting a story by feature title."""
181
+ story = await populated_repo.get_by_feature_title("Upload Document")
182
+ assert story is not None
183
+ assert story.slug == "upload-doc"
184
+
185
+ @pytest.mark.asyncio
186
+ async def test_get_by_feature_title_case_insensitive(
187
+ self, populated_repo: MemoryStoryRepository
188
+ ) -> None:
189
+ """Test feature title matching is case-insensitive."""
190
+ story = await populated_repo.get_by_feature_title("upload document")
191
+ assert story is not None
192
+ assert story.slug == "upload-doc"
193
+
194
+ @pytest.mark.asyncio
195
+ async def test_get_by_feature_title_not_found(
196
+ self, populated_repo: MemoryStoryRepository
197
+ ) -> None:
198
+ """Test getting story by nonexistent feature title."""
199
+ story = await populated_repo.get_by_feature_title("Nonexistent Feature")
200
+ assert story is None
201
+
202
+ @pytest.mark.asyncio
203
+ async def test_get_apps_with_stories(
204
+ self, populated_repo: MemoryStoryRepository
205
+ ) -> None:
206
+ """Test getting apps that have stories."""
207
+ apps = await populated_repo.get_apps_with_stories()
208
+ assert apps == {"staff-portal", "checkout-app", "admin-portal"}
209
+
210
+ @pytest.mark.asyncio
211
+ async def test_get_all_personas(
212
+ self, populated_repo: MemoryStoryRepository
213
+ ) -> None:
214
+ """Test getting all unique personas."""
215
+ personas = await populated_repo.get_all_personas()
216
+ assert personas == {"staff member", "customer", "admin"}
217
+
218
+ @pytest.mark.asyncio
219
+ async def test_get_all_personas_excludes_unknown(
220
+ self, repo: MemoryStoryRepository
221
+ ) -> None:
222
+ """Test that 'unknown' persona is excluded from results."""
223
+ await repo.save(
224
+ Story(
225
+ slug="test",
226
+ feature_title="Test",
227
+ persona="unknown",
228
+ app_slug="app",
229
+ file_path="test.feature",
230
+ )
231
+ )
232
+ await repo.save(create_story(slug="known", persona="Known User"))
233
+
234
+ personas = await repo.get_all_personas()
235
+ assert "unknown" not in personas
236
+ assert "known user" in personas
@@ -0,0 +1 @@
1
+ """Sphinx application layer tests."""
@@ -0,0 +1 @@
1
+ """Tests for sphinx_hcd directives."""
@@ -0,0 +1,160 @@
1
+ """Tests for base directive utilities."""
2
+
3
+ from julee.docs.sphinx_hcd.sphinx.directives.base import make_deprecated_directive
4
+
5
+
6
+ class TestMakeDeprecatedDirective:
7
+ """Test make_deprecated_directive function."""
8
+
9
+ def test_creates_subclass(self) -> None:
10
+ """Test that function creates a proper subclass."""
11
+ from sphinx.util.docutils import SphinxDirective
12
+
13
+ # Create a simple base class
14
+ class TestDirective(SphinxDirective):
15
+ def run(self):
16
+ return []
17
+
18
+ DeprecatedClass = make_deprecated_directive(
19
+ TestDirective,
20
+ "old-name",
21
+ "new-name",
22
+ )
23
+
24
+ assert issubclass(DeprecatedClass, TestDirective)
25
+
26
+ def test_class_name_set(self) -> None:
27
+ """Test that deprecated class has appropriate name."""
28
+ from sphinx.util.docutils import SphinxDirective
29
+
30
+ class TestDirective(SphinxDirective):
31
+ def run(self):
32
+ return []
33
+
34
+ DeprecatedClass = make_deprecated_directive(
35
+ TestDirective,
36
+ "old-name",
37
+ "new-name",
38
+ )
39
+
40
+ assert "Deprecated" in DeprecatedClass.__name__
41
+
42
+
43
+ class TestDirectiveImports:
44
+ """Test that all directives can be imported."""
45
+
46
+ def test_story_directives_import(self) -> None:
47
+ """Test story directive imports."""
48
+ from julee.docs.sphinx_hcd.sphinx.directives.story import (
49
+ StoriesDirective,
50
+ StoryAppDirective,
51
+ StoryIndexDirective,
52
+ StoryListForAppDirective,
53
+ StoryListForPersonaDirective,
54
+ StoryRefDirective,
55
+ )
56
+
57
+ assert StoryAppDirective is not None
58
+ assert StoryIndexDirective is not None
59
+ assert StoryListForAppDirective is not None
60
+ assert StoryListForPersonaDirective is not None
61
+ assert StoryRefDirective is not None
62
+ assert StoriesDirective is not None
63
+
64
+ def test_journey_directives_import(self) -> None:
65
+ """Test journey directive imports."""
66
+ from julee.docs.sphinx_hcd.sphinx.directives.journey import (
67
+ DefineJourneyDirective,
68
+ JourneyIndexDirective,
69
+ JourneysForPersonaDirective,
70
+ StepEpicDirective,
71
+ StepPhaseDirective,
72
+ StepStoryDirective,
73
+ )
74
+
75
+ assert DefineJourneyDirective is not None
76
+ assert JourneyIndexDirective is not None
77
+ assert JourneysForPersonaDirective is not None
78
+ assert StepEpicDirective is not None
79
+ assert StepPhaseDirective is not None
80
+ assert StepStoryDirective is not None
81
+
82
+ def test_epic_directives_import(self) -> None:
83
+ """Test epic directive imports."""
84
+ from julee.docs.sphinx_hcd.sphinx.directives.epic import (
85
+ DefineEpicDirective,
86
+ EpicIndexDirective,
87
+ EpicsForPersonaDirective,
88
+ EpicStoryDirective,
89
+ )
90
+
91
+ assert DefineEpicDirective is not None
92
+ assert EpicIndexDirective is not None
93
+ assert EpicStoryDirective is not None
94
+ assert EpicsForPersonaDirective is not None
95
+
96
+ def test_app_directives_import(self) -> None:
97
+ """Test app directive imports."""
98
+ from julee.docs.sphinx_hcd.sphinx.directives.app import (
99
+ AppIndexDirective,
100
+ AppsForPersonaDirective,
101
+ DefineAppDirective,
102
+ )
103
+
104
+ assert DefineAppDirective is not None
105
+ assert AppIndexDirective is not None
106
+ assert AppsForPersonaDirective is not None
107
+
108
+ def test_accelerator_directives_import(self) -> None:
109
+ """Test accelerator directive imports."""
110
+ from julee.docs.sphinx_hcd.sphinx.directives.accelerator import (
111
+ AcceleratorDependencyDiagramDirective,
112
+ AcceleratorIndexDirective,
113
+ AcceleratorsForAppDirective,
114
+ AcceleratorStatusDirective,
115
+ DefineAcceleratorDirective,
116
+ )
117
+
118
+ assert DefineAcceleratorDirective is not None
119
+ assert AcceleratorIndexDirective is not None
120
+ assert AcceleratorsForAppDirective is not None
121
+ assert AcceleratorDependencyDiagramDirective is not None
122
+ assert AcceleratorStatusDirective is not None
123
+
124
+ def test_integration_directives_import(self) -> None:
125
+ """Test integration directive imports."""
126
+ from julee.docs.sphinx_hcd.sphinx.directives.integration import (
127
+ DefineIntegrationDirective,
128
+ IntegrationIndexDirective,
129
+ )
130
+
131
+ assert DefineIntegrationDirective is not None
132
+ assert IntegrationIndexDirective is not None
133
+
134
+ def test_persona_directives_import(self) -> None:
135
+ """Test persona directive imports."""
136
+ from julee.docs.sphinx_hcd.sphinx.directives.persona import (
137
+ PersonaDiagramDirective,
138
+ PersonaIndexDiagramDirective,
139
+ )
140
+
141
+ assert PersonaDiagramDirective is not None
142
+ assert PersonaIndexDiagramDirective is not None
143
+
144
+
145
+ class TestEventHandlerImports:
146
+ """Test that all event handlers can be imported."""
147
+
148
+ def test_event_handlers_import(self) -> None:
149
+ """Test event handler imports."""
150
+ from julee.docs.sphinx_hcd.sphinx.event_handlers import (
151
+ on_builder_inited,
152
+ on_doctree_read,
153
+ on_doctree_resolved,
154
+ on_env_purge_doc,
155
+ )
156
+
157
+ assert on_builder_inited is not None
158
+ assert on_doctree_read is not None
159
+ assert on_doctree_resolved is not None
160
+ assert on_env_purge_doc is not None
@@ -0,0 +1,176 @@
1
+ """Tests for SyncRepositoryAdapter."""
2
+
3
+ import pytest
4
+ from pydantic import BaseModel
5
+
6
+ from julee.docs.sphinx_hcd.repositories.memory.base import MemoryRepositoryMixin
7
+ from julee.docs.sphinx_hcd.sphinx.adapters import SyncRepositoryAdapter
8
+
9
+
10
+ class SampleEntity(BaseModel):
11
+ """Simple entity for adapter tests."""
12
+
13
+ id: str
14
+ name: str
15
+ value: int = 0
16
+
17
+
18
+ class SampleMemoryRepository(MemoryRepositoryMixin[SampleEntity]):
19
+ """Sample repository implementation for testing."""
20
+
21
+ def __init__(self) -> None:
22
+ self.storage: dict[str, SampleEntity] = {}
23
+ self.entity_name = "SampleEntity"
24
+ self.id_field = "id"
25
+
26
+ async def find_by_name(self, name: str) -> list[SampleEntity]:
27
+ """Custom query method for testing run_async."""
28
+ return [e for e in self.storage.values() if e.name == name]
29
+
30
+
31
+ class TestSyncRepositoryAdapter:
32
+ """Test suite for SyncRepositoryAdapter."""
33
+
34
+ @pytest.fixture
35
+ def async_repo(self) -> SampleMemoryRepository:
36
+ """Create a fresh test repository."""
37
+ return SampleMemoryRepository()
38
+
39
+ @pytest.fixture
40
+ def sync_repo(
41
+ self, async_repo: SampleMemoryRepository
42
+ ) -> SyncRepositoryAdapter[SampleEntity]:
43
+ """Create a sync adapter wrapping the async repo."""
44
+ return SyncRepositoryAdapter(async_repo)
45
+
46
+ @pytest.fixture
47
+ def sample_entity(self) -> SampleEntity:
48
+ """Create a sample test entity."""
49
+ return SampleEntity(id="test-1", name="Test One", value=42)
50
+
51
+ def test_save_and_get(
52
+ self,
53
+ sync_repo: SyncRepositoryAdapter[SampleEntity],
54
+ sample_entity: SampleEntity,
55
+ ) -> None:
56
+ """Test saving and retrieving an entity."""
57
+ # Save
58
+ sync_repo.save(sample_entity)
59
+
60
+ # Get
61
+ retrieved = sync_repo.get("test-1")
62
+ assert retrieved is not None
63
+ assert retrieved.id == "test-1"
64
+ assert retrieved.name == "Test One"
65
+ assert retrieved.value == 42
66
+
67
+ def test_get_nonexistent(
68
+ self,
69
+ sync_repo: SyncRepositoryAdapter[SampleEntity],
70
+ ) -> None:
71
+ """Test getting a nonexistent entity returns None."""
72
+ result = sync_repo.get("nonexistent")
73
+ assert result is None
74
+
75
+ def test_get_many(
76
+ self,
77
+ sync_repo: SyncRepositoryAdapter[SampleEntity],
78
+ ) -> None:
79
+ """Test retrieving multiple entities."""
80
+ # Save some entities
81
+ sync_repo.save(SampleEntity(id="a", name="A"))
82
+ sync_repo.save(SampleEntity(id="b", name="B"))
83
+ sync_repo.save(SampleEntity(id="c", name="C"))
84
+
85
+ # Get many
86
+ result = sync_repo.get_many(["a", "c", "nonexistent"])
87
+ assert result["a"] is not None
88
+ assert result["a"].name == "A"
89
+ assert result["c"] is not None
90
+ assert result["c"].name == "C"
91
+ assert result["nonexistent"] is None
92
+
93
+ def test_list_all(
94
+ self,
95
+ sync_repo: SyncRepositoryAdapter[SampleEntity],
96
+ ) -> None:
97
+ """Test listing all entities."""
98
+ # Initially empty
99
+ assert sync_repo.list_all() == []
100
+
101
+ # Add some entities
102
+ sync_repo.save(SampleEntity(id="1", name="First"))
103
+ sync_repo.save(SampleEntity(id="2", name="Second"))
104
+
105
+ # List all
106
+ all_entities = sync_repo.list_all()
107
+ assert len(all_entities) == 2
108
+ names = {e.name for e in all_entities}
109
+ assert names == {"First", "Second"}
110
+
111
+ def test_delete(
112
+ self,
113
+ sync_repo: SyncRepositoryAdapter[SampleEntity],
114
+ sample_entity: SampleEntity,
115
+ ) -> None:
116
+ """Test deleting an entity."""
117
+ sync_repo.save(sample_entity)
118
+ assert sync_repo.get("test-1") is not None
119
+
120
+ # Delete
121
+ result = sync_repo.delete("test-1")
122
+ assert result is True
123
+ assert sync_repo.get("test-1") is None
124
+
125
+ # Delete nonexistent
126
+ result = sync_repo.delete("test-1")
127
+ assert result is False
128
+
129
+ def test_clear(
130
+ self,
131
+ sync_repo: SyncRepositoryAdapter[SampleEntity],
132
+ ) -> None:
133
+ """Test clearing all entities."""
134
+ sync_repo.save(SampleEntity(id="1", name="One"))
135
+ sync_repo.save(SampleEntity(id="2", name="Two"))
136
+ assert len(sync_repo.list_all()) == 2
137
+
138
+ sync_repo.clear()
139
+ assert len(sync_repo.list_all()) == 0
140
+
141
+ def test_async_repo_property(
142
+ self,
143
+ sync_repo: SyncRepositoryAdapter[SampleEntity],
144
+ async_repo: SampleMemoryRepository,
145
+ ) -> None:
146
+ """Test accessing the underlying async repo."""
147
+ assert sync_repo.async_repo is async_repo
148
+
149
+ def test_run_async_custom_method(
150
+ self,
151
+ sync_repo: SyncRepositoryAdapter[SampleEntity],
152
+ async_repo: SampleMemoryRepository,
153
+ ) -> None:
154
+ """Test running a custom async method via run_async."""
155
+ sync_repo.save(SampleEntity(id="1", name="Alice", value=1))
156
+ sync_repo.save(SampleEntity(id="2", name="Bob", value=2))
157
+ sync_repo.save(SampleEntity(id="3", name="Alice", value=3))
158
+
159
+ # Use run_async for custom query
160
+ result = sync_repo.run_async(async_repo.find_by_name("Alice"))
161
+ assert len(result) == 2
162
+ assert all(e.name == "Alice" for e in result)
163
+
164
+ def test_save_overwrites_existing(
165
+ self,
166
+ sync_repo: SyncRepositoryAdapter[SampleEntity],
167
+ ) -> None:
168
+ """Test that saving with same ID overwrites."""
169
+ sync_repo.save(SampleEntity(id="x", name="Original", value=1))
170
+ sync_repo.save(SampleEntity(id="x", name="Updated", value=2))
171
+
172
+ retrieved = sync_repo.get("x")
173
+ assert retrieved is not None
174
+ assert retrieved.name == "Updated"
175
+ assert retrieved.value == 2
176
+ assert len(sync_repo.list_all()) == 1