julee 0.1.0__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 +3 -0
- julee/api/__init__.py +20 -0
- julee/api/app.py +180 -0
- julee/api/dependencies.py +257 -0
- julee/api/requests.py +175 -0
- julee/api/responses.py +43 -0
- julee/api/routers/__init__.py +43 -0
- julee/api/routers/assembly_specifications.py +212 -0
- julee/api/routers/documents.py +182 -0
- julee/api/routers/knowledge_service_configs.py +79 -0
- julee/api/routers/knowledge_service_queries.py +293 -0
- julee/api/routers/system.py +137 -0
- julee/api/routers/workflows.py +234 -0
- julee/api/services/__init__.py +20 -0
- julee/api/services/system_initialization.py +214 -0
- julee/api/tests/__init__.py +14 -0
- julee/api/tests/routers/__init__.py +17 -0
- julee/api/tests/routers/test_assembly_specifications.py +749 -0
- julee/api/tests/routers/test_documents.py +301 -0
- julee/api/tests/routers/test_knowledge_service_configs.py +234 -0
- julee/api/tests/routers/test_knowledge_service_queries.py +738 -0
- julee/api/tests/routers/test_system.py +179 -0
- julee/api/tests/routers/test_workflows.py +393 -0
- julee/api/tests/test_app.py +285 -0
- julee/api/tests/test_dependencies.py +245 -0
- julee/api/tests/test_requests.py +250 -0
- julee/domain/__init__.py +22 -0
- julee/domain/models/__init__.py +49 -0
- julee/domain/models/assembly/__init__.py +17 -0
- julee/domain/models/assembly/assembly.py +103 -0
- julee/domain/models/assembly/tests/__init__.py +0 -0
- julee/domain/models/assembly/tests/factories.py +37 -0
- julee/domain/models/assembly/tests/test_assembly.py +430 -0
- julee/domain/models/assembly_specification/__init__.py +24 -0
- julee/domain/models/assembly_specification/assembly_specification.py +172 -0
- julee/domain/models/assembly_specification/knowledge_service_query.py +123 -0
- julee/domain/models/assembly_specification/tests/__init__.py +0 -0
- julee/domain/models/assembly_specification/tests/factories.py +78 -0
- julee/domain/models/assembly_specification/tests/test_assembly_specification.py +490 -0
- julee/domain/models/assembly_specification/tests/test_knowledge_service_query.py +310 -0
- julee/domain/models/custom_fields/__init__.py +0 -0
- julee/domain/models/custom_fields/content_stream.py +68 -0
- julee/domain/models/custom_fields/tests/__init__.py +0 -0
- julee/domain/models/custom_fields/tests/test_custom_fields.py +53 -0
- julee/domain/models/document/__init__.py +17 -0
- julee/domain/models/document/document.py +150 -0
- julee/domain/models/document/tests/__init__.py +0 -0
- julee/domain/models/document/tests/factories.py +76 -0
- julee/domain/models/document/tests/test_document.py +297 -0
- julee/domain/models/knowledge_service_config/__init__.py +17 -0
- julee/domain/models/knowledge_service_config/knowledge_service_config.py +86 -0
- julee/domain/models/policy/__init__.py +15 -0
- julee/domain/models/policy/document_policy_validation.py +220 -0
- julee/domain/models/policy/policy.py +203 -0
- julee/domain/models/policy/tests/__init__.py +0 -0
- julee/domain/models/policy/tests/factories.py +47 -0
- julee/domain/models/policy/tests/test_document_policy_validation.py +420 -0
- julee/domain/models/policy/tests/test_policy.py +546 -0
- julee/domain/repositories/__init__.py +27 -0
- julee/domain/repositories/assembly.py +45 -0
- julee/domain/repositories/assembly_specification.py +52 -0
- julee/domain/repositories/base.py +146 -0
- julee/domain/repositories/document.py +49 -0
- julee/domain/repositories/document_policy_validation.py +52 -0
- julee/domain/repositories/knowledge_service_config.py +54 -0
- julee/domain/repositories/knowledge_service_query.py +44 -0
- julee/domain/repositories/policy.py +49 -0
- julee/domain/use_cases/__init__.py +17 -0
- julee/domain/use_cases/decorators.py +107 -0
- julee/domain/use_cases/extract_assemble_data.py +649 -0
- julee/domain/use_cases/initialize_system_data.py +842 -0
- julee/domain/use_cases/tests/__init__.py +7 -0
- julee/domain/use_cases/tests/test_extract_assemble_data.py +548 -0
- julee/domain/use_cases/tests/test_initialize_system_data.py +455 -0
- julee/domain/use_cases/tests/test_validate_document.py +1228 -0
- julee/domain/use_cases/validate_document.py +736 -0
- julee/fixtures/assembly_specifications.yaml +70 -0
- julee/fixtures/documents.yaml +178 -0
- julee/fixtures/knowledge_service_configs.yaml +37 -0
- julee/fixtures/knowledge_service_queries.yaml +27 -0
- julee/repositories/__init__.py +17 -0
- julee/repositories/memory/__init__.py +31 -0
- julee/repositories/memory/assembly.py +84 -0
- julee/repositories/memory/assembly_specification.py +125 -0
- julee/repositories/memory/base.py +227 -0
- julee/repositories/memory/document.py +149 -0
- julee/repositories/memory/document_policy_validation.py +104 -0
- julee/repositories/memory/knowledge_service_config.py +123 -0
- julee/repositories/memory/knowledge_service_query.py +120 -0
- julee/repositories/memory/policy.py +87 -0
- julee/repositories/memory/tests/__init__.py +0 -0
- julee/repositories/memory/tests/test_document.py +212 -0
- julee/repositories/memory/tests/test_document_policy_validation.py +161 -0
- julee/repositories/memory/tests/test_policy.py +443 -0
- julee/repositories/minio/__init__.py +31 -0
- julee/repositories/minio/assembly.py +103 -0
- julee/repositories/minio/assembly_specification.py +170 -0
- julee/repositories/minio/client.py +570 -0
- julee/repositories/minio/document.py +530 -0
- julee/repositories/minio/document_policy_validation.py +120 -0
- julee/repositories/minio/knowledge_service_config.py +187 -0
- julee/repositories/minio/knowledge_service_query.py +211 -0
- julee/repositories/minio/policy.py +106 -0
- julee/repositories/minio/tests/__init__.py +0 -0
- julee/repositories/minio/tests/fake_client.py +213 -0
- julee/repositories/minio/tests/test_assembly.py +374 -0
- julee/repositories/minio/tests/test_assembly_specification.py +391 -0
- julee/repositories/minio/tests/test_client_protocol.py +57 -0
- julee/repositories/minio/tests/test_document.py +591 -0
- julee/repositories/minio/tests/test_document_policy_validation.py +192 -0
- julee/repositories/minio/tests/test_knowledge_service_config.py +374 -0
- julee/repositories/minio/tests/test_knowledge_service_query.py +438 -0
- julee/repositories/minio/tests/test_policy.py +559 -0
- julee/repositories/temporal/__init__.py +38 -0
- julee/repositories/temporal/activities.py +114 -0
- julee/repositories/temporal/activity_names.py +34 -0
- julee/repositories/temporal/proxies.py +159 -0
- julee/services/__init__.py +18 -0
- julee/services/knowledge_service/__init__.py +48 -0
- julee/services/knowledge_service/anthropic/__init__.py +12 -0
- julee/services/knowledge_service/anthropic/knowledge_service.py +331 -0
- julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +318 -0
- julee/services/knowledge_service/factory.py +138 -0
- julee/services/knowledge_service/knowledge_service.py +160 -0
- julee/services/knowledge_service/memory/__init__.py +13 -0
- julee/services/knowledge_service/memory/knowledge_service.py +278 -0
- julee/services/knowledge_service/memory/test_knowledge_service.py +345 -0
- julee/services/knowledge_service/test_factory.py +112 -0
- julee/services/temporal/__init__.py +38 -0
- julee/services/temporal/activities.py +86 -0
- julee/services/temporal/activity_names.py +22 -0
- julee/services/temporal/proxies.py +41 -0
- julee/util/__init__.py +0 -0
- julee/util/domain.py +119 -0
- julee/util/repos/__init__.py +0 -0
- julee/util/repos/minio/__init__.py +0 -0
- julee/util/repos/minio/file_storage.py +213 -0
- julee/util/repos/temporal/__init__.py +11 -0
- julee/util/repos/temporal/client_proxies/file_storage.py +68 -0
- julee/util/repos/temporal/data_converter.py +123 -0
- julee/util/repos/temporal/minio_file_storage.py +12 -0
- julee/util/repos/temporal/proxies/__init__.py +0 -0
- julee/util/repos/temporal/proxies/file_storage.py +58 -0
- julee/util/repositories.py +55 -0
- julee/util/temporal/__init__.py +22 -0
- julee/util/temporal/activities.py +123 -0
- julee/util/temporal/decorators.py +473 -0
- julee/util/tests/__init__.py +1 -0
- julee/util/tests/test_decorators.py +770 -0
- julee/util/validation/__init__.py +29 -0
- julee/util/validation/repository.py +100 -0
- julee/util/validation/type_guards.py +369 -0
- julee/worker.py +211 -0
- julee/workflows/__init__.py +26 -0
- julee/workflows/extract_assemble.py +215 -0
- julee/workflows/validate_document.py +228 -0
- julee-0.1.0.dist-info/METADATA +195 -0
- julee-0.1.0.dist-info/RECORD +161 -0
- julee-0.1.0.dist-info/WHEEL +5 -0
- julee-0.1.0.dist-info/licenses/LICENSE +674 -0
- julee-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1228 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for ValidateDocumentUseCase.
|
|
3
|
+
|
|
4
|
+
This module provides tests for the validate document use case,
|
|
5
|
+
ensuring proper business logic execution and repository interaction patterns
|
|
6
|
+
following the Clean Architecture principles.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import io
|
|
10
|
+
import pytest
|
|
11
|
+
from unittest.mock import AsyncMock
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
from pydantic import ValidationError
|
|
14
|
+
|
|
15
|
+
from julee.domain.use_cases import ValidateDocumentUseCase
|
|
16
|
+
|
|
17
|
+
from julee.domain.models import (
|
|
18
|
+
Document,
|
|
19
|
+
DocumentStatus,
|
|
20
|
+
ContentStream,
|
|
21
|
+
KnowledgeServiceConfig,
|
|
22
|
+
KnowledgeServiceQuery,
|
|
23
|
+
)
|
|
24
|
+
from julee.domain.models.policy import (
|
|
25
|
+
Policy,
|
|
26
|
+
PolicyStatus,
|
|
27
|
+
DocumentPolicyValidation,
|
|
28
|
+
DocumentPolicyValidationStatus,
|
|
29
|
+
)
|
|
30
|
+
from julee.domain.models.knowledge_service_config import ServiceApi
|
|
31
|
+
from julee.repositories.memory import (
|
|
32
|
+
MemoryDocumentRepository,
|
|
33
|
+
MemoryKnowledgeServiceQueryRepository,
|
|
34
|
+
MemoryKnowledgeServiceConfigRepository,
|
|
35
|
+
MemoryPolicyRepository,
|
|
36
|
+
MemoryDocumentPolicyValidationRepository,
|
|
37
|
+
)
|
|
38
|
+
from julee.services.knowledge_service.memory import (
|
|
39
|
+
MemoryKnowledgeService,
|
|
40
|
+
)
|
|
41
|
+
from julee.services.knowledge_service import QueryResult
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TestValidateDocumentUseCase:
|
|
45
|
+
"""Test cases for ValidateDocumentUseCase business logic."""
|
|
46
|
+
|
|
47
|
+
@pytest.fixture
|
|
48
|
+
def document_repo(self) -> MemoryDocumentRepository:
|
|
49
|
+
"""Create a memory DocumentRepository for testing."""
|
|
50
|
+
return MemoryDocumentRepository()
|
|
51
|
+
|
|
52
|
+
@pytest.fixture
|
|
53
|
+
def knowledge_service_query_repo(
|
|
54
|
+
self,
|
|
55
|
+
) -> MemoryKnowledgeServiceQueryRepository:
|
|
56
|
+
"""Create a memory KnowledgeServiceQueryRepository for testing."""
|
|
57
|
+
return MemoryKnowledgeServiceQueryRepository()
|
|
58
|
+
|
|
59
|
+
@pytest.fixture
|
|
60
|
+
def knowledge_service_config_repo(
|
|
61
|
+
self,
|
|
62
|
+
) -> MemoryKnowledgeServiceConfigRepository:
|
|
63
|
+
"""Create a memory KnowledgeServiceConfigRepository for testing."""
|
|
64
|
+
return MemoryKnowledgeServiceConfigRepository()
|
|
65
|
+
|
|
66
|
+
@pytest.fixture
|
|
67
|
+
def policy_repo(self) -> MemoryPolicyRepository:
|
|
68
|
+
"""Create a memory PolicyRepository for testing."""
|
|
69
|
+
return MemoryPolicyRepository()
|
|
70
|
+
|
|
71
|
+
@pytest.fixture
|
|
72
|
+
def document_policy_validation_repo(
|
|
73
|
+
self,
|
|
74
|
+
) -> MemoryDocumentPolicyValidationRepository:
|
|
75
|
+
"""Create a memory DocumentPolicyValidationRepository for testing."""
|
|
76
|
+
return MemoryDocumentPolicyValidationRepository()
|
|
77
|
+
|
|
78
|
+
@pytest.fixture
|
|
79
|
+
def knowledge_service(self) -> MemoryKnowledgeService:
|
|
80
|
+
"""Create a memory KnowledgeService for testing."""
|
|
81
|
+
ks_config = KnowledgeServiceConfig(
|
|
82
|
+
knowledge_service_id="ks-test",
|
|
83
|
+
name="Test Knowledge Service",
|
|
84
|
+
description="Test service",
|
|
85
|
+
service_api=ServiceApi.ANTHROPIC,
|
|
86
|
+
created_at=datetime.now(timezone.utc),
|
|
87
|
+
updated_at=datetime.now(timezone.utc),
|
|
88
|
+
)
|
|
89
|
+
return MemoryKnowledgeService(ks_config)
|
|
90
|
+
|
|
91
|
+
@pytest.fixture
|
|
92
|
+
def use_case(
|
|
93
|
+
self,
|
|
94
|
+
document_repo: MemoryDocumentRepository,
|
|
95
|
+
knowledge_service_query_repo: MemoryKnowledgeServiceQueryRepository,
|
|
96
|
+
knowledge_service_config_repo: MemoryKnowledgeServiceConfigRepository,
|
|
97
|
+
policy_repo: MemoryPolicyRepository,
|
|
98
|
+
document_policy_validation_repo: MemoryDocumentPolicyValidationRepository,
|
|
99
|
+
knowledge_service: MemoryKnowledgeService,
|
|
100
|
+
) -> ValidateDocumentUseCase:
|
|
101
|
+
"""Create ValidateDocumentUseCase with memory repository
|
|
102
|
+
dependencies."""
|
|
103
|
+
return ValidateDocumentUseCase(
|
|
104
|
+
document_repo=document_repo,
|
|
105
|
+
knowledge_service_query_repo=knowledge_service_query_repo,
|
|
106
|
+
knowledge_service_config_repo=knowledge_service_config_repo,
|
|
107
|
+
policy_repo=policy_repo,
|
|
108
|
+
document_policy_validation_repo=document_policy_validation_repo,
|
|
109
|
+
knowledge_service=knowledge_service,
|
|
110
|
+
now_fn=lambda: datetime.now(timezone.utc),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
def _create_configured_use_case(
|
|
114
|
+
self,
|
|
115
|
+
document_repo: MemoryDocumentRepository,
|
|
116
|
+
knowledge_service_query_repo: MemoryKnowledgeServiceQueryRepository,
|
|
117
|
+
knowledge_service_config_repo: MemoryKnowledgeServiceConfigRepository,
|
|
118
|
+
policy_repo: MemoryPolicyRepository,
|
|
119
|
+
document_policy_validation_repo: MemoryDocumentPolicyValidationRepository,
|
|
120
|
+
memory_service: MemoryKnowledgeService,
|
|
121
|
+
) -> ValidateDocumentUseCase:
|
|
122
|
+
"""Helper to create ValidateDocumentUseCase with configured memory
|
|
123
|
+
service."""
|
|
124
|
+
return ValidateDocumentUseCase(
|
|
125
|
+
document_repo=document_repo,
|
|
126
|
+
knowledge_service_query_repo=knowledge_service_query_repo,
|
|
127
|
+
knowledge_service_config_repo=knowledge_service_config_repo,
|
|
128
|
+
policy_repo=policy_repo,
|
|
129
|
+
document_policy_validation_repo=document_policy_validation_repo,
|
|
130
|
+
knowledge_service=memory_service,
|
|
131
|
+
now_fn=lambda: datetime.now(timezone.utc),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
@pytest.mark.asyncio
|
|
135
|
+
async def test_validate_document_fails_without_document(
|
|
136
|
+
self, use_case: ValidateDocumentUseCase
|
|
137
|
+
) -> None:
|
|
138
|
+
"""Test that validate_document fails when document doesn't exist."""
|
|
139
|
+
# Arrange
|
|
140
|
+
document_id = "nonexistent-doc"
|
|
141
|
+
policy_id = "policy-123"
|
|
142
|
+
|
|
143
|
+
# Act & Assert
|
|
144
|
+
with pytest.raises(ValueError, match="Document not found"):
|
|
145
|
+
await use_case.validate_document(
|
|
146
|
+
document_id=document_id, policy_id=policy_id
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
@pytest.mark.asyncio
|
|
150
|
+
async def test_validate_document_fails_without_policy(
|
|
151
|
+
self,
|
|
152
|
+
use_case: ValidateDocumentUseCase,
|
|
153
|
+
document_repo: MemoryDocumentRepository,
|
|
154
|
+
) -> None:
|
|
155
|
+
"""Test that validate_document fails when policy doesn't exist."""
|
|
156
|
+
# Arrange - Create document but no policy
|
|
157
|
+
content_text = "Sample document for testing"
|
|
158
|
+
content_bytes = content_text.encode("utf-8")
|
|
159
|
+
document = Document(
|
|
160
|
+
document_id="doc-123",
|
|
161
|
+
original_filename="test_document.txt",
|
|
162
|
+
content_type="text/plain",
|
|
163
|
+
size_bytes=len(content_bytes),
|
|
164
|
+
content_multihash="test-hash-123",
|
|
165
|
+
status=DocumentStatus.CAPTURED,
|
|
166
|
+
content=ContentStream(io.BytesIO(content_bytes)),
|
|
167
|
+
created_at=datetime.now(timezone.utc),
|
|
168
|
+
updated_at=datetime.now(timezone.utc),
|
|
169
|
+
)
|
|
170
|
+
await document_repo.save(document)
|
|
171
|
+
|
|
172
|
+
document_id = "doc-123"
|
|
173
|
+
policy_id = "nonexistent-policy"
|
|
174
|
+
|
|
175
|
+
# Act & Assert
|
|
176
|
+
with pytest.raises(ValueError, match="Policy not found"):
|
|
177
|
+
await use_case.validate_document(
|
|
178
|
+
document_id=document_id, policy_id=policy_id
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
@pytest.mark.asyncio
|
|
182
|
+
async def test_validate_document_propagates_id_generation_error(
|
|
183
|
+
self,
|
|
184
|
+
use_case: ValidateDocumentUseCase,
|
|
185
|
+
document_policy_validation_repo: MemoryDocumentPolicyValidationRepository,
|
|
186
|
+
) -> None:
|
|
187
|
+
"""Test that ID generation errors are properly propagated."""
|
|
188
|
+
# Arrange
|
|
189
|
+
document_id = "doc-456"
|
|
190
|
+
policy_id = "policy-789"
|
|
191
|
+
expected_error = RuntimeError("ID generation failed")
|
|
192
|
+
|
|
193
|
+
# Mock the generate_id method to raise an error
|
|
194
|
+
document_policy_validation_repo.generate_id = AsyncMock( # type: ignore[method-assign]
|
|
195
|
+
side_effect=expected_error
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Act & Assert
|
|
199
|
+
with pytest.raises(RuntimeError, match="ID generation failed"):
|
|
200
|
+
await use_case.validate_document(
|
|
201
|
+
document_id=document_id, policy_id=policy_id
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
@pytest.mark.asyncio
|
|
205
|
+
async def test_validate_document_fails_when_query_not_found(
|
|
206
|
+
self,
|
|
207
|
+
use_case: ValidateDocumentUseCase,
|
|
208
|
+
document_repo: MemoryDocumentRepository,
|
|
209
|
+
policy_repo: MemoryPolicyRepository,
|
|
210
|
+
) -> None:
|
|
211
|
+
"""Test that validation fails when query is not found."""
|
|
212
|
+
# Arrange - Create document and policy with non-existent query
|
|
213
|
+
content_text = "Sample content"
|
|
214
|
+
content_bytes = content_text.encode("utf-8")
|
|
215
|
+
document = Document(
|
|
216
|
+
document_id="doc-123",
|
|
217
|
+
original_filename="test.txt",
|
|
218
|
+
content_type="text/plain",
|
|
219
|
+
size_bytes=len(content_bytes),
|
|
220
|
+
content_multihash="test-hash",
|
|
221
|
+
status=DocumentStatus.CAPTURED,
|
|
222
|
+
content=ContentStream(io.BytesIO(content_bytes)),
|
|
223
|
+
created_at=datetime.now(timezone.utc),
|
|
224
|
+
updated_at=datetime.now(timezone.utc),
|
|
225
|
+
)
|
|
226
|
+
await document_repo.save(document)
|
|
227
|
+
|
|
228
|
+
policy = Policy(
|
|
229
|
+
policy_id="policy-123",
|
|
230
|
+
title="Test Policy",
|
|
231
|
+
description="Policy with non-existent query",
|
|
232
|
+
status=PolicyStatus.ACTIVE,
|
|
233
|
+
validation_scores=[("nonexistent-query", 80)],
|
|
234
|
+
created_at=datetime.now(timezone.utc),
|
|
235
|
+
updated_at=datetime.now(timezone.utc),
|
|
236
|
+
)
|
|
237
|
+
await policy_repo.save(policy)
|
|
238
|
+
|
|
239
|
+
# Act & Assert
|
|
240
|
+
with pytest.raises(ValueError, match="Validation query not found"):
|
|
241
|
+
await use_case.validate_document(
|
|
242
|
+
document_id="doc-123", policy_id="policy-123"
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
@pytest.mark.asyncio
|
|
246
|
+
async def test_validate_document_fails_with_score_parse_error(
|
|
247
|
+
self,
|
|
248
|
+
use_case: ValidateDocumentUseCase,
|
|
249
|
+
document_repo: MemoryDocumentRepository,
|
|
250
|
+
policy_repo: MemoryPolicyRepository,
|
|
251
|
+
knowledge_service_query_repo: MemoryKnowledgeServiceQueryRepository,
|
|
252
|
+
knowledge_service_config_repo: MemoryKnowledgeServiceConfigRepository,
|
|
253
|
+
document_policy_validation_repo: MemoryDocumentPolicyValidationRepository,
|
|
254
|
+
) -> None:
|
|
255
|
+
"""Test that validation fails when score cannot be parsed."""
|
|
256
|
+
# Arrange - Create test document
|
|
257
|
+
content_text = "Sample document content"
|
|
258
|
+
content_bytes = content_text.encode("utf-8")
|
|
259
|
+
document = Document(
|
|
260
|
+
document_id="doc-123",
|
|
261
|
+
original_filename="test.txt",
|
|
262
|
+
content_type="text/plain",
|
|
263
|
+
size_bytes=len(content_bytes),
|
|
264
|
+
content_multihash="test-hash",
|
|
265
|
+
status=DocumentStatus.CAPTURED,
|
|
266
|
+
content=ContentStream(io.BytesIO(content_bytes)),
|
|
267
|
+
created_at=datetime.now(timezone.utc),
|
|
268
|
+
updated_at=datetime.now(timezone.utc),
|
|
269
|
+
)
|
|
270
|
+
await document_repo.save(document)
|
|
271
|
+
|
|
272
|
+
# Create policy
|
|
273
|
+
policy = Policy(
|
|
274
|
+
policy_id="policy-123",
|
|
275
|
+
title="Test Policy",
|
|
276
|
+
description="Policy for testing score parsing",
|
|
277
|
+
status=PolicyStatus.ACTIVE,
|
|
278
|
+
validation_scores=[("query-1", 80)],
|
|
279
|
+
created_at=datetime.now(timezone.utc),
|
|
280
|
+
updated_at=datetime.now(timezone.utc),
|
|
281
|
+
)
|
|
282
|
+
await policy_repo.save(policy)
|
|
283
|
+
|
|
284
|
+
# Create knowledge service config and query
|
|
285
|
+
ks_config = KnowledgeServiceConfig(
|
|
286
|
+
knowledge_service_id="ks-123",
|
|
287
|
+
name="Test Knowledge Service",
|
|
288
|
+
description="Test service",
|
|
289
|
+
service_api=ServiceApi.ANTHROPIC,
|
|
290
|
+
created_at=datetime.now(timezone.utc),
|
|
291
|
+
updated_at=datetime.now(timezone.utc),
|
|
292
|
+
)
|
|
293
|
+
await knowledge_service_config_repo.save(ks_config)
|
|
294
|
+
|
|
295
|
+
query = KnowledgeServiceQuery(
|
|
296
|
+
query_id="query-1",
|
|
297
|
+
name="Quality Check",
|
|
298
|
+
knowledge_service_id="ks-123",
|
|
299
|
+
prompt="Rate the quality of this document",
|
|
300
|
+
created_at=datetime.now(timezone.utc),
|
|
301
|
+
updated_at=datetime.now(timezone.utc),
|
|
302
|
+
)
|
|
303
|
+
await knowledge_service_query_repo.save(query)
|
|
304
|
+
|
|
305
|
+
# Create memory service that returns unparseable score
|
|
306
|
+
memory_service = MemoryKnowledgeService(ks_config)
|
|
307
|
+
memory_service.add_canned_query_result(
|
|
308
|
+
QueryResult(
|
|
309
|
+
query_id="result-1",
|
|
310
|
+
query_text="Rate the quality of this document",
|
|
311
|
+
result_data={"response": "not a number"}, # Invalid score format
|
|
312
|
+
execution_time_ms=100,
|
|
313
|
+
created_at=datetime.now(timezone.utc),
|
|
314
|
+
)
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# Create use case with configured memory service
|
|
318
|
+
configured_use_case = self._create_configured_use_case(
|
|
319
|
+
document_repo=document_repo,
|
|
320
|
+
knowledge_service_query_repo=knowledge_service_query_repo,
|
|
321
|
+
knowledge_service_config_repo=knowledge_service_config_repo,
|
|
322
|
+
policy_repo=policy_repo,
|
|
323
|
+
document_policy_validation_repo=document_policy_validation_repo,
|
|
324
|
+
memory_service=memory_service,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
# Act & Assert
|
|
328
|
+
with pytest.raises(
|
|
329
|
+
ValueError,
|
|
330
|
+
match="Failed to parse numeric score from response",
|
|
331
|
+
):
|
|
332
|
+
await configured_use_case.validate_document(
|
|
333
|
+
document_id="doc-123", policy_id="policy-123"
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
@pytest.mark.asyncio
|
|
337
|
+
async def test_full_validation_workflow_success_pass(
|
|
338
|
+
self,
|
|
339
|
+
use_case: ValidateDocumentUseCase,
|
|
340
|
+
document_repo: MemoryDocumentRepository,
|
|
341
|
+
policy_repo: MemoryPolicyRepository,
|
|
342
|
+
knowledge_service_query_repo: MemoryKnowledgeServiceQueryRepository,
|
|
343
|
+
knowledge_service_config_repo: MemoryKnowledgeServiceConfigRepository,
|
|
344
|
+
document_policy_validation_repo: MemoryDocumentPolicyValidationRepository,
|
|
345
|
+
) -> None:
|
|
346
|
+
"""Test complete validation workflow that passes validation."""
|
|
347
|
+
# Arrange - Create test document
|
|
348
|
+
content_text = "High quality document for testing validation"
|
|
349
|
+
content_bytes = content_text.encode("utf-8")
|
|
350
|
+
document = Document(
|
|
351
|
+
document_id="doc-123",
|
|
352
|
+
original_filename="test_document.txt",
|
|
353
|
+
content_type="text/plain",
|
|
354
|
+
size_bytes=len(content_bytes),
|
|
355
|
+
content_multihash="test-hash-123",
|
|
356
|
+
status=DocumentStatus.CAPTURED,
|
|
357
|
+
content=ContentStream(io.BytesIO(content_bytes)),
|
|
358
|
+
created_at=datetime.now(timezone.utc),
|
|
359
|
+
updated_at=datetime.now(timezone.utc),
|
|
360
|
+
)
|
|
361
|
+
await document_repo.save(document)
|
|
362
|
+
|
|
363
|
+
# Create policy with validation criteria
|
|
364
|
+
policy = Policy(
|
|
365
|
+
policy_id="policy-123",
|
|
366
|
+
title="Quality Policy",
|
|
367
|
+
description="Validates document quality",
|
|
368
|
+
status=PolicyStatus.ACTIVE,
|
|
369
|
+
validation_scores=[
|
|
370
|
+
("quality-query", 80),
|
|
371
|
+
("clarity-query", 70),
|
|
372
|
+
],
|
|
373
|
+
created_at=datetime.now(timezone.utc),
|
|
374
|
+
updated_at=datetime.now(timezone.utc),
|
|
375
|
+
)
|
|
376
|
+
await policy_repo.save(policy)
|
|
377
|
+
|
|
378
|
+
# Create knowledge service config
|
|
379
|
+
ks_config = KnowledgeServiceConfig(
|
|
380
|
+
knowledge_service_id="ks-123",
|
|
381
|
+
name="Test Knowledge Service",
|
|
382
|
+
description="Test service",
|
|
383
|
+
service_api=ServiceApi.ANTHROPIC,
|
|
384
|
+
created_at=datetime.now(timezone.utc),
|
|
385
|
+
updated_at=datetime.now(timezone.utc),
|
|
386
|
+
)
|
|
387
|
+
await knowledge_service_config_repo.save(ks_config)
|
|
388
|
+
|
|
389
|
+
# Create knowledge service queries
|
|
390
|
+
quality_query = KnowledgeServiceQuery(
|
|
391
|
+
query_id="quality-query",
|
|
392
|
+
name="Quality Check",
|
|
393
|
+
knowledge_service_id="ks-123",
|
|
394
|
+
prompt="Rate the quality of this document on a scale of 0-100",
|
|
395
|
+
query_metadata={"max_tokens": 10},
|
|
396
|
+
created_at=datetime.now(timezone.utc),
|
|
397
|
+
updated_at=datetime.now(timezone.utc),
|
|
398
|
+
)
|
|
399
|
+
clarity_query = KnowledgeServiceQuery(
|
|
400
|
+
query_id="clarity-query",
|
|
401
|
+
name="Clarity Check",
|
|
402
|
+
knowledge_service_id="ks-123",
|
|
403
|
+
prompt="Rate the clarity of this document on a scale of 0-100",
|
|
404
|
+
query_metadata={"max_tokens": 10},
|
|
405
|
+
created_at=datetime.now(timezone.utc),
|
|
406
|
+
updated_at=datetime.now(timezone.utc),
|
|
407
|
+
)
|
|
408
|
+
await knowledge_service_query_repo.save(quality_query)
|
|
409
|
+
await knowledge_service_query_repo.save(clarity_query)
|
|
410
|
+
|
|
411
|
+
# Create memory service with passing scores
|
|
412
|
+
memory_service = MemoryKnowledgeService(ks_config)
|
|
413
|
+
memory_service.add_canned_query_results(
|
|
414
|
+
[
|
|
415
|
+
QueryResult(
|
|
416
|
+
query_id="result-1",
|
|
417
|
+
query_text="Rate the quality of this document on a "
|
|
418
|
+
"scale of 0-100",
|
|
419
|
+
result_data={"response": "85"}, # Passes requirement of 80
|
|
420
|
+
execution_time_ms=100,
|
|
421
|
+
created_at=datetime.now(timezone.utc),
|
|
422
|
+
),
|
|
423
|
+
QueryResult(
|
|
424
|
+
query_id="result-2",
|
|
425
|
+
query_text="Rate the clarity of this document on a "
|
|
426
|
+
"scale of 0-100",
|
|
427
|
+
result_data={"response": "75"}, # Passes requirement of 70
|
|
428
|
+
execution_time_ms=150,
|
|
429
|
+
created_at=datetime.now(timezone.utc),
|
|
430
|
+
),
|
|
431
|
+
]
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
# Create use case with configured memory service
|
|
435
|
+
configured_use_case = self._create_configured_use_case(
|
|
436
|
+
document_repo=document_repo,
|
|
437
|
+
knowledge_service_query_repo=knowledge_service_query_repo,
|
|
438
|
+
knowledge_service_config_repo=knowledge_service_config_repo,
|
|
439
|
+
policy_repo=policy_repo,
|
|
440
|
+
document_policy_validation_repo=document_policy_validation_repo,
|
|
441
|
+
memory_service=memory_service,
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
# Act
|
|
445
|
+
result = await configured_use_case.validate_document(
|
|
446
|
+
document_id="doc-123", policy_id="policy-123"
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
# Assert
|
|
450
|
+
assert isinstance(result, DocumentPolicyValidation)
|
|
451
|
+
assert result.status == DocumentPolicyValidationStatus.PASSED
|
|
452
|
+
assert result.passed is True
|
|
453
|
+
assert result.validation_scores == [
|
|
454
|
+
("quality-query", 85),
|
|
455
|
+
("clarity-query", 75),
|
|
456
|
+
]
|
|
457
|
+
assert result.completed_at is not None
|
|
458
|
+
assert result.error_message is None
|
|
459
|
+
|
|
460
|
+
# Verify validation was saved to repository
|
|
461
|
+
saved_validation = await document_policy_validation_repo.get(
|
|
462
|
+
result.validation_id
|
|
463
|
+
)
|
|
464
|
+
assert saved_validation is not None
|
|
465
|
+
assert saved_validation.status == DocumentPolicyValidationStatus.PASSED
|
|
466
|
+
assert saved_validation.passed is True
|
|
467
|
+
|
|
468
|
+
@pytest.mark.asyncio
|
|
469
|
+
async def test_full_validation_workflow_success_fail(
|
|
470
|
+
self,
|
|
471
|
+
use_case: ValidateDocumentUseCase,
|
|
472
|
+
document_repo: MemoryDocumentRepository,
|
|
473
|
+
policy_repo: MemoryPolicyRepository,
|
|
474
|
+
knowledge_service_query_repo: MemoryKnowledgeServiceQueryRepository,
|
|
475
|
+
knowledge_service_config_repo: MemoryKnowledgeServiceConfigRepository,
|
|
476
|
+
document_policy_validation_repo: MemoryDocumentPolicyValidationRepository,
|
|
477
|
+
) -> None:
|
|
478
|
+
"""Test complete validation workflow that fails validation."""
|
|
479
|
+
# Arrange - Create test document
|
|
480
|
+
content_text = "Poor quality document"
|
|
481
|
+
content_bytes = content_text.encode("utf-8")
|
|
482
|
+
document = Document(
|
|
483
|
+
document_id="doc-456",
|
|
484
|
+
original_filename="poor_document.txt",
|
|
485
|
+
content_type="text/plain",
|
|
486
|
+
size_bytes=len(content_bytes),
|
|
487
|
+
content_multihash="test-hash-456",
|
|
488
|
+
status=DocumentStatus.CAPTURED,
|
|
489
|
+
content=ContentStream(io.BytesIO(content_bytes)),
|
|
490
|
+
created_at=datetime.now(timezone.utc),
|
|
491
|
+
updated_at=datetime.now(timezone.utc),
|
|
492
|
+
)
|
|
493
|
+
await document_repo.save(document)
|
|
494
|
+
|
|
495
|
+
# Create policy with high standards
|
|
496
|
+
policy = Policy(
|
|
497
|
+
policy_id="policy-456",
|
|
498
|
+
title="High Standards Policy",
|
|
499
|
+
description="Requires high quality scores",
|
|
500
|
+
status=PolicyStatus.ACTIVE,
|
|
501
|
+
validation_scores=[("quality-query", 90)], # High requirement
|
|
502
|
+
created_at=datetime.now(timezone.utc),
|
|
503
|
+
updated_at=datetime.now(timezone.utc),
|
|
504
|
+
)
|
|
505
|
+
await policy_repo.save(policy)
|
|
506
|
+
|
|
507
|
+
# Create knowledge service config and query
|
|
508
|
+
ks_config = KnowledgeServiceConfig(
|
|
509
|
+
knowledge_service_id="ks-456",
|
|
510
|
+
name="Test Knowledge Service",
|
|
511
|
+
description="Test service",
|
|
512
|
+
service_api=ServiceApi.ANTHROPIC,
|
|
513
|
+
created_at=datetime.now(timezone.utc),
|
|
514
|
+
updated_at=datetime.now(timezone.utc),
|
|
515
|
+
)
|
|
516
|
+
await knowledge_service_config_repo.save(ks_config)
|
|
517
|
+
|
|
518
|
+
quality_query = KnowledgeServiceQuery(
|
|
519
|
+
query_id="quality-query",
|
|
520
|
+
name="Quality Check",
|
|
521
|
+
knowledge_service_id="ks-456",
|
|
522
|
+
prompt="Rate the quality of this document on a scale of 0-100",
|
|
523
|
+
created_at=datetime.now(timezone.utc),
|
|
524
|
+
updated_at=datetime.now(timezone.utc),
|
|
525
|
+
)
|
|
526
|
+
await knowledge_service_query_repo.save(quality_query)
|
|
527
|
+
|
|
528
|
+
# Create memory service with failing score
|
|
529
|
+
memory_service = MemoryKnowledgeService(ks_config)
|
|
530
|
+
memory_service.add_canned_query_result(
|
|
531
|
+
QueryResult(
|
|
532
|
+
query_id="result-1",
|
|
533
|
+
query_text="Rate the quality of this document on a " "scale of 0-100",
|
|
534
|
+
result_data={"response": "60"}, # Fails requirement of 90
|
|
535
|
+
execution_time_ms=100,
|
|
536
|
+
created_at=datetime.now(timezone.utc),
|
|
537
|
+
)
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
# Create use case with configured memory service
|
|
541
|
+
configured_use_case = self._create_configured_use_case(
|
|
542
|
+
document_repo=document_repo,
|
|
543
|
+
knowledge_service_query_repo=knowledge_service_query_repo,
|
|
544
|
+
knowledge_service_config_repo=knowledge_service_config_repo,
|
|
545
|
+
policy_repo=policy_repo,
|
|
546
|
+
document_policy_validation_repo=document_policy_validation_repo,
|
|
547
|
+
memory_service=memory_service,
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
# Act
|
|
551
|
+
await configured_use_case.validate_document(
|
|
552
|
+
document_id="doc-456", policy_id="policy-456"
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
@pytest.mark.asyncio
|
|
556
|
+
async def test_validation_with_transformation_success(
|
|
557
|
+
self,
|
|
558
|
+
use_case: ValidateDocumentUseCase,
|
|
559
|
+
document_repo: MemoryDocumentRepository,
|
|
560
|
+
policy_repo: MemoryPolicyRepository,
|
|
561
|
+
knowledge_service_query_repo: MemoryKnowledgeServiceQueryRepository,
|
|
562
|
+
knowledge_service_config_repo: MemoryKnowledgeServiceConfigRepository,
|
|
563
|
+
document_policy_validation_repo: MemoryDocumentPolicyValidationRepository,
|
|
564
|
+
) -> None:
|
|
565
|
+
"""Test validation with transformation that results in passing."""
|
|
566
|
+
# Arrange - Create test document
|
|
567
|
+
content_text = "Poor quality document that can be improved"
|
|
568
|
+
content_bytes = content_text.encode("utf-8")
|
|
569
|
+
document = Document(
|
|
570
|
+
document_id="doc-transform-1",
|
|
571
|
+
original_filename="transform_test.txt",
|
|
572
|
+
content_type="text/plain",
|
|
573
|
+
size_bytes=len(content_bytes),
|
|
574
|
+
content_multihash="test-hash-transform-1",
|
|
575
|
+
status=DocumentStatus.CAPTURED,
|
|
576
|
+
content=ContentStream(io.BytesIO(content_bytes)),
|
|
577
|
+
created_at=datetime.now(timezone.utc),
|
|
578
|
+
updated_at=datetime.now(timezone.utc),
|
|
579
|
+
)
|
|
580
|
+
await document_repo.save(document)
|
|
581
|
+
|
|
582
|
+
# Create policy with transformation queries
|
|
583
|
+
policy = Policy(
|
|
584
|
+
policy_id="policy-transform-1",
|
|
585
|
+
title="Transformation Policy",
|
|
586
|
+
description="Policy with transformation capabilities",
|
|
587
|
+
status=PolicyStatus.ACTIVE,
|
|
588
|
+
validation_scores=[("quality-query", 80)],
|
|
589
|
+
transformation_queries=["improvement-query"],
|
|
590
|
+
created_at=datetime.now(timezone.utc),
|
|
591
|
+
updated_at=datetime.now(timezone.utc),
|
|
592
|
+
)
|
|
593
|
+
await policy_repo.save(policy)
|
|
594
|
+
|
|
595
|
+
# Create knowledge service config
|
|
596
|
+
ks_config = KnowledgeServiceConfig(
|
|
597
|
+
knowledge_service_id="ks-transform-1",
|
|
598
|
+
name="Transform Knowledge Service",
|
|
599
|
+
description="Service with transformation capability",
|
|
600
|
+
service_api=ServiceApi.ANTHROPIC,
|
|
601
|
+
created_at=datetime.now(timezone.utc),
|
|
602
|
+
updated_at=datetime.now(timezone.utc),
|
|
603
|
+
)
|
|
604
|
+
await knowledge_service_config_repo.save(ks_config)
|
|
605
|
+
|
|
606
|
+
# Create validation and transformation queries
|
|
607
|
+
quality_query = KnowledgeServiceQuery(
|
|
608
|
+
query_id="quality-query",
|
|
609
|
+
name="Quality Check",
|
|
610
|
+
knowledge_service_id="ks-transform-1",
|
|
611
|
+
prompt="Rate the quality of this document on a scale of 0-100",
|
|
612
|
+
created_at=datetime.now(timezone.utc),
|
|
613
|
+
updated_at=datetime.now(timezone.utc),
|
|
614
|
+
)
|
|
615
|
+
improvement_query = KnowledgeServiceQuery(
|
|
616
|
+
query_id="improvement-query",
|
|
617
|
+
name="Document Improvement",
|
|
618
|
+
knowledge_service_id="ks-transform-1",
|
|
619
|
+
prompt="Improve this document to make it higher quality",
|
|
620
|
+
created_at=datetime.now(timezone.utc),
|
|
621
|
+
updated_at=datetime.now(timezone.utc),
|
|
622
|
+
)
|
|
623
|
+
await knowledge_service_query_repo.save(quality_query)
|
|
624
|
+
await knowledge_service_query_repo.save(improvement_query)
|
|
625
|
+
|
|
626
|
+
# Create memory service that simulates transformation workflow
|
|
627
|
+
memory_service = MemoryKnowledgeService(ks_config)
|
|
628
|
+
|
|
629
|
+
# First validation (fails with score 60)
|
|
630
|
+
memory_service.add_canned_query_result(
|
|
631
|
+
QueryResult(
|
|
632
|
+
query_id="initial-validation",
|
|
633
|
+
query_text="Rate the quality of this document on a scale " "of 0-100",
|
|
634
|
+
result_data={"response": "60"}, # Initial score fails
|
|
635
|
+
execution_time_ms=100,
|
|
636
|
+
created_at=datetime.now(timezone.utc),
|
|
637
|
+
)
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
# Transformation query returns improved content
|
|
641
|
+
memory_service.add_canned_query_result(
|
|
642
|
+
QueryResult(
|
|
643
|
+
query_id="transformation",
|
|
644
|
+
query_text="Improve this document to make it higher quality",
|
|
645
|
+
result_data={
|
|
646
|
+
"response": '{"improved_content": "This is a much '
|
|
647
|
+
"higher quality document with better structure and "
|
|
648
|
+
'clarity."}'
|
|
649
|
+
},
|
|
650
|
+
execution_time_ms=200,
|
|
651
|
+
created_at=datetime.now(timezone.utc),
|
|
652
|
+
)
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
# Post-transformation validation (passes with score 85)
|
|
656
|
+
memory_service.add_canned_query_result(
|
|
657
|
+
QueryResult(
|
|
658
|
+
query_id="post-transform-validation",
|
|
659
|
+
query_text="Rate the quality of this document on a scale " "of 0-100",
|
|
660
|
+
result_data={"response": "85"}, # Post-transform score passes
|
|
661
|
+
execution_time_ms=100,
|
|
662
|
+
created_at=datetime.now(timezone.utc),
|
|
663
|
+
)
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
# Create use case with configured memory service
|
|
667
|
+
configured_use_case = self._create_configured_use_case(
|
|
668
|
+
document_repo=document_repo,
|
|
669
|
+
knowledge_service_query_repo=knowledge_service_query_repo,
|
|
670
|
+
knowledge_service_config_repo=knowledge_service_config_repo,
|
|
671
|
+
policy_repo=policy_repo,
|
|
672
|
+
document_policy_validation_repo=document_policy_validation_repo,
|
|
673
|
+
memory_service=memory_service,
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
# Act
|
|
677
|
+
result = await configured_use_case.validate_document(
|
|
678
|
+
document_id="doc-transform-1", policy_id="policy-transform-1"
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
# Assert
|
|
682
|
+
assert isinstance(result, DocumentPolicyValidation)
|
|
683
|
+
assert result.status == DocumentPolicyValidationStatus.PASSED
|
|
684
|
+
assert result.passed is True
|
|
685
|
+
assert result.validation_scores == [("quality-query", 60)] # Initial scores
|
|
686
|
+
assert result.post_transform_validation_scores == [
|
|
687
|
+
("quality-query", 85)
|
|
688
|
+
] # Final scores
|
|
689
|
+
assert result.transformed_document_id is not None
|
|
690
|
+
assert result.completed_at is not None
|
|
691
|
+
|
|
692
|
+
# Verify transformed document was created and saved
|
|
693
|
+
transformed_document = await document_repo.get(result.transformed_document_id)
|
|
694
|
+
assert transformed_document is not None
|
|
695
|
+
assert transformed_document.original_filename.startswith("transformed_")
|
|
696
|
+
assert transformed_document.content_type == "text/plain"
|
|
697
|
+
|
|
698
|
+
# Verify validation was saved to repository
|
|
699
|
+
saved_validation = await document_policy_validation_repo.get(
|
|
700
|
+
result.validation_id
|
|
701
|
+
)
|
|
702
|
+
assert saved_validation is not None
|
|
703
|
+
assert saved_validation.status == DocumentPolicyValidationStatus.PASSED
|
|
704
|
+
assert saved_validation.transformed_document_id is not None
|
|
705
|
+
|
|
706
|
+
@pytest.mark.asyncio
|
|
707
|
+
async def test_validation_with_transformation_still_fails(
|
|
708
|
+
self,
|
|
709
|
+
use_case: ValidateDocumentUseCase,
|
|
710
|
+
document_repo: MemoryDocumentRepository,
|
|
711
|
+
policy_repo: MemoryPolicyRepository,
|
|
712
|
+
knowledge_service_query_repo: MemoryKnowledgeServiceQueryRepository,
|
|
713
|
+
knowledge_service_config_repo: MemoryKnowledgeServiceConfigRepository,
|
|
714
|
+
document_policy_validation_repo: MemoryDocumentPolicyValidationRepository,
|
|
715
|
+
) -> None:
|
|
716
|
+
"""Test validation with transformation that still fails after
|
|
717
|
+
transformation."""
|
|
718
|
+
# Arrange - Create test document
|
|
719
|
+
content_text = "Very poor quality document"
|
|
720
|
+
content_bytes = content_text.encode("utf-8")
|
|
721
|
+
document = Document(
|
|
722
|
+
document_id="doc-transform-2",
|
|
723
|
+
original_filename="poor_transform_test.txt",
|
|
724
|
+
content_type="text/plain",
|
|
725
|
+
size_bytes=len(content_bytes),
|
|
726
|
+
content_multihash="test-hash-transform-2",
|
|
727
|
+
status=DocumentStatus.CAPTURED,
|
|
728
|
+
content=ContentStream(io.BytesIO(content_bytes)),
|
|
729
|
+
created_at=datetime.now(timezone.utc),
|
|
730
|
+
updated_at=datetime.now(timezone.utc),
|
|
731
|
+
)
|
|
732
|
+
await document_repo.save(document)
|
|
733
|
+
|
|
734
|
+
# Create policy with high standards and transformation
|
|
735
|
+
policy = Policy(
|
|
736
|
+
policy_id="policy-transform-2",
|
|
737
|
+
title="High Standards Transform Policy",
|
|
738
|
+
description="Policy with very high standards even after " "transformation",
|
|
739
|
+
status=PolicyStatus.ACTIVE,
|
|
740
|
+
validation_scores=[("quality-query", 95)], # Very high requirement
|
|
741
|
+
transformation_queries=["improvement-query"],
|
|
742
|
+
created_at=datetime.now(timezone.utc),
|
|
743
|
+
updated_at=datetime.now(timezone.utc),
|
|
744
|
+
)
|
|
745
|
+
await policy_repo.save(policy)
|
|
746
|
+
|
|
747
|
+
# Create knowledge service config
|
|
748
|
+
ks_config = KnowledgeServiceConfig(
|
|
749
|
+
knowledge_service_id="ks-transform-2",
|
|
750
|
+
name="Transform Knowledge Service",
|
|
751
|
+
description="Service with transformation capability",
|
|
752
|
+
service_api=ServiceApi.ANTHROPIC,
|
|
753
|
+
created_at=datetime.now(timezone.utc),
|
|
754
|
+
updated_at=datetime.now(timezone.utc),
|
|
755
|
+
)
|
|
756
|
+
await knowledge_service_config_repo.save(ks_config)
|
|
757
|
+
|
|
758
|
+
# Create validation and transformation queries
|
|
759
|
+
quality_query = KnowledgeServiceQuery(
|
|
760
|
+
query_id="quality-query",
|
|
761
|
+
name="Quality Check",
|
|
762
|
+
knowledge_service_id="ks-transform-2",
|
|
763
|
+
prompt="Rate the quality of this document on a scale of 0-100",
|
|
764
|
+
created_at=datetime.now(timezone.utc),
|
|
765
|
+
updated_at=datetime.now(timezone.utc),
|
|
766
|
+
)
|
|
767
|
+
improvement_query = KnowledgeServiceQuery(
|
|
768
|
+
query_id="improvement-query",
|
|
769
|
+
name="Document Improvement",
|
|
770
|
+
knowledge_service_id="ks-transform-2",
|
|
771
|
+
prompt="Try to improve this document",
|
|
772
|
+
created_at=datetime.now(timezone.utc),
|
|
773
|
+
updated_at=datetime.now(timezone.utc),
|
|
774
|
+
)
|
|
775
|
+
await knowledge_service_query_repo.save(quality_query)
|
|
776
|
+
await knowledge_service_query_repo.save(improvement_query)
|
|
777
|
+
|
|
778
|
+
# Create memory service that simulates failed transformation
|
|
779
|
+
memory_service = MemoryKnowledgeService(ks_config)
|
|
780
|
+
|
|
781
|
+
# Initial validation fails
|
|
782
|
+
memory_service.add_canned_query_result(
|
|
783
|
+
QueryResult(
|
|
784
|
+
query_id="initial-validation",
|
|
785
|
+
query_text="Rate the quality of this document on a scale " "of 0-100",
|
|
786
|
+
result_data={"response": "40"}, # Initial score fails
|
|
787
|
+
execution_time_ms=100,
|
|
788
|
+
created_at=datetime.now(timezone.utc),
|
|
789
|
+
)
|
|
790
|
+
)
|
|
791
|
+
|
|
792
|
+
# Transformation attempt
|
|
793
|
+
memory_service.add_canned_query_result(
|
|
794
|
+
QueryResult(
|
|
795
|
+
query_id="transformation",
|
|
796
|
+
query_text="Try to improve this document",
|
|
797
|
+
result_data={
|
|
798
|
+
"response": '{"improved_content": "Slightly improved '
|
|
799
|
+
'but still poor quality document."}'
|
|
800
|
+
},
|
|
801
|
+
execution_time_ms=200,
|
|
802
|
+
created_at=datetime.now(timezone.utc),
|
|
803
|
+
)
|
|
804
|
+
)
|
|
805
|
+
|
|
806
|
+
# Post-transformation validation still fails
|
|
807
|
+
memory_service.add_canned_query_result(
|
|
808
|
+
QueryResult(
|
|
809
|
+
query_id="post-transform-validation",
|
|
810
|
+
query_text="Rate the quality of this document on a scale " "of 0-100",
|
|
811
|
+
result_data={"response": "70"}, # Still fails requirement of 95
|
|
812
|
+
execution_time_ms=100,
|
|
813
|
+
created_at=datetime.now(timezone.utc),
|
|
814
|
+
)
|
|
815
|
+
)
|
|
816
|
+
|
|
817
|
+
# Create use case with configured memory service
|
|
818
|
+
configured_use_case = self._create_configured_use_case(
|
|
819
|
+
document_repo=document_repo,
|
|
820
|
+
knowledge_service_query_repo=knowledge_service_query_repo,
|
|
821
|
+
knowledge_service_config_repo=knowledge_service_config_repo,
|
|
822
|
+
policy_repo=policy_repo,
|
|
823
|
+
document_policy_validation_repo=document_policy_validation_repo,
|
|
824
|
+
memory_service=memory_service,
|
|
825
|
+
)
|
|
826
|
+
|
|
827
|
+
# Act
|
|
828
|
+
result = await configured_use_case.validate_document(
|
|
829
|
+
document_id="doc-transform-2", policy_id="policy-transform-2"
|
|
830
|
+
)
|
|
831
|
+
|
|
832
|
+
# Assert
|
|
833
|
+
assert isinstance(result, DocumentPolicyValidation)
|
|
834
|
+
assert result.status == DocumentPolicyValidationStatus.FAILED
|
|
835
|
+
assert result.passed is False
|
|
836
|
+
assert result.validation_scores == [("quality-query", 40)] # Initial scores
|
|
837
|
+
assert result.post_transform_validation_scores == [
|
|
838
|
+
("quality-query", 70)
|
|
839
|
+
] # Final scores still fail
|
|
840
|
+
assert result.transformed_document_id is not None
|
|
841
|
+
assert result.completed_at is not None
|
|
842
|
+
|
|
843
|
+
@pytest.mark.asyncio
|
|
844
|
+
async def test_validation_no_transformation_when_initially_passes(
|
|
845
|
+
self,
|
|
846
|
+
use_case: ValidateDocumentUseCase,
|
|
847
|
+
document_repo: MemoryDocumentRepository,
|
|
848
|
+
policy_repo: MemoryPolicyRepository,
|
|
849
|
+
knowledge_service_query_repo: MemoryKnowledgeServiceQueryRepository,
|
|
850
|
+
knowledge_service_config_repo: MemoryKnowledgeServiceConfigRepository,
|
|
851
|
+
document_policy_validation_repo: MemoryDocumentPolicyValidationRepository,
|
|
852
|
+
) -> None:
|
|
853
|
+
"""Test that transformation is skipped when initial validation
|
|
854
|
+
passes."""
|
|
855
|
+
# Arrange - Create high quality document
|
|
856
|
+
content_text = "Excellent high quality document"
|
|
857
|
+
content_bytes = content_text.encode("utf-8")
|
|
858
|
+
document = Document(
|
|
859
|
+
document_id="doc-no-transform",
|
|
860
|
+
original_filename="excellent_doc.txt",
|
|
861
|
+
content_type="text/plain",
|
|
862
|
+
size_bytes=len(content_bytes),
|
|
863
|
+
content_multihash="test-hash-no-transform",
|
|
864
|
+
status=DocumentStatus.CAPTURED,
|
|
865
|
+
content=ContentStream(io.BytesIO(content_bytes)),
|
|
866
|
+
created_at=datetime.now(timezone.utc),
|
|
867
|
+
updated_at=datetime.now(timezone.utc),
|
|
868
|
+
)
|
|
869
|
+
await document_repo.save(document)
|
|
870
|
+
|
|
871
|
+
# Create policy with transformation available but not needed
|
|
872
|
+
policy = Policy(
|
|
873
|
+
policy_id="policy-no-transform",
|
|
874
|
+
title="Policy with Unnecessary Transform",
|
|
875
|
+
description="Policy with transformation that won't be used",
|
|
876
|
+
status=PolicyStatus.ACTIVE,
|
|
877
|
+
validation_scores=[("quality-query", 80)],
|
|
878
|
+
transformation_queries=["improvement-query"], # Available but unused
|
|
879
|
+
created_at=datetime.now(timezone.utc),
|
|
880
|
+
updated_at=datetime.now(timezone.utc),
|
|
881
|
+
)
|
|
882
|
+
await policy_repo.save(policy)
|
|
883
|
+
|
|
884
|
+
# Create knowledge service config
|
|
885
|
+
ks_config = KnowledgeServiceConfig(
|
|
886
|
+
knowledge_service_id="ks-no-transform",
|
|
887
|
+
name="Knowledge Service",
|
|
888
|
+
description="Service description",
|
|
889
|
+
service_api=ServiceApi.ANTHROPIC,
|
|
890
|
+
created_at=datetime.now(timezone.utc),
|
|
891
|
+
updated_at=datetime.now(timezone.utc),
|
|
892
|
+
)
|
|
893
|
+
await knowledge_service_config_repo.save(ks_config)
|
|
894
|
+
|
|
895
|
+
# Create queries (transformation query won't be used)
|
|
896
|
+
quality_query = KnowledgeServiceQuery(
|
|
897
|
+
query_id="quality-query",
|
|
898
|
+
name="Quality Check",
|
|
899
|
+
knowledge_service_id="ks-no-transform",
|
|
900
|
+
prompt="Rate the quality of this document on a scale of 0-100",
|
|
901
|
+
created_at=datetime.now(timezone.utc),
|
|
902
|
+
updated_at=datetime.now(timezone.utc),
|
|
903
|
+
)
|
|
904
|
+
improvement_query = KnowledgeServiceQuery(
|
|
905
|
+
query_id="improvement-query",
|
|
906
|
+
name="Document Improvement",
|
|
907
|
+
knowledge_service_id="ks-no-transform",
|
|
908
|
+
prompt="This query should not be called",
|
|
909
|
+
created_at=datetime.now(timezone.utc),
|
|
910
|
+
updated_at=datetime.now(timezone.utc),
|
|
911
|
+
)
|
|
912
|
+
await knowledge_service_query_repo.save(quality_query)
|
|
913
|
+
await knowledge_service_query_repo.save(improvement_query)
|
|
914
|
+
|
|
915
|
+
# Create memory service with only validation result (no
|
|
916
|
+
# transformation)
|
|
917
|
+
memory_service = MemoryKnowledgeService(ks_config)
|
|
918
|
+
memory_service.add_canned_query_result(
|
|
919
|
+
QueryResult(
|
|
920
|
+
query_id="validation-only",
|
|
921
|
+
query_text="Rate the quality of this document on a scale " "of 0-100",
|
|
922
|
+
result_data={"response": "90"}, # Passes initial validation
|
|
923
|
+
execution_time_ms=100,
|
|
924
|
+
created_at=datetime.now(timezone.utc),
|
|
925
|
+
)
|
|
926
|
+
)
|
|
927
|
+
|
|
928
|
+
# Create use case with configured memory service
|
|
929
|
+
configured_use_case = self._create_configured_use_case(
|
|
930
|
+
document_repo=document_repo,
|
|
931
|
+
knowledge_service_query_repo=knowledge_service_query_repo,
|
|
932
|
+
knowledge_service_config_repo=knowledge_service_config_repo,
|
|
933
|
+
policy_repo=policy_repo,
|
|
934
|
+
document_policy_validation_repo=document_policy_validation_repo,
|
|
935
|
+
memory_service=memory_service,
|
|
936
|
+
)
|
|
937
|
+
|
|
938
|
+
# Act
|
|
939
|
+
result = await configured_use_case.validate_document(
|
|
940
|
+
document_id="doc-no-transform", policy_id="policy-no-transform"
|
|
941
|
+
)
|
|
942
|
+
|
|
943
|
+
# Assert
|
|
944
|
+
assert isinstance(result, DocumentPolicyValidation)
|
|
945
|
+
assert result.status == DocumentPolicyValidationStatus.PASSED
|
|
946
|
+
assert result.passed is True
|
|
947
|
+
assert result.validation_scores == [("quality-query", 90)]
|
|
948
|
+
assert result.post_transform_validation_scores is None # No transformation
|
|
949
|
+
assert result.transformed_document_id is None # No transformation
|
|
950
|
+
assert result.completed_at is not None
|
|
951
|
+
|
|
952
|
+
@pytest.mark.asyncio
|
|
953
|
+
async def test_transformation_fails_with_invalid_json(
|
|
954
|
+
self,
|
|
955
|
+
use_case: ValidateDocumentUseCase,
|
|
956
|
+
document_repo: MemoryDocumentRepository,
|
|
957
|
+
policy_repo: MemoryPolicyRepository,
|
|
958
|
+
knowledge_service_query_repo: MemoryKnowledgeServiceQueryRepository,
|
|
959
|
+
knowledge_service_config_repo: MemoryKnowledgeServiceConfigRepository,
|
|
960
|
+
document_policy_validation_repo: MemoryDocumentPolicyValidationRepository,
|
|
961
|
+
) -> None:
|
|
962
|
+
"""Test that transformation fails when result is not valid JSON."""
|
|
963
|
+
# Arrange - Create test document
|
|
964
|
+
content_text = "Document needing transformation"
|
|
965
|
+
content_bytes = content_text.encode("utf-8")
|
|
966
|
+
document = Document(
|
|
967
|
+
document_id="doc-invalid-json",
|
|
968
|
+
original_filename="invalid_json_test.txt",
|
|
969
|
+
content_type="text/plain",
|
|
970
|
+
size_bytes=len(content_bytes),
|
|
971
|
+
content_multihash="test-hash-invalid-json",
|
|
972
|
+
status=DocumentStatus.CAPTURED,
|
|
973
|
+
content=ContentStream(io.BytesIO(content_bytes)),
|
|
974
|
+
created_at=datetime.now(timezone.utc),
|
|
975
|
+
updated_at=datetime.now(timezone.utc),
|
|
976
|
+
)
|
|
977
|
+
await document_repo.save(document)
|
|
978
|
+
|
|
979
|
+
# Create policy with transformation
|
|
980
|
+
policy = Policy(
|
|
981
|
+
policy_id="policy-invalid-json",
|
|
982
|
+
title="Invalid JSON Transform Policy",
|
|
983
|
+
description="Policy that will get invalid JSON from " "transformation",
|
|
984
|
+
status=PolicyStatus.ACTIVE,
|
|
985
|
+
validation_scores=[("quality-query", 80)],
|
|
986
|
+
transformation_queries=["bad-transform-query"],
|
|
987
|
+
created_at=datetime.now(timezone.utc),
|
|
988
|
+
updated_at=datetime.now(timezone.utc),
|
|
989
|
+
)
|
|
990
|
+
await policy_repo.save(policy)
|
|
991
|
+
|
|
992
|
+
# Create knowledge service config
|
|
993
|
+
ks_config = KnowledgeServiceConfig(
|
|
994
|
+
knowledge_service_id="ks-invalid-json",
|
|
995
|
+
name="Invalid JSON Service",
|
|
996
|
+
description="Service that returns invalid JSON",
|
|
997
|
+
service_api=ServiceApi.ANTHROPIC,
|
|
998
|
+
created_at=datetime.now(timezone.utc),
|
|
999
|
+
updated_at=datetime.now(timezone.utc),
|
|
1000
|
+
)
|
|
1001
|
+
await knowledge_service_config_repo.save(ks_config)
|
|
1002
|
+
|
|
1003
|
+
# Create queries
|
|
1004
|
+
quality_query = KnowledgeServiceQuery(
|
|
1005
|
+
query_id="quality-query",
|
|
1006
|
+
name="Quality Check",
|
|
1007
|
+
knowledge_service_id="ks-invalid-json",
|
|
1008
|
+
prompt="Rate the quality of this document on a scale of 0-100",
|
|
1009
|
+
created_at=datetime.now(timezone.utc),
|
|
1010
|
+
updated_at=datetime.now(timezone.utc),
|
|
1011
|
+
)
|
|
1012
|
+
bad_transform_query = KnowledgeServiceQuery(
|
|
1013
|
+
query_id="bad-transform-query",
|
|
1014
|
+
name="Bad Transform Query",
|
|
1015
|
+
knowledge_service_id="ks-invalid-json",
|
|
1016
|
+
prompt="Transform this document",
|
|
1017
|
+
created_at=datetime.now(timezone.utc),
|
|
1018
|
+
updated_at=datetime.now(timezone.utc),
|
|
1019
|
+
)
|
|
1020
|
+
await knowledge_service_query_repo.save(quality_query)
|
|
1021
|
+
await knowledge_service_query_repo.save(bad_transform_query)
|
|
1022
|
+
|
|
1023
|
+
# Create memory service that returns invalid JSON
|
|
1024
|
+
memory_service = MemoryKnowledgeService(ks_config)
|
|
1025
|
+
|
|
1026
|
+
# Initial validation fails
|
|
1027
|
+
memory_service.add_canned_query_result(
|
|
1028
|
+
QueryResult(
|
|
1029
|
+
query_id="initial-validation",
|
|
1030
|
+
query_text="Rate the quality of this document on a scale " "of 0-100",
|
|
1031
|
+
result_data={"response": "50"}, # Fails, triggers transformation
|
|
1032
|
+
execution_time_ms=100,
|
|
1033
|
+
created_at=datetime.now(timezone.utc),
|
|
1034
|
+
)
|
|
1035
|
+
)
|
|
1036
|
+
|
|
1037
|
+
# Transformation returns invalid JSON
|
|
1038
|
+
memory_service.add_canned_query_result(
|
|
1039
|
+
QueryResult(
|
|
1040
|
+
query_id="bad-transformation",
|
|
1041
|
+
query_text="Transform this document",
|
|
1042
|
+
result_data={"response": "This is not valid JSON at all!"},
|
|
1043
|
+
execution_time_ms=200,
|
|
1044
|
+
created_at=datetime.now(timezone.utc),
|
|
1045
|
+
)
|
|
1046
|
+
)
|
|
1047
|
+
|
|
1048
|
+
# Create use case with configured memory service
|
|
1049
|
+
configured_use_case = self._create_configured_use_case(
|
|
1050
|
+
document_repo=document_repo,
|
|
1051
|
+
knowledge_service_query_repo=knowledge_service_query_repo,
|
|
1052
|
+
knowledge_service_config_repo=knowledge_service_config_repo,
|
|
1053
|
+
policy_repo=policy_repo,
|
|
1054
|
+
document_policy_validation_repo=document_policy_validation_repo,
|
|
1055
|
+
memory_service=memory_service,
|
|
1056
|
+
)
|
|
1057
|
+
|
|
1058
|
+
# Act & Assert
|
|
1059
|
+
with pytest.raises(
|
|
1060
|
+
ValueError,
|
|
1061
|
+
match="Transformation result must be valid JSON",
|
|
1062
|
+
):
|
|
1063
|
+
await configured_use_case.validate_document(
|
|
1064
|
+
document_id="doc-invalid-json",
|
|
1065
|
+
policy_id="policy-invalid-json",
|
|
1066
|
+
)
|
|
1067
|
+
|
|
1068
|
+
@pytest.mark.asyncio
|
|
1069
|
+
async def test_transformation_query_not_found(
|
|
1070
|
+
self,
|
|
1071
|
+
use_case: ValidateDocumentUseCase,
|
|
1072
|
+
document_repo: MemoryDocumentRepository,
|
|
1073
|
+
policy_repo: MemoryPolicyRepository,
|
|
1074
|
+
knowledge_service_query_repo: MemoryKnowledgeServiceQueryRepository,
|
|
1075
|
+
knowledge_service_config_repo: MemoryKnowledgeServiceConfigRepository,
|
|
1076
|
+
) -> None:
|
|
1077
|
+
"""Test that validation fails when transformation query is not
|
|
1078
|
+
found."""
|
|
1079
|
+
# Arrange - Create test document
|
|
1080
|
+
content_text = "Document needing transformation"
|
|
1081
|
+
content_bytes = content_text.encode("utf-8")
|
|
1082
|
+
document = Document(
|
|
1083
|
+
document_id="doc-missing-query",
|
|
1084
|
+
original_filename="missing_query_test.txt",
|
|
1085
|
+
content_type="text/plain",
|
|
1086
|
+
size_bytes=len(content_bytes),
|
|
1087
|
+
content_multihash="test-hash-missing-query",
|
|
1088
|
+
status=DocumentStatus.CAPTURED,
|
|
1089
|
+
content=ContentStream(io.BytesIO(content_bytes)),
|
|
1090
|
+
created_at=datetime.now(timezone.utc),
|
|
1091
|
+
updated_at=datetime.now(timezone.utc),
|
|
1092
|
+
)
|
|
1093
|
+
await document_repo.save(document)
|
|
1094
|
+
|
|
1095
|
+
# Create policy with non-existent transformation query
|
|
1096
|
+
policy = Policy(
|
|
1097
|
+
policy_id="policy-missing-query",
|
|
1098
|
+
title="Missing Query Policy",
|
|
1099
|
+
description="Policy with missing transformation query",
|
|
1100
|
+
status=PolicyStatus.ACTIVE,
|
|
1101
|
+
validation_scores=[("quality-query", 80)],
|
|
1102
|
+
transformation_queries=["nonexistent-transform-query"],
|
|
1103
|
+
created_at=datetime.now(timezone.utc),
|
|
1104
|
+
updated_at=datetime.now(timezone.utc),
|
|
1105
|
+
)
|
|
1106
|
+
await policy_repo.save(policy)
|
|
1107
|
+
|
|
1108
|
+
# Create knowledge service config
|
|
1109
|
+
ks_config = KnowledgeServiceConfig(
|
|
1110
|
+
knowledge_service_id="ks-missing-query",
|
|
1111
|
+
name="Missing Query Service",
|
|
1112
|
+
description="Service config",
|
|
1113
|
+
service_api=ServiceApi.ANTHROPIC,
|
|
1114
|
+
created_at=datetime.now(timezone.utc),
|
|
1115
|
+
updated_at=datetime.now(timezone.utc),
|
|
1116
|
+
)
|
|
1117
|
+
await knowledge_service_config_repo.save(ks_config)
|
|
1118
|
+
|
|
1119
|
+
# Create only the validation query (transformation query is missing)
|
|
1120
|
+
quality_query = KnowledgeServiceQuery(
|
|
1121
|
+
query_id="quality-query",
|
|
1122
|
+
name="Quality Check",
|
|
1123
|
+
knowledge_service_id="ks-missing-query",
|
|
1124
|
+
prompt="Rate the quality of this document on a scale of 0-100",
|
|
1125
|
+
created_at=datetime.now(timezone.utc),
|
|
1126
|
+
updated_at=datetime.now(timezone.utc),
|
|
1127
|
+
)
|
|
1128
|
+
await knowledge_service_query_repo.save(quality_query)
|
|
1129
|
+
# Note: NOT saving the transformation query
|
|
1130
|
+
|
|
1131
|
+
# Act & Assert
|
|
1132
|
+
with pytest.raises(ValueError, match="Transformation query not found"):
|
|
1133
|
+
await use_case.validate_document(
|
|
1134
|
+
document_id="doc-missing-query",
|
|
1135
|
+
policy_id="policy-missing-query",
|
|
1136
|
+
)
|
|
1137
|
+
|
|
1138
|
+
@pytest.mark.asyncio
|
|
1139
|
+
async def test_validation_fails_with_out_of_range_scores(
|
|
1140
|
+
self,
|
|
1141
|
+
use_case: ValidateDocumentUseCase,
|
|
1142
|
+
document_repo: MemoryDocumentRepository,
|
|
1143
|
+
policy_repo: MemoryPolicyRepository,
|
|
1144
|
+
knowledge_service_query_repo: MemoryKnowledgeServiceQueryRepository,
|
|
1145
|
+
knowledge_service_config_repo: MemoryKnowledgeServiceConfigRepository,
|
|
1146
|
+
document_policy_validation_repo: MemoryDocumentPolicyValidationRepository,
|
|
1147
|
+
) -> None:
|
|
1148
|
+
"""Test that validation fails when domain model rejects out-of-range
|
|
1149
|
+
scores."""
|
|
1150
|
+
# Arrange - Create test document
|
|
1151
|
+
content_text = "Test document"
|
|
1152
|
+
content_bytes = content_text.encode("utf-8")
|
|
1153
|
+
document = Document(
|
|
1154
|
+
document_id="doc-789",
|
|
1155
|
+
original_filename="test.txt",
|
|
1156
|
+
content_type="text/plain",
|
|
1157
|
+
size_bytes=len(content_bytes),
|
|
1158
|
+
content_multihash="test-hash-789",
|
|
1159
|
+
status=DocumentStatus.CAPTURED,
|
|
1160
|
+
content=ContentStream(io.BytesIO(content_bytes)),
|
|
1161
|
+
created_at=datetime.now(timezone.utc),
|
|
1162
|
+
updated_at=datetime.now(timezone.utc),
|
|
1163
|
+
)
|
|
1164
|
+
await document_repo.save(document)
|
|
1165
|
+
|
|
1166
|
+
# Create policy
|
|
1167
|
+
policy = Policy(
|
|
1168
|
+
policy_id="policy-789",
|
|
1169
|
+
title="Test Policy",
|
|
1170
|
+
description="Test policy for out-of-range scores",
|
|
1171
|
+
status=PolicyStatus.ACTIVE,
|
|
1172
|
+
validation_scores=[("test-query", 80)],
|
|
1173
|
+
created_at=datetime.now(timezone.utc),
|
|
1174
|
+
updated_at=datetime.now(timezone.utc),
|
|
1175
|
+
)
|
|
1176
|
+
await policy_repo.save(policy)
|
|
1177
|
+
|
|
1178
|
+
# Create knowledge service config and query
|
|
1179
|
+
ks_config = KnowledgeServiceConfig(
|
|
1180
|
+
knowledge_service_id="ks-789",
|
|
1181
|
+
name="Test Knowledge Service",
|
|
1182
|
+
description="Test service",
|
|
1183
|
+
service_api=ServiceApi.ANTHROPIC,
|
|
1184
|
+
created_at=datetime.now(timezone.utc),
|
|
1185
|
+
updated_at=datetime.now(timezone.utc),
|
|
1186
|
+
)
|
|
1187
|
+
await knowledge_service_config_repo.save(ks_config)
|
|
1188
|
+
|
|
1189
|
+
test_query = KnowledgeServiceQuery(
|
|
1190
|
+
query_id="test-query",
|
|
1191
|
+
name="Test Query",
|
|
1192
|
+
knowledge_service_id="ks-789",
|
|
1193
|
+
prompt="Rate this document",
|
|
1194
|
+
created_at=datetime.now(timezone.utc),
|
|
1195
|
+
updated_at=datetime.now(timezone.utc),
|
|
1196
|
+
)
|
|
1197
|
+
await knowledge_service_query_repo.save(test_query)
|
|
1198
|
+
|
|
1199
|
+
# Create memory service with out-of-range score
|
|
1200
|
+
memory_service = MemoryKnowledgeService(ks_config)
|
|
1201
|
+
memory_service.add_canned_query_result(
|
|
1202
|
+
QueryResult(
|
|
1203
|
+
query_id="result-1",
|
|
1204
|
+
query_text="Rate this document",
|
|
1205
|
+
result_data={"response": "150"}, # Out of normal 0-100 range
|
|
1206
|
+
execution_time_ms=100,
|
|
1207
|
+
created_at=datetime.now(timezone.utc),
|
|
1208
|
+
)
|
|
1209
|
+
)
|
|
1210
|
+
|
|
1211
|
+
# Create use case with configured memory service
|
|
1212
|
+
configured_use_case = self._create_configured_use_case(
|
|
1213
|
+
document_repo=document_repo,
|
|
1214
|
+
knowledge_service_query_repo=knowledge_service_query_repo,
|
|
1215
|
+
knowledge_service_config_repo=knowledge_service_config_repo,
|
|
1216
|
+
policy_repo=policy_repo,
|
|
1217
|
+
document_policy_validation_repo=document_policy_validation_repo,
|
|
1218
|
+
memory_service=memory_service,
|
|
1219
|
+
)
|
|
1220
|
+
|
|
1221
|
+
# Act & Assert
|
|
1222
|
+
with pytest.raises(
|
|
1223
|
+
ValidationError,
|
|
1224
|
+
match="must be between 0 and 100",
|
|
1225
|
+
):
|
|
1226
|
+
await configured_use_case.validate_document(
|
|
1227
|
+
document_id="doc-789", policy_id="policy-789"
|
|
1228
|
+
)
|