julee 0.1.5__py3-none-any.whl → 0.1.7__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.
- julee/__init__.py +1 -1
- julee/contrib/polling/apps/worker/pipelines.py +3 -1
- julee/contrib/polling/tests/unit/apps/worker/test_pipelines.py +3 -0
- julee/docs/sphinx_hcd/__init__.py +146 -13
- julee/docs/sphinx_hcd/domain/__init__.py +5 -0
- julee/docs/sphinx_hcd/domain/models/__init__.py +32 -0
- julee/docs/sphinx_hcd/domain/models/accelerator.py +152 -0
- julee/docs/sphinx_hcd/domain/models/app.py +151 -0
- julee/docs/sphinx_hcd/domain/models/code_info.py +121 -0
- julee/docs/sphinx_hcd/domain/models/epic.py +79 -0
- julee/docs/sphinx_hcd/domain/models/integration.py +230 -0
- julee/docs/sphinx_hcd/domain/models/journey.py +222 -0
- julee/docs/sphinx_hcd/domain/models/persona.py +106 -0
- julee/docs/sphinx_hcd/domain/models/story.py +128 -0
- julee/docs/sphinx_hcd/domain/repositories/__init__.py +25 -0
- julee/docs/sphinx_hcd/domain/repositories/accelerator.py +98 -0
- julee/docs/sphinx_hcd/domain/repositories/app.py +57 -0
- julee/docs/sphinx_hcd/domain/repositories/base.py +89 -0
- julee/docs/sphinx_hcd/domain/repositories/code_info.py +69 -0
- julee/docs/sphinx_hcd/domain/repositories/epic.py +62 -0
- julee/docs/sphinx_hcd/domain/repositories/integration.py +79 -0
- julee/docs/sphinx_hcd/domain/repositories/journey.py +106 -0
- julee/docs/sphinx_hcd/domain/repositories/story.py +68 -0
- julee/docs/sphinx_hcd/domain/use_cases/__init__.py +64 -0
- julee/docs/sphinx_hcd/domain/use_cases/derive_personas.py +166 -0
- julee/docs/sphinx_hcd/domain/use_cases/resolve_accelerator_references.py +236 -0
- julee/docs/sphinx_hcd/domain/use_cases/resolve_app_references.py +144 -0
- julee/docs/sphinx_hcd/domain/use_cases/resolve_story_references.py +121 -0
- julee/docs/sphinx_hcd/parsers/__init__.py +48 -0
- julee/docs/sphinx_hcd/parsers/ast.py +150 -0
- julee/docs/sphinx_hcd/parsers/gherkin.py +155 -0
- julee/docs/sphinx_hcd/parsers/yaml.py +184 -0
- julee/docs/sphinx_hcd/repositories/__init__.py +4 -0
- julee/docs/sphinx_hcd/repositories/memory/__init__.py +25 -0
- julee/docs/sphinx_hcd/repositories/memory/accelerator.py +86 -0
- julee/docs/sphinx_hcd/repositories/memory/app.py +45 -0
- julee/docs/sphinx_hcd/repositories/memory/base.py +106 -0
- julee/docs/sphinx_hcd/repositories/memory/code_info.py +59 -0
- julee/docs/sphinx_hcd/repositories/memory/epic.py +54 -0
- julee/docs/sphinx_hcd/repositories/memory/integration.py +70 -0
- julee/docs/sphinx_hcd/repositories/memory/journey.py +96 -0
- julee/docs/sphinx_hcd/repositories/memory/story.py +63 -0
- julee/docs/sphinx_hcd/sphinx/__init__.py +28 -0
- julee/docs/sphinx_hcd/sphinx/adapters.py +116 -0
- julee/docs/sphinx_hcd/sphinx/context.py +163 -0
- julee/docs/sphinx_hcd/sphinx/directives/__init__.py +160 -0
- julee/docs/sphinx_hcd/sphinx/directives/accelerator.py +576 -0
- julee/docs/sphinx_hcd/sphinx/directives/app.py +349 -0
- julee/docs/sphinx_hcd/sphinx/directives/base.py +211 -0
- julee/docs/sphinx_hcd/sphinx/directives/epic.py +434 -0
- julee/docs/sphinx_hcd/sphinx/directives/integration.py +220 -0
- julee/docs/sphinx_hcd/sphinx/directives/journey.py +642 -0
- julee/docs/sphinx_hcd/sphinx/directives/persona.py +345 -0
- julee/docs/sphinx_hcd/sphinx/directives/story.py +575 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/__init__.py +16 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/builder_inited.py +31 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/doctree_read.py +27 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/doctree_resolved.py +43 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/env_purge_doc.py +42 -0
- julee/docs/sphinx_hcd/sphinx/initialization.py +139 -0
- julee/docs/sphinx_hcd/tests/__init__.py +9 -0
- julee/docs/sphinx_hcd/tests/conftest.py +6 -0
- julee/docs/sphinx_hcd/tests/domain/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/domain/models/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_accelerator.py +266 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_app.py +258 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_code_info.py +231 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_epic.py +163 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_integration.py +327 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_journey.py +249 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_persona.py +172 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_story.py +216 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/test_derive_personas.py +314 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_accelerator_references.py +476 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_app_references.py +265 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_story_references.py +229 -0
- julee/docs/sphinx_hcd/tests/integration/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/parsers/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/parsers/test_ast.py +298 -0
- julee/docs/sphinx_hcd/tests/parsers/test_gherkin.py +282 -0
- julee/docs/sphinx_hcd/tests/parsers/test_yaml.py +496 -0
- julee/docs/sphinx_hcd/tests/repositories/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/repositories/test_accelerator.py +298 -0
- julee/docs/sphinx_hcd/tests/repositories/test_app.py +218 -0
- julee/docs/sphinx_hcd/tests/repositories/test_base.py +151 -0
- julee/docs/sphinx_hcd/tests/repositories/test_code_info.py +253 -0
- julee/docs/sphinx_hcd/tests/repositories/test_epic.py +237 -0
- julee/docs/sphinx_hcd/tests/repositories/test_integration.py +268 -0
- julee/docs/sphinx_hcd/tests/repositories/test_journey.py +294 -0
- julee/docs/sphinx_hcd/tests/repositories/test_story.py +236 -0
- julee/docs/sphinx_hcd/tests/sphinx/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/sphinx/directives/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/sphinx/directives/test_base.py +160 -0
- julee/docs/sphinx_hcd/tests/sphinx/test_adapters.py +176 -0
- julee/docs/sphinx_hcd/tests/sphinx/test_context.py +257 -0
- {julee-0.1.5.dist-info → julee-0.1.7.dist-info}/METADATA +2 -1
- {julee-0.1.5.dist-info → julee-0.1.7.dist-info}/RECORD +101 -16
- julee/docs/sphinx_hcd/accelerators.py +0 -1175
- julee/docs/sphinx_hcd/apps.py +0 -518
- julee/docs/sphinx_hcd/epics.py +0 -453
- julee/docs/sphinx_hcd/integrations.py +0 -310
- julee/docs/sphinx_hcd/journeys.py +0 -797
- julee/docs/sphinx_hcd/personas.py +0 -457
- julee/docs/sphinx_hcd/stories.py +0 -960
- {julee-0.1.5.dist-info → julee-0.1.7.dist-info}/WHEEL +0 -0
- {julee-0.1.5.dist-info → julee-0.1.7.dist-info}/licenses/LICENSE +0 -0
- {julee-0.1.5.dist-info → julee-0.1.7.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
|