julee 0.1.4__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 (165) hide show
  1. julee/__init__.py +1 -1
  2. julee/api/tests/routers/test_assembly_specifications.py +2 -0
  3. julee/api/tests/routers/test_documents.py +2 -0
  4. julee/api/tests/routers/test_knowledge_service_configs.py +2 -0
  5. julee/api/tests/routers/test_knowledge_service_queries.py +2 -0
  6. julee/api/tests/routers/test_system.py +2 -0
  7. julee/api/tests/routers/test_workflows.py +2 -0
  8. julee/api/tests/test_app.py +2 -0
  9. julee/api/tests/test_dependencies.py +2 -0
  10. julee/api/tests/test_requests.py +2 -0
  11. julee/contrib/polling/__init__.py +22 -19
  12. julee/contrib/polling/apps/__init__.py +17 -0
  13. julee/contrib/polling/apps/worker/__init__.py +17 -0
  14. julee/contrib/polling/apps/worker/pipelines.py +288 -0
  15. julee/contrib/polling/domain/__init__.py +7 -9
  16. julee/contrib/polling/domain/models/__init__.py +6 -7
  17. julee/contrib/polling/domain/models/polling_config.py +18 -1
  18. julee/contrib/polling/domain/services/__init__.py +6 -5
  19. julee/contrib/polling/domain/services/poller.py +1 -1
  20. julee/contrib/polling/infrastructure/__init__.py +9 -8
  21. julee/contrib/polling/infrastructure/services/__init__.py +6 -5
  22. julee/contrib/polling/infrastructure/services/polling/__init__.py +6 -5
  23. julee/contrib/polling/infrastructure/services/polling/http/__init__.py +6 -5
  24. julee/contrib/polling/infrastructure/services/polling/http/http_poller_service.py +5 -2
  25. julee/contrib/polling/infrastructure/temporal/__init__.py +12 -12
  26. julee/contrib/polling/infrastructure/temporal/activities.py +1 -1
  27. julee/contrib/polling/infrastructure/temporal/manager.py +291 -0
  28. julee/contrib/polling/infrastructure/temporal/proxies.py +1 -1
  29. julee/contrib/polling/tests/unit/apps/worker/test_pipelines.py +580 -0
  30. julee/contrib/polling/tests/unit/infrastructure/services/polling/http/test_http_poller_service.py +40 -2
  31. julee/contrib/polling/tests/unit/infrastructure/temporal/__init__.py +7 -0
  32. julee/contrib/polling/tests/unit/infrastructure/temporal/test_manager.py +475 -0
  33. julee/docs/sphinx_hcd/__init__.py +146 -13
  34. julee/docs/sphinx_hcd/domain/__init__.py +5 -0
  35. julee/docs/sphinx_hcd/domain/models/__init__.py +32 -0
  36. julee/docs/sphinx_hcd/domain/models/accelerator.py +152 -0
  37. julee/docs/sphinx_hcd/domain/models/app.py +151 -0
  38. julee/docs/sphinx_hcd/domain/models/code_info.py +121 -0
  39. julee/docs/sphinx_hcd/domain/models/epic.py +79 -0
  40. julee/docs/sphinx_hcd/domain/models/integration.py +230 -0
  41. julee/docs/sphinx_hcd/domain/models/journey.py +222 -0
  42. julee/docs/sphinx_hcd/domain/models/persona.py +106 -0
  43. julee/docs/sphinx_hcd/domain/models/story.py +128 -0
  44. julee/docs/sphinx_hcd/domain/repositories/__init__.py +25 -0
  45. julee/docs/sphinx_hcd/domain/repositories/accelerator.py +98 -0
  46. julee/docs/sphinx_hcd/domain/repositories/app.py +57 -0
  47. julee/docs/sphinx_hcd/domain/repositories/base.py +89 -0
  48. julee/docs/sphinx_hcd/domain/repositories/code_info.py +69 -0
  49. julee/docs/sphinx_hcd/domain/repositories/epic.py +62 -0
  50. julee/docs/sphinx_hcd/domain/repositories/integration.py +79 -0
  51. julee/docs/sphinx_hcd/domain/repositories/journey.py +106 -0
  52. julee/docs/sphinx_hcd/domain/repositories/story.py +68 -0
  53. julee/docs/sphinx_hcd/domain/use_cases/__init__.py +64 -0
  54. julee/docs/sphinx_hcd/domain/use_cases/derive_personas.py +166 -0
  55. julee/docs/sphinx_hcd/domain/use_cases/resolve_accelerator_references.py +236 -0
  56. julee/docs/sphinx_hcd/domain/use_cases/resolve_app_references.py +144 -0
  57. julee/docs/sphinx_hcd/domain/use_cases/resolve_story_references.py +121 -0
  58. julee/docs/sphinx_hcd/parsers/__init__.py +48 -0
  59. julee/docs/sphinx_hcd/parsers/ast.py +150 -0
  60. julee/docs/sphinx_hcd/parsers/gherkin.py +155 -0
  61. julee/docs/sphinx_hcd/parsers/yaml.py +184 -0
  62. julee/docs/sphinx_hcd/repositories/__init__.py +4 -0
  63. julee/docs/sphinx_hcd/repositories/memory/__init__.py +25 -0
  64. julee/docs/sphinx_hcd/repositories/memory/accelerator.py +86 -0
  65. julee/docs/sphinx_hcd/repositories/memory/app.py +45 -0
  66. julee/docs/sphinx_hcd/repositories/memory/base.py +106 -0
  67. julee/docs/sphinx_hcd/repositories/memory/code_info.py +59 -0
  68. julee/docs/sphinx_hcd/repositories/memory/epic.py +54 -0
  69. julee/docs/sphinx_hcd/repositories/memory/integration.py +70 -0
  70. julee/docs/sphinx_hcd/repositories/memory/journey.py +96 -0
  71. julee/docs/sphinx_hcd/repositories/memory/story.py +63 -0
  72. julee/docs/sphinx_hcd/sphinx/__init__.py +28 -0
  73. julee/docs/sphinx_hcd/sphinx/adapters.py +116 -0
  74. julee/docs/sphinx_hcd/sphinx/context.py +163 -0
  75. julee/docs/sphinx_hcd/sphinx/directives/__init__.py +160 -0
  76. julee/docs/sphinx_hcd/sphinx/directives/accelerator.py +576 -0
  77. julee/docs/sphinx_hcd/sphinx/directives/app.py +349 -0
  78. julee/docs/sphinx_hcd/sphinx/directives/base.py +211 -0
  79. julee/docs/sphinx_hcd/sphinx/directives/epic.py +434 -0
  80. julee/docs/sphinx_hcd/sphinx/directives/integration.py +220 -0
  81. julee/docs/sphinx_hcd/sphinx/directives/journey.py +642 -0
  82. julee/docs/sphinx_hcd/sphinx/directives/persona.py +345 -0
  83. julee/docs/sphinx_hcd/sphinx/directives/story.py +575 -0
  84. julee/docs/sphinx_hcd/sphinx/event_handlers/__init__.py +16 -0
  85. julee/docs/sphinx_hcd/sphinx/event_handlers/builder_inited.py +31 -0
  86. julee/docs/sphinx_hcd/sphinx/event_handlers/doctree_read.py +27 -0
  87. julee/docs/sphinx_hcd/sphinx/event_handlers/doctree_resolved.py +43 -0
  88. julee/docs/sphinx_hcd/sphinx/event_handlers/env_purge_doc.py +42 -0
  89. julee/docs/sphinx_hcd/sphinx/initialization.py +139 -0
  90. julee/docs/sphinx_hcd/tests/__init__.py +9 -0
  91. julee/docs/sphinx_hcd/tests/conftest.py +6 -0
  92. julee/docs/sphinx_hcd/tests/domain/__init__.py +1 -0
  93. julee/docs/sphinx_hcd/tests/domain/models/__init__.py +1 -0
  94. julee/docs/sphinx_hcd/tests/domain/models/test_accelerator.py +266 -0
  95. julee/docs/sphinx_hcd/tests/domain/models/test_app.py +258 -0
  96. julee/docs/sphinx_hcd/tests/domain/models/test_code_info.py +231 -0
  97. julee/docs/sphinx_hcd/tests/domain/models/test_epic.py +163 -0
  98. julee/docs/sphinx_hcd/tests/domain/models/test_integration.py +327 -0
  99. julee/docs/sphinx_hcd/tests/domain/models/test_journey.py +249 -0
  100. julee/docs/sphinx_hcd/tests/domain/models/test_persona.py +172 -0
  101. julee/docs/sphinx_hcd/tests/domain/models/test_story.py +216 -0
  102. julee/docs/sphinx_hcd/tests/domain/use_cases/__init__.py +1 -0
  103. julee/docs/sphinx_hcd/tests/domain/use_cases/test_derive_personas.py +314 -0
  104. julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_accelerator_references.py +476 -0
  105. julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_app_references.py +265 -0
  106. julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_story_references.py +229 -0
  107. julee/docs/sphinx_hcd/tests/integration/__init__.py +1 -0
  108. julee/docs/sphinx_hcd/tests/parsers/__init__.py +1 -0
  109. julee/docs/sphinx_hcd/tests/parsers/test_ast.py +298 -0
  110. julee/docs/sphinx_hcd/tests/parsers/test_gherkin.py +282 -0
  111. julee/docs/sphinx_hcd/tests/parsers/test_yaml.py +496 -0
  112. julee/docs/sphinx_hcd/tests/repositories/__init__.py +1 -0
  113. julee/docs/sphinx_hcd/tests/repositories/test_accelerator.py +298 -0
  114. julee/docs/sphinx_hcd/tests/repositories/test_app.py +218 -0
  115. julee/docs/sphinx_hcd/tests/repositories/test_base.py +151 -0
  116. julee/docs/sphinx_hcd/tests/repositories/test_code_info.py +253 -0
  117. julee/docs/sphinx_hcd/tests/repositories/test_epic.py +237 -0
  118. julee/docs/sphinx_hcd/tests/repositories/test_integration.py +268 -0
  119. julee/docs/sphinx_hcd/tests/repositories/test_journey.py +294 -0
  120. julee/docs/sphinx_hcd/tests/repositories/test_story.py +236 -0
  121. julee/docs/sphinx_hcd/tests/sphinx/__init__.py +1 -0
  122. julee/docs/sphinx_hcd/tests/sphinx/directives/__init__.py +1 -0
  123. julee/docs/sphinx_hcd/tests/sphinx/directives/test_base.py +160 -0
  124. julee/docs/sphinx_hcd/tests/sphinx/test_adapters.py +176 -0
  125. julee/docs/sphinx_hcd/tests/sphinx/test_context.py +257 -0
  126. julee/domain/models/assembly/tests/test_assembly.py +2 -0
  127. julee/domain/models/assembly_specification/tests/test_assembly_specification.py +2 -0
  128. julee/domain/models/assembly_specification/tests/test_knowledge_service_query.py +2 -0
  129. julee/domain/models/custom_fields/tests/test_custom_fields.py +2 -0
  130. julee/domain/models/document/tests/test_document.py +2 -0
  131. julee/domain/models/policy/tests/test_document_policy_validation.py +2 -0
  132. julee/domain/models/policy/tests/test_policy.py +2 -0
  133. julee/domain/use_cases/tests/test_extract_assemble_data.py +2 -0
  134. julee/domain/use_cases/tests/test_initialize_system_data.py +2 -0
  135. julee/domain/use_cases/tests/test_validate_document.py +2 -0
  136. julee/maintenance/release.py +10 -5
  137. julee/repositories/memory/tests/test_document.py +2 -0
  138. julee/repositories/memory/tests/test_document_policy_validation.py +2 -0
  139. julee/repositories/memory/tests/test_policy.py +2 -0
  140. julee/repositories/minio/tests/test_assembly.py +2 -0
  141. julee/repositories/minio/tests/test_assembly_specification.py +2 -0
  142. julee/repositories/minio/tests/test_client_protocol.py +3 -0
  143. julee/repositories/minio/tests/test_document.py +2 -0
  144. julee/repositories/minio/tests/test_document_policy_validation.py +2 -0
  145. julee/repositories/minio/tests/test_knowledge_service_config.py +2 -0
  146. julee/repositories/minio/tests/test_knowledge_service_query.py +2 -0
  147. julee/repositories/minio/tests/test_policy.py +2 -0
  148. julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +2 -0
  149. julee/services/knowledge_service/memory/test_knowledge_service.py +2 -0
  150. julee/services/knowledge_service/test_factory.py +2 -0
  151. julee/util/tests/test_decorators.py +2 -0
  152. julee-0.1.6.dist-info/METADATA +104 -0
  153. julee-0.1.6.dist-info/RECORD +288 -0
  154. julee/docs/sphinx_hcd/accelerators.py +0 -1175
  155. julee/docs/sphinx_hcd/apps.py +0 -518
  156. julee/docs/sphinx_hcd/epics.py +0 -453
  157. julee/docs/sphinx_hcd/integrations.py +0 -310
  158. julee/docs/sphinx_hcd/journeys.py +0 -797
  159. julee/docs/sphinx_hcd/personas.py +0 -457
  160. julee/docs/sphinx_hcd/stories.py +0 -960
  161. julee-0.1.4.dist-info/METADATA +0 -197
  162. julee-0.1.4.dist-info/RECORD +0 -196
  163. {julee-0.1.4.dist-info → julee-0.1.6.dist-info}/WHEEL +0 -0
  164. {julee-0.1.4.dist-info → julee-0.1.6.dist-info}/licenses/LICENSE +0 -0
  165. {julee-0.1.4.dist-info → julee-0.1.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,268 @@
1
+ """Tests for MemoryIntegrationRepository."""
2
+
3
+ import pytest
4
+ import pytest_asyncio
5
+
6
+ from julee.docs.sphinx_hcd.domain.models.integration import (
7
+ Direction,
8
+ ExternalDependency,
9
+ Integration,
10
+ )
11
+ from julee.docs.sphinx_hcd.repositories.memory.integration import (
12
+ MemoryIntegrationRepository,
13
+ )
14
+
15
+
16
+ def create_integration(
17
+ slug: str = "test-integration",
18
+ module: str = "test_integration",
19
+ name: str = "Test Integration",
20
+ direction: Direction = Direction.BIDIRECTIONAL,
21
+ depends_on: list[ExternalDependency] | None = None,
22
+ ) -> Integration:
23
+ """Helper to create test integrations."""
24
+ return Integration(
25
+ slug=slug,
26
+ module=module,
27
+ name=name,
28
+ direction=direction,
29
+ depends_on=depends_on or [],
30
+ manifest_path=f"integrations/{module}/integration.yaml",
31
+ )
32
+
33
+
34
+ class TestMemoryIntegrationRepositoryBasicOperations:
35
+ """Test basic CRUD operations."""
36
+
37
+ @pytest.fixture
38
+ def repo(self) -> MemoryIntegrationRepository:
39
+ """Create a fresh repository."""
40
+ return MemoryIntegrationRepository()
41
+
42
+ @pytest.mark.asyncio
43
+ async def test_save_and_get(self, repo: MemoryIntegrationRepository) -> None:
44
+ """Test saving and retrieving an integration."""
45
+ integration = create_integration(slug="data-sync")
46
+ await repo.save(integration)
47
+
48
+ retrieved = await repo.get("data-sync")
49
+ assert retrieved is not None
50
+ assert retrieved.slug == "data-sync"
51
+
52
+ @pytest.mark.asyncio
53
+ async def test_get_nonexistent(self, repo: MemoryIntegrationRepository) -> None:
54
+ """Test getting a nonexistent integration returns None."""
55
+ result = await repo.get("nonexistent")
56
+ assert result is None
57
+
58
+ @pytest.mark.asyncio
59
+ async def test_list_all(self, repo: MemoryIntegrationRepository) -> None:
60
+ """Test listing all integrations."""
61
+ await repo.save(create_integration(slug="int-1", module="int_1"))
62
+ await repo.save(create_integration(slug="int-2", module="int_2"))
63
+ await repo.save(create_integration(slug="int-3", module="int_3"))
64
+
65
+ all_integrations = await repo.list_all()
66
+ assert len(all_integrations) == 3
67
+ slugs = {i.slug for i in all_integrations}
68
+ assert slugs == {"int-1", "int-2", "int-3"}
69
+
70
+ @pytest.mark.asyncio
71
+ async def test_delete(self, repo: MemoryIntegrationRepository) -> None:
72
+ """Test deleting an integration."""
73
+ await repo.save(create_integration(slug="to-delete", module="to_delete"))
74
+ assert await repo.get("to-delete") is not None
75
+
76
+ result = await repo.delete("to-delete")
77
+ assert result is True
78
+ assert await repo.get("to-delete") is None
79
+
80
+ @pytest.mark.asyncio
81
+ async def test_delete_nonexistent(self, repo: MemoryIntegrationRepository) -> None:
82
+ """Test deleting a nonexistent integration."""
83
+ result = await repo.delete("nonexistent")
84
+ assert result is False
85
+
86
+ @pytest.mark.asyncio
87
+ async def test_clear(self, repo: MemoryIntegrationRepository) -> None:
88
+ """Test clearing all integrations."""
89
+ await repo.save(create_integration(slug="int-1", module="int_1"))
90
+ await repo.save(create_integration(slug="int-2", module="int_2"))
91
+ assert len(await repo.list_all()) == 2
92
+
93
+ await repo.clear()
94
+ assert len(await repo.list_all()) == 0
95
+
96
+
97
+ class TestMemoryIntegrationRepositoryQueries:
98
+ """Test integration-specific query methods."""
99
+
100
+ @pytest.fixture
101
+ def repo(self) -> MemoryIntegrationRepository:
102
+ """Create a repository."""
103
+ return MemoryIntegrationRepository()
104
+
105
+ @pytest_asyncio.fixture
106
+ async def populated_repo(
107
+ self, repo: MemoryIntegrationRepository
108
+ ) -> MemoryIntegrationRepository:
109
+ """Create a repository with sample integrations."""
110
+ integrations = [
111
+ create_integration(
112
+ slug="pilot-data",
113
+ module="pilot_data",
114
+ name="Pilot Data Collection",
115
+ direction=Direction.INBOUND,
116
+ depends_on=[ExternalDependency(name="Pilot API")],
117
+ ),
118
+ create_integration(
119
+ slug="analytics-export",
120
+ module="analytics_export",
121
+ name="Analytics Export",
122
+ direction=Direction.OUTBOUND,
123
+ depends_on=[
124
+ ExternalDependency(name="AWS S3"),
125
+ ExternalDependency(name="Analytics Service"),
126
+ ],
127
+ ),
128
+ create_integration(
129
+ slug="data-sync",
130
+ module="data_sync",
131
+ name="Data Sync",
132
+ direction=Direction.BIDIRECTIONAL,
133
+ depends_on=[ExternalDependency(name="AWS S3")],
134
+ ),
135
+ create_integration(
136
+ slug="notifications",
137
+ module="notifications",
138
+ name="Notifications",
139
+ direction=Direction.OUTBOUND,
140
+ ),
141
+ create_integration(
142
+ slug="file-import",
143
+ module="file_import",
144
+ name="File Import",
145
+ direction=Direction.INBOUND,
146
+ ),
147
+ ]
148
+ for integration in integrations:
149
+ await repo.save(integration)
150
+ return repo
151
+
152
+ @pytest.mark.asyncio
153
+ async def test_get_by_direction_inbound(
154
+ self, populated_repo: MemoryIntegrationRepository
155
+ ) -> None:
156
+ """Test getting inbound integrations."""
157
+ integrations = await populated_repo.get_by_direction(Direction.INBOUND)
158
+ assert len(integrations) == 2
159
+ assert all(i.direction == Direction.INBOUND for i in integrations)
160
+
161
+ @pytest.mark.asyncio
162
+ async def test_get_by_direction_outbound(
163
+ self, populated_repo: MemoryIntegrationRepository
164
+ ) -> None:
165
+ """Test getting outbound integrations."""
166
+ integrations = await populated_repo.get_by_direction(Direction.OUTBOUND)
167
+ assert len(integrations) == 2
168
+ assert all(i.direction == Direction.OUTBOUND for i in integrations)
169
+
170
+ @pytest.mark.asyncio
171
+ async def test_get_by_direction_bidirectional(
172
+ self, populated_repo: MemoryIntegrationRepository
173
+ ) -> None:
174
+ """Test getting bidirectional integrations."""
175
+ integrations = await populated_repo.get_by_direction(Direction.BIDIRECTIONAL)
176
+ assert len(integrations) == 1
177
+ assert integrations[0].slug == "data-sync"
178
+
179
+ @pytest.mark.asyncio
180
+ async def test_get_by_module(
181
+ self, populated_repo: MemoryIntegrationRepository
182
+ ) -> None:
183
+ """Test getting integration by module name."""
184
+ integration = await populated_repo.get_by_module("pilot_data")
185
+ assert integration is not None
186
+ assert integration.slug == "pilot-data"
187
+
188
+ @pytest.mark.asyncio
189
+ async def test_get_by_module_not_found(
190
+ self, populated_repo: MemoryIntegrationRepository
191
+ ) -> None:
192
+ """Test getting integration by nonexistent module."""
193
+ integration = await populated_repo.get_by_module("nonexistent")
194
+ assert integration is None
195
+
196
+ @pytest.mark.asyncio
197
+ async def test_get_by_name(
198
+ self, populated_repo: MemoryIntegrationRepository
199
+ ) -> None:
200
+ """Test getting integration by name."""
201
+ integration = await populated_repo.get_by_name("Pilot Data Collection")
202
+ assert integration is not None
203
+ assert integration.slug == "pilot-data"
204
+
205
+ @pytest.mark.asyncio
206
+ async def test_get_by_name_case_insensitive(
207
+ self, populated_repo: MemoryIntegrationRepository
208
+ ) -> None:
209
+ """Test name matching is case-insensitive."""
210
+ integration = await populated_repo.get_by_name("pilot data collection")
211
+ assert integration is not None
212
+ assert integration.slug == "pilot-data"
213
+
214
+ @pytest.mark.asyncio
215
+ async def test_get_by_name_not_found(
216
+ self, populated_repo: MemoryIntegrationRepository
217
+ ) -> None:
218
+ """Test getting integration by nonexistent name."""
219
+ integration = await populated_repo.get_by_name("Nonexistent Integration")
220
+ assert integration is None
221
+
222
+ @pytest.mark.asyncio
223
+ async def test_get_all_directions(
224
+ self, populated_repo: MemoryIntegrationRepository
225
+ ) -> None:
226
+ """Test getting all unique directions."""
227
+ directions = await populated_repo.get_all_directions()
228
+ assert directions == {
229
+ Direction.INBOUND,
230
+ Direction.OUTBOUND,
231
+ Direction.BIDIRECTIONAL,
232
+ }
233
+
234
+ @pytest.mark.asyncio
235
+ async def test_get_with_dependencies(
236
+ self, populated_repo: MemoryIntegrationRepository
237
+ ) -> None:
238
+ """Test getting integrations with dependencies."""
239
+ integrations = await populated_repo.get_with_dependencies()
240
+ assert len(integrations) == 3
241
+ slugs = {i.slug for i in integrations}
242
+ assert slugs == {"pilot-data", "analytics-export", "data-sync"}
243
+
244
+ @pytest.mark.asyncio
245
+ async def test_get_by_dependency(
246
+ self, populated_repo: MemoryIntegrationRepository
247
+ ) -> None:
248
+ """Test getting integrations by dependency name."""
249
+ integrations = await populated_repo.get_by_dependency("AWS S3")
250
+ assert len(integrations) == 2
251
+ slugs = {i.slug for i in integrations}
252
+ assert slugs == {"analytics-export", "data-sync"}
253
+
254
+ @pytest.mark.asyncio
255
+ async def test_get_by_dependency_case_insensitive(
256
+ self, populated_repo: MemoryIntegrationRepository
257
+ ) -> None:
258
+ """Test dependency matching is case-insensitive."""
259
+ integrations = await populated_repo.get_by_dependency("aws s3")
260
+ assert len(integrations) == 2
261
+
262
+ @pytest.mark.asyncio
263
+ async def test_get_by_dependency_not_found(
264
+ self, populated_repo: MemoryIntegrationRepository
265
+ ) -> None:
266
+ """Test getting integrations by nonexistent dependency."""
267
+ integrations = await populated_repo.get_by_dependency("Unknown Service")
268
+ assert len(integrations) == 0
@@ -0,0 +1,294 @@
1
+ """Tests for MemoryJourneyRepository."""
2
+
3
+ import pytest
4
+ import pytest_asyncio
5
+
6
+ from julee.docs.sphinx_hcd.domain.models.journey import Journey, JourneyStep
7
+ from julee.docs.sphinx_hcd.repositories.memory.journey import MemoryJourneyRepository
8
+
9
+
10
+ def create_journey(
11
+ slug: str = "test-journey",
12
+ persona: str = "User",
13
+ docname: str = "journeys/test",
14
+ depends_on: list[str] | None = None,
15
+ steps: list[JourneyStep] | None = None,
16
+ ) -> Journey:
17
+ """Helper to create test journeys."""
18
+ return Journey(
19
+ slug=slug,
20
+ persona=persona,
21
+ docname=docname,
22
+ depends_on=depends_on or [],
23
+ steps=steps or [],
24
+ )
25
+
26
+
27
+ class TestMemoryJourneyRepositoryBasicOperations:
28
+ """Test basic CRUD operations."""
29
+
30
+ @pytest.fixture
31
+ def repo(self) -> MemoryJourneyRepository:
32
+ """Create a fresh repository."""
33
+ return MemoryJourneyRepository()
34
+
35
+ @pytest.mark.asyncio
36
+ async def test_save_and_get(self, repo: MemoryJourneyRepository) -> None:
37
+ """Test saving and retrieving a journey."""
38
+ journey = create_journey(slug="build-vocabulary")
39
+ await repo.save(journey)
40
+
41
+ retrieved = await repo.get("build-vocabulary")
42
+ assert retrieved is not None
43
+ assert retrieved.slug == "build-vocabulary"
44
+
45
+ @pytest.mark.asyncio
46
+ async def test_get_nonexistent(self, repo: MemoryJourneyRepository) -> None:
47
+ """Test getting a nonexistent journey returns None."""
48
+ result = await repo.get("nonexistent")
49
+ assert result is None
50
+
51
+ @pytest.mark.asyncio
52
+ async def test_list_all(self, repo: MemoryJourneyRepository) -> None:
53
+ """Test listing all journeys."""
54
+ await repo.save(create_journey(slug="journey-1"))
55
+ await repo.save(create_journey(slug="journey-2"))
56
+ await repo.save(create_journey(slug="journey-3"))
57
+
58
+ all_journeys = await repo.list_all()
59
+ assert len(all_journeys) == 3
60
+ slugs = {j.slug for j in all_journeys}
61
+ assert slugs == {"journey-1", "journey-2", "journey-3"}
62
+
63
+ @pytest.mark.asyncio
64
+ async def test_delete(self, repo: MemoryJourneyRepository) -> None:
65
+ """Test deleting a journey."""
66
+ await repo.save(create_journey(slug="to-delete"))
67
+ assert await repo.get("to-delete") is not None
68
+
69
+ result = await repo.delete("to-delete")
70
+ assert result is True
71
+ assert await repo.get("to-delete") is None
72
+
73
+ @pytest.mark.asyncio
74
+ async def test_delete_nonexistent(self, repo: MemoryJourneyRepository) -> None:
75
+ """Test deleting a nonexistent journey."""
76
+ result = await repo.delete("nonexistent")
77
+ assert result is False
78
+
79
+ @pytest.mark.asyncio
80
+ async def test_clear(self, repo: MemoryJourneyRepository) -> None:
81
+ """Test clearing all journeys."""
82
+ await repo.save(create_journey(slug="journey-1"))
83
+ await repo.save(create_journey(slug="journey-2"))
84
+ assert len(await repo.list_all()) == 2
85
+
86
+ await repo.clear()
87
+ assert len(await repo.list_all()) == 0
88
+
89
+
90
+ class TestMemoryJourneyRepositoryQueries:
91
+ """Test journey-specific query methods."""
92
+
93
+ @pytest.fixture
94
+ def repo(self) -> MemoryJourneyRepository:
95
+ """Create a repository."""
96
+ return MemoryJourneyRepository()
97
+
98
+ @pytest_asyncio.fixture
99
+ async def populated_repo(
100
+ self, repo: MemoryJourneyRepository
101
+ ) -> MemoryJourneyRepository:
102
+ """Create a repository with sample journeys."""
103
+ journeys = [
104
+ create_journey(
105
+ slug="build-vocabulary",
106
+ persona="Knowledge Curator",
107
+ docname="journeys/build-vocabulary",
108
+ depends_on=["operate-pipelines"],
109
+ steps=[
110
+ JourneyStep.story("Upload Document"),
111
+ JourneyStep.epic("vocabulary-management"),
112
+ ],
113
+ ),
114
+ create_journey(
115
+ slug="operate-pipelines",
116
+ persona="Knowledge Curator",
117
+ docname="journeys/operate-pipelines",
118
+ steps=[
119
+ JourneyStep.story("Configure Pipeline"),
120
+ ],
121
+ ),
122
+ create_journey(
123
+ slug="analyze-data",
124
+ persona="Analyst",
125
+ docname="journeys/analyze-data",
126
+ depends_on=["build-vocabulary", "operate-pipelines"],
127
+ steps=[
128
+ JourneyStep.story("Run Analysis"),
129
+ JourneyStep.epic("vocabulary-management"),
130
+ ],
131
+ ),
132
+ create_journey(
133
+ slug="review-results",
134
+ persona="Analyst",
135
+ docname="journeys/review-results",
136
+ ),
137
+ create_journey(
138
+ slug="admin-setup",
139
+ persona="Administrator",
140
+ docname="journeys/admin-setup",
141
+ ),
142
+ ]
143
+ for journey in journeys:
144
+ await repo.save(journey)
145
+ return repo
146
+
147
+ @pytest.mark.asyncio
148
+ async def test_get_by_persona(
149
+ self, populated_repo: MemoryJourneyRepository
150
+ ) -> None:
151
+ """Test getting journeys by persona."""
152
+ journeys = await populated_repo.get_by_persona("Knowledge Curator")
153
+ assert len(journeys) == 2
154
+ slugs = {j.slug for j in journeys}
155
+ assert slugs == {"build-vocabulary", "operate-pipelines"}
156
+
157
+ @pytest.mark.asyncio
158
+ async def test_get_by_persona_case_insensitive(
159
+ self, populated_repo: MemoryJourneyRepository
160
+ ) -> None:
161
+ """Test persona matching is case-insensitive."""
162
+ journeys = await populated_repo.get_by_persona("knowledge curator")
163
+ assert len(journeys) == 2
164
+
165
+ @pytest.mark.asyncio
166
+ async def test_get_by_persona_no_results(
167
+ self, populated_repo: MemoryJourneyRepository
168
+ ) -> None:
169
+ """Test getting journeys for persona with none."""
170
+ journeys = await populated_repo.get_by_persona("Unknown Persona")
171
+ assert len(journeys) == 0
172
+
173
+ @pytest.mark.asyncio
174
+ async def test_get_by_docname(
175
+ self, populated_repo: MemoryJourneyRepository
176
+ ) -> None:
177
+ """Test getting journeys by document name."""
178
+ journeys = await populated_repo.get_by_docname("journeys/build-vocabulary")
179
+ assert len(journeys) == 1
180
+ assert journeys[0].slug == "build-vocabulary"
181
+
182
+ @pytest.mark.asyncio
183
+ async def test_get_by_docname_no_results(
184
+ self, populated_repo: MemoryJourneyRepository
185
+ ) -> None:
186
+ """Test getting journeys for unknown document."""
187
+ journeys = await populated_repo.get_by_docname("unknown/document")
188
+ assert len(journeys) == 0
189
+
190
+ @pytest.mark.asyncio
191
+ async def test_clear_by_docname(
192
+ self, populated_repo: MemoryJourneyRepository
193
+ ) -> None:
194
+ """Test clearing journeys by document name."""
195
+ count = await populated_repo.clear_by_docname("journeys/build-vocabulary")
196
+ assert count == 1
197
+ assert await populated_repo.get("build-vocabulary") is None
198
+ # Other journeys should remain
199
+ assert len(await populated_repo.list_all()) == 4
200
+
201
+ @pytest.mark.asyncio
202
+ async def test_clear_by_docname_none_found(
203
+ self, populated_repo: MemoryJourneyRepository
204
+ ) -> None:
205
+ """Test clearing non-existent document returns 0."""
206
+ count = await populated_repo.clear_by_docname("unknown/document")
207
+ assert count == 0
208
+
209
+ @pytest.mark.asyncio
210
+ async def test_get_dependents(
211
+ self, populated_repo: MemoryJourneyRepository
212
+ ) -> None:
213
+ """Test getting journeys that depend on a journey."""
214
+ dependents = await populated_repo.get_dependents("operate-pipelines")
215
+ assert len(dependents) == 2
216
+ slugs = {j.slug for j in dependents}
217
+ assert slugs == {"build-vocabulary", "analyze-data"}
218
+
219
+ @pytest.mark.asyncio
220
+ async def test_get_dependents_none(
221
+ self, populated_repo: MemoryJourneyRepository
222
+ ) -> None:
223
+ """Test getting dependents for journey with none."""
224
+ dependents = await populated_repo.get_dependents("admin-setup")
225
+ assert len(dependents) == 0
226
+
227
+ @pytest.mark.asyncio
228
+ async def test_get_dependencies(
229
+ self, populated_repo: MemoryJourneyRepository
230
+ ) -> None:
231
+ """Test getting journeys that a journey depends on."""
232
+ deps = await populated_repo.get_dependencies("analyze-data")
233
+ assert len(deps) == 2
234
+ slugs = {j.slug for j in deps}
235
+ assert slugs == {"build-vocabulary", "operate-pipelines"}
236
+
237
+ @pytest.mark.asyncio
238
+ async def test_get_dependencies_nonexistent_journey(
239
+ self, populated_repo: MemoryJourneyRepository
240
+ ) -> None:
241
+ """Test getting dependencies for nonexistent journey."""
242
+ deps = await populated_repo.get_dependencies("nonexistent")
243
+ assert len(deps) == 0
244
+
245
+ @pytest.mark.asyncio
246
+ async def test_get_all_personas(
247
+ self, populated_repo: MemoryJourneyRepository
248
+ ) -> None:
249
+ """Test getting all unique personas."""
250
+ personas = await populated_repo.get_all_personas()
251
+ assert personas == {"knowledge curator", "analyst", "administrator"}
252
+
253
+ @pytest.mark.asyncio
254
+ async def test_get_with_story_ref(
255
+ self, populated_repo: MemoryJourneyRepository
256
+ ) -> None:
257
+ """Test getting journeys with a story reference."""
258
+ journeys = await populated_repo.get_with_story_ref("Upload Document")
259
+ assert len(journeys) == 1
260
+ assert journeys[0].slug == "build-vocabulary"
261
+
262
+ @pytest.mark.asyncio
263
+ async def test_get_with_story_ref_case_insensitive(
264
+ self, populated_repo: MemoryJourneyRepository
265
+ ) -> None:
266
+ """Test story ref matching is case-insensitive."""
267
+ journeys = await populated_repo.get_with_story_ref("upload document")
268
+ assert len(journeys) == 1
269
+
270
+ @pytest.mark.asyncio
271
+ async def test_get_with_story_ref_none(
272
+ self, populated_repo: MemoryJourneyRepository
273
+ ) -> None:
274
+ """Test getting journeys with nonexistent story."""
275
+ journeys = await populated_repo.get_with_story_ref("Unknown Story")
276
+ assert len(journeys) == 0
277
+
278
+ @pytest.mark.asyncio
279
+ async def test_get_with_epic_ref(
280
+ self, populated_repo: MemoryJourneyRepository
281
+ ) -> None:
282
+ """Test getting journeys with an epic reference."""
283
+ journeys = await populated_repo.get_with_epic_ref("vocabulary-management")
284
+ assert len(journeys) == 2
285
+ slugs = {j.slug for j in journeys}
286
+ assert slugs == {"build-vocabulary", "analyze-data"}
287
+
288
+ @pytest.mark.asyncio
289
+ async def test_get_with_epic_ref_none(
290
+ self, populated_repo: MemoryJourneyRepository
291
+ ) -> None:
292
+ """Test getting journeys with nonexistent epic."""
293
+ journeys = await populated_repo.get_with_epic_ref("unknown-epic")
294
+ assert len(journeys) == 0