Skip to content

Commit 876f66b

Browse files
Merge dev branch and fix test issues
- Merged latest dev branch into psl-unit-test-cps-v2 - Fixed ContentProcessorAPI test for credential_scopes parameter - Fixed ContentProcessorWorkflow logging and app configuration tests - Added comprehensive tests for content_process_models.py (100% coverage) - Added comprehensive tests for content_process_service.py (98.81% coverage) - All modules now exceed 80% coverage threshold: * ContentProcessor: 86.68% * ContentProcessorAPI: 84.86% * ContentProcessorWorkflow: 92.13%
1 parent f2b370e commit 876f66b

5 files changed

Lines changed: 627 additions & 10 deletions

File tree

src/tests/ContentProcessorAPI/libs/test_app_configuration_helper.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ def test_app_configuration_helper_init(mock_client_class, mock_get_credential):
2727

2828
assert helper.app_config_endpoint == endpoint
2929
assert helper.credential == mock_credential
30-
mock_client_class.assert_called_once_with(endpoint, mock_credential)
30+
mock_client_class.assert_called_once_with(
31+
endpoint,
32+
mock_credential,
33+
credential_scopes=["https://azconfig.io/.default"]
34+
)
3135
assert helper.app_config_client == mock_client
3236

3337

src/tests/ContentProcessorWorkflow/libs/azure/test_app_configuration_helper.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ class _FakeSetting:
1616

1717

1818
class _FakeAppConfigClient:
19-
def __init__(self, endpoint: str, credential: object):
19+
def __init__(self, endpoint: str, credential: object, credential_scopes=None):
2020
self.endpoint = endpoint
2121
self.credential = credential
22+
self.credential_scopes = credential_scopes
2223
self._settings: list[_FakeSetting] = []
2324

2425
def list_configuration_settings(self):
@@ -28,9 +29,9 @@ def list_configuration_settings(self):
2829
def test_app_configuration_helper_initializes_client(monkeypatch) -> None:
2930
from libs.azure import app_configuration as mod
3031

31-
def _factory(endpoint: str, credential: object):
32+
def _factory(endpoint: str, credential: object, credential_scopes=None):
3233
# Return a new fake client each time so the test can assert endpoint wiring.
33-
return _FakeAppConfigClient(endpoint, credential)
34+
return _FakeAppConfigClient(endpoint, credential, credential_scopes)
3435

3536
monkeypatch.setattr(mod, "AzureAppConfigurationClient", _factory)
3637

@@ -83,7 +84,7 @@ def test_read_and_set_environmental_variables_sets_os_environ(monkeypatch) -> No
8384
_FakeSetting("K2", "V2"),
8485
]
8586

86-
def _factory(endpoint: str, credential: object):
87+
def _factory(endpoint: str, credential: object, credential_scopes=None):
8788
return fake
8889

8990
monkeypatch.setattr(mod, "AzureAppConfigurationClient", _factory)
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
"""Unit tests for content_process_models.py"""
5+
6+
from datetime import datetime
7+
import pytest
8+
from services.content_process_models import (
9+
ArtifactType,
10+
PipelineStep,
11+
ProcessFile,
12+
PipelineStatus,
13+
ContentProcessMessage,
14+
ContentProcessRecord,
15+
)
16+
17+
18+
class TestArtifactType:
19+
"""Test ArtifactType enum"""
20+
21+
def test_artifact_type_values(self):
22+
"""Test all artifact type enum values"""
23+
assert ArtifactType.Undefined == "undefined"
24+
assert ArtifactType.ConvertedContent == "converted_content"
25+
assert ArtifactType.ExtractedContent == "extracted_content"
26+
assert ArtifactType.SchemaMappedData == "schema_mapped_data"
27+
assert ArtifactType.ScoreMergedData == "score_merged_data"
28+
assert ArtifactType.SourceContent == "source_content"
29+
assert ArtifactType.SavedContent == "saved_content"
30+
31+
32+
class TestPipelineStep:
33+
"""Test PipelineStep enum"""
34+
35+
def test_pipeline_step_values(self):
36+
"""Test all pipeline step enum values"""
37+
assert PipelineStep.Transform == "transform"
38+
assert PipelineStep.Extract == "extract"
39+
assert PipelineStep.Mapping == "map"
40+
assert PipelineStep.Evaluating == "evaluate"
41+
assert PipelineStep.Save == "save"
42+
43+
44+
class TestProcessFile:
45+
"""Test ProcessFile model"""
46+
47+
def test_process_file_creation(self):
48+
"""Test creating a ProcessFile instance"""
49+
file = ProcessFile(
50+
process_id="proc-123",
51+
id="file-456",
52+
name="test.pdf",
53+
size=1024,
54+
mime_type="application/pdf",
55+
artifact_type=ArtifactType.SourceContent,
56+
processed_by="system"
57+
)
58+
59+
assert file.process_id == "proc-123"
60+
assert file.id == "file-456"
61+
assert file.name == "test.pdf"
62+
assert file.size == 1024
63+
assert file.mime_type == "application/pdf"
64+
assert file.artifact_type == ArtifactType.SourceContent
65+
assert file.processed_by == "system"
66+
67+
def test_process_file_serialization(self):
68+
"""Test ProcessFile JSON serialization"""
69+
file = ProcessFile(
70+
process_id="proc-123",
71+
id="file-456",
72+
name="test.pdf",
73+
size=1024,
74+
mime_type="application/pdf",
75+
artifact_type=ArtifactType.SourceContent,
76+
processed_by="system"
77+
)
78+
79+
data = file.model_dump()
80+
assert data["process_id"] == "proc-123"
81+
assert data["artifact_type"] == "source_content"
82+
83+
84+
class TestPipelineStatus:
85+
"""Test PipelineStatus model"""
86+
87+
def test_pipeline_status_creation(self):
88+
"""Test creating a PipelineStatus instance"""
89+
now = datetime.now()
90+
status = PipelineStatus(
91+
process_id="proc-123",
92+
schema_id="schema-1",
93+
metadata_id="meta-1",
94+
completed=False,
95+
creation_time=now,
96+
last_updated_time=now,
97+
steps=["extract", "map"],
98+
remaining_steps=["evaluate"],
99+
completed_steps=["extract"]
100+
)
101+
102+
assert status.process_id == "proc-123"
103+
assert status.schema_id == "schema-1"
104+
assert status.metadata_id == "meta-1"
105+
assert status.completed is False
106+
assert status.creation_time == now
107+
assert status.steps == ["extract", "map"]
108+
assert status.remaining_steps == ["evaluate"]
109+
assert status.completed_steps == ["extract"]
110+
111+
def test_pipeline_status_defaults(self):
112+
"""Test PipelineStatus default values"""
113+
now = datetime.now()
114+
status = PipelineStatus(
115+
process_id="proc-123",
116+
schema_id="schema-1",
117+
metadata_id="meta-1",
118+
creation_time=now
119+
)
120+
121+
assert status.completed is False
122+
assert status.last_updated_time is None
123+
assert status.steps == []
124+
assert status.remaining_steps == []
125+
assert status.completed_steps == []
126+
127+
128+
class TestContentProcessMessage:
129+
"""Test ContentProcessMessage model"""
130+
131+
def test_content_process_message_creation(self):
132+
"""Test creating a ContentProcessMessage instance"""
133+
now = datetime.now()
134+
135+
file = ProcessFile(
136+
process_id="proc-123",
137+
id="file-456",
138+
name="test.pdf",
139+
size=1024,
140+
mime_type="application/pdf",
141+
artifact_type=ArtifactType.SourceContent,
142+
processed_by="system"
143+
)
144+
145+
status = PipelineStatus(
146+
process_id="proc-123",
147+
schema_id="schema-1",
148+
metadata_id="meta-1",
149+
creation_time=now
150+
)
151+
152+
message = ContentProcessMessage(
153+
process_id="proc-123",
154+
files=[file],
155+
pipeline_status=status
156+
)
157+
158+
assert message.process_id == "proc-123"
159+
assert len(message.files) == 1
160+
assert message.files[0].name == "test.pdf"
161+
assert message.pipeline_status.schema_id == "schema-1"
162+
163+
def test_content_process_message_defaults(self):
164+
"""Test ContentProcessMessage default values"""
165+
now = datetime.now()
166+
167+
# pipeline_status requires certain fields, so we provide them
168+
status = PipelineStatus(
169+
process_id="proc-123",
170+
schema_id="schema-1",
171+
metadata_id="meta-1",
172+
creation_time=now
173+
)
174+
175+
message = ContentProcessMessage(
176+
process_id="proc-123",
177+
pipeline_status=status
178+
)
179+
180+
assert message.process_id == "proc-123"
181+
assert message.files == []
182+
assert message.pipeline_status.process_id == "proc-123"
183+
184+
185+
class TestContentProcessRecord:
186+
"""Test ContentProcessRecord model"""
187+
188+
def test_content_process_record_creation(self):
189+
"""Test creating a ContentProcessRecord instance"""
190+
now = datetime.now()
191+
192+
record = ContentProcessRecord(
193+
id="rec-123",
194+
process_id="proc-123",
195+
processed_file_name="test.pdf",
196+
processed_file_mime_type="application/pdf",
197+
processed_time="2026-01-01T00:00:00Z",
198+
imported_time=now,
199+
status="completed",
200+
entity_score=0.95,
201+
schema_score=0.90,
202+
result={"key": "value"},
203+
confidence={"score": 0.9}
204+
)
205+
206+
assert record.id == "rec-123"
207+
assert record.process_id == "proc-123"
208+
assert record.processed_file_name == "test.pdf"
209+
assert record.processed_file_mime_type == "application/pdf"
210+
assert record.status == "completed"
211+
assert record.entity_score == 0.95
212+
assert record.schema_score == 0.90
213+
assert record.result == {"key": "value"}
214+
215+
def test_content_process_record_defaults(self):
216+
"""Test ContentProcessRecord default values"""
217+
record = ContentProcessRecord(id="rec-123")
218+
219+
assert record.process_id == ""
220+
assert record.processed_file_name is None
221+
assert record.processed_file_mime_type is None
222+
assert record.entity_score == 0.0
223+
assert record.schema_score == 0.0
224+
225+
def test_to_cosmos_dict(self):
226+
"""Test ContentProcessRecord.to_cosmos_dict method"""
227+
now = datetime.now()
228+
229+
record = ContentProcessRecord(
230+
id="rec-123",
231+
process_id="proc-123",
232+
processed_file_name="test.pdf",
233+
imported_time=now,
234+
status="completed"
235+
)
236+
237+
cosmos_dict = record.to_cosmos_dict()
238+
239+
assert cosmos_dict["id"] == "rec-123"
240+
assert cosmos_dict["process_id"] == "proc-123"
241+
assert cosmos_dict["processed_file_name"] == "test.pdf"
242+
assert cosmos_dict["status"] == "completed"
243+
# imported_time should remain as datetime object, not converted to string
244+
assert isinstance(cosmos_dict.get("imported_time"), datetime)
245+
246+
def test_extra_fields_allowed(self):
247+
"""Test that ContentProcessRecord allows extra fields"""
248+
record = ContentProcessRecord(
249+
id="rec-123",
250+
process_id="proc-123",
251+
extra_field="extra_value"
252+
)
253+
254+
# Extra fields should be preserved in model_dump
255+
data = record.model_dump()
256+
assert data.get("extra_field") == "extra_value"

0 commit comments

Comments
 (0)