Skip to content

Commit 5954dfd

Browse files
Merge pull request #440 from microsoft/dev
chore: Dev to main
2 parents 48c4c8e + bc1828a commit 5954dfd

File tree

11 files changed

+892
-92
lines changed

11 files changed

+892
-92
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ jobs:
7272
if: env.skip_backend_tests == 'false'
7373
run: |
7474
cd src/ContentProcessor
75-
python -m pytest -vv --cov=. --cov-report=xml --cov-report=term-missing --cov-fail-under=80
75+
python -m pytest -vv --cov=src --cov-report=xml --cov-report=term-missing --cov-fail-under=80
7676
7777
- name: Skip Backend Tests
7878
if: env.skip_backend_tests == 'true'

src/ContentProcessor/pyproject.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,15 @@ addopts = "--maxfail=1"
4141
testpaths = ["tests"]
4242
pythonpath = ["src"]
4343

44+
[tool.coverage.run]
45+
source = ["src"]
46+
omit = ["src/tests/*", "**/test_*.py", "**/*_test.py"]
47+
48+
[tool.coverage.report]
49+
exclude_lines = [
50+
"pragma: no cover",
51+
"if __name__ == .__main__.:",
52+
]
53+
4454
[tool.poetry.scripts]
4555
start-app = "src.main:main"
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
"""Tests for ContentProcess model."""
2+
3+
from unittest.mock import patch, MagicMock
4+
from libs.models.content_process import ContentProcess, Step_Outputs
5+
6+
7+
class TestStepOutputs:
8+
"""Tests for Step_Outputs class."""
9+
10+
def test_step_outputs_creation(self):
11+
"""Test creating Step_Outputs."""
12+
step = Step_Outputs(
13+
step_name="extract",
14+
processed_time="2026-01-01T00:00:00Z",
15+
step_result={"extracted": "data"}
16+
)
17+
assert step.step_name == "extract"
18+
assert step.step_result == {"extracted": "data"}
19+
20+
21+
class TestContentProcess:
22+
"""Tests for ContentProcess class."""
23+
24+
def test_content_process_creation(self):
25+
"""Test creating ContentProcess."""
26+
process = ContentProcess(
27+
process_id="test-123",
28+
status="processing"
29+
)
30+
assert process.process_id == "test-123"
31+
assert process.status == "processing"
32+
33+
@patch("libs.models.content_process.CosmosMongDBHelper")
34+
def test_update_process_status_to_cosmos_existing(self, mock_cosmos):
35+
"""Test updating existing process status in Cosmos."""
36+
mock_instance = MagicMock()
37+
mock_instance.find_document.return_value = [{"process_id": "test-123"}]
38+
mock_cosmos.return_value = mock_instance
39+
40+
process = ContentProcess(process_id="test-123", status="completed")
41+
process.update_process_status_to_cosmos(
42+
"connection_string",
43+
"database",
44+
"collection"
45+
)
46+
47+
mock_instance.find_document.assert_called_once()
48+
mock_instance.update_document.assert_called_once()
49+
50+
@patch("libs.models.content_process.CosmosMongDBHelper")
51+
def test_update_process_status_to_cosmos_new(self, mock_cosmos):
52+
"""Test inserting new process status in Cosmos."""
53+
mock_instance = MagicMock()
54+
mock_instance.find_document.return_value = []
55+
mock_cosmos.return_value = mock_instance
56+
57+
process = ContentProcess(process_id="test-123", status="processing")
58+
process.update_process_status_to_cosmos(
59+
"connection_string",
60+
"database",
61+
"collection"
62+
)
63+
64+
mock_instance.find_document.assert_called_once()
65+
mock_instance.insert_document.assert_called_once()
66+
67+
@patch("libs.models.content_process.CosmosMongDBHelper")
68+
def test_update_status_to_cosmos_existing(self, mock_cosmos):
69+
"""Test updating existing status in Cosmos."""
70+
mock_instance = MagicMock()
71+
mock_instance.find_document.return_value = [{"process_id": "test-123"}]
72+
mock_cosmos.return_value = mock_instance
73+
74+
process = ContentProcess(process_id="test-123", status="completed")
75+
process.update_status_to_cosmos(
76+
"connection_string",
77+
"database",
78+
"collection"
79+
)
80+
81+
mock_instance.find_document.assert_called_once()
82+
mock_instance.update_document.assert_called_once()
83+
84+
@patch("libs.models.content_process.CosmosMongDBHelper")
85+
def test_update_status_to_cosmos_new(self, mock_cosmos):
86+
"""Test inserting new status in Cosmos."""
87+
mock_instance = MagicMock()
88+
mock_instance.find_document.return_value = []
89+
mock_cosmos.return_value = mock_instance
90+
91+
process = ContentProcess(process_id="test-123", status="processing")
92+
process.update_status_to_cosmos(
93+
"connection_string",
94+
"database",
95+
"collection"
96+
)
97+
98+
mock_instance.find_document.assert_called_once()
99+
mock_instance.insert_document.assert_called_once()
Lines changed: 91 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import pytest
2-
from unittest.mock import Mock
2+
from unittest.mock import Mock, patch, MagicMock
33
from libs.pipeline.entities.pipeline_step_result import StepResult
44
from libs.pipeline.entities.pipeline_status import PipelineStatus
5+
from libs.pipeline.entities.pipeline_data import DataPipeline
6+
from libs.pipeline.entities.pipeline_file import ArtifactType
57

68

79
def test_update_step():
@@ -47,57 +49,6 @@ def test_get_previous_step_result():
4749
assert result is None
4850

4951

50-
# def test_save_to_persistent_storage(mocker):
51-
# # Mock the StorageBlobHelper.upload_text method
52-
# mock_upload_text = mocker.patch(
53-
# "libs.azure_helper.storage_blob.StorageBlobHelper.upload_text"
54-
# )
55-
56-
# # Mock the StorageBlobHelper constructor to return a mock instance
57-
# mock_storage_blob_helper = mocker.patch(
58-
# "libs.azure_helper.storage_blob.StorageBlobHelper", autospec=True
59-
# )
60-
# mock_storage_blob_helper_instance = mock_storage_blob_helper.return_value
61-
62-
# # Mock the create_container method on the container_client
63-
# mock_container_client = Mock()
64-
# mock_container_client.create_container = Mock()
65-
# mock_storage_blob_helper_instance._invalidate_container = Mock()
66-
# mock_storage_blob_helper_instance._invalidate_container.return_value = (
67-
# mock_container_client
68-
# )
69-
70-
# # Create a PipelineStatus object with a process_id
71-
# pipeline_status = PipelineStatus(process_id="123")
72-
73-
# # Mock the update_step method using pytest-mock
74-
# mock_update_step = mocker.patch.object(
75-
# PipelineStatus, "update_step", return_value=None
76-
# )
77-
78-
# # Mock the model_dump_json method using pytest-mock
79-
# mock_model_dump_json = mocker.patch.object(
80-
# PipelineStatus, "model_dump_json", return_value='{"key": "value"}'
81-
# )
82-
83-
# account_url = "https://example.com"
84-
# container_name = "container"
85-
86-
# # Call the save_to_persistent_storage method
87-
# pipeline_status.save_to_persistent_storage(account_url, container_name)
88-
89-
# # Assert that update_step was called once
90-
# mock_update_step.assert_called_once()
91-
92-
# # Assert that model_dump_json was called once
93-
# mock_model_dump_json.assert_called_once()
94-
95-
# # Assert that upload_text was called with the correct arguments
96-
# mock_upload_text.assert_called_once_with(
97-
# container_name="123", blob_name="process-status.json", text='{"key": "value"}'
98-
# )
99-
100-
10152
def test_save_to_persistent_storage_no_process_id():
10253
pipeline_status = PipelineStatus()
10354
with pytest.raises(ValueError, match="Process ID is required to save the result."):
@@ -115,3 +66,91 @@ def test_move_to_next_step():
11566
assert pipeline_status.completed_steps == ["step1", "step2"]
11667
assert pipeline_status.remaining_steps == []
11768
assert pipeline_status.completed is True
69+
70+
71+
# DataPipeline Tests
72+
class TestDataPipeline:
73+
"""Tests for DataPipeline class."""
74+
75+
def test_get_object_valid_json(self):
76+
"""Test parsing valid JSON string to DataPipeline."""
77+
json_str = '{"process_id": "test-123", "PipelineStatus": {"Completed": false}, "Files": []}'
78+
result = DataPipeline.get_object(json_str)
79+
assert result.process_id == "test-123"
80+
assert result.pipeline_status is not None
81+
82+
def test_get_object_invalid_json(self):
83+
"""Test that invalid JSON raises ValueError."""
84+
with pytest.raises(ValueError, match="Failed to parse"):
85+
DataPipeline.get_object("invalid json {")
86+
87+
def test_add_file(self):
88+
"""Test adding a file to the pipeline."""
89+
pipeline_status = PipelineStatus(process_id="test-123", active_step="step1")
90+
data_pipeline = DataPipeline(process_id="test-123", pipeline_status=pipeline_status)
91+
92+
file = data_pipeline.add_file("document.pdf", ArtifactType.SourceContent)
93+
94+
assert len(data_pipeline.files) == 1
95+
assert file.name == "document.pdf"
96+
assert file.artifact_type == ArtifactType.SourceContent
97+
assert file.processed_by == "step1"
98+
99+
def test_get_step_result(self):
100+
"""Test getting step result from DataPipeline."""
101+
pipeline_status = PipelineStatus(process_id="test-123")
102+
step_result = StepResult(step_name="extract", result={"data": "value"})
103+
pipeline_status.process_results.append(step_result)
104+
105+
data_pipeline = DataPipeline(process_id="test-123", pipeline_status=pipeline_status)
106+
107+
result = data_pipeline.get_step_result("extract")
108+
assert result == step_result
109+
110+
def test_get_previous_step_result(self):
111+
"""Test getting previous step result from DataPipeline."""
112+
pipeline_status = PipelineStatus(process_id="test-123", completed_steps=["step1"])
113+
step_result = StepResult(step_name="step1", result={"data": "value"})
114+
pipeline_status.process_results.append(step_result)
115+
116+
data_pipeline = DataPipeline(process_id="test-123", pipeline_status=pipeline_status)
117+
118+
result = data_pipeline.get_previous_step_result("step2")
119+
assert result == step_result
120+
121+
def test_get_source_files(self):
122+
"""Test getting source files from pipeline."""
123+
pipeline_status = PipelineStatus(process_id="test-123", active_step="step1")
124+
data_pipeline = DataPipeline(process_id="test-123", pipeline_status=pipeline_status)
125+
126+
# Add source file
127+
data_pipeline.add_file("source.pdf", ArtifactType.SourceContent)
128+
# Add extracted file
129+
data_pipeline.add_file("output.json", ArtifactType.ExtractedContent)
130+
131+
source_files = data_pipeline.get_source_files()
132+
133+
assert len(source_files) == 1
134+
assert source_files[0].name == "source.pdf"
135+
136+
def test_save_to_database_not_implemented(self):
137+
"""Test that save_to_database raises NotImplementedError."""
138+
pipeline_status = PipelineStatus(process_id="test-123")
139+
data_pipeline = DataPipeline(process_id="test-123", pipeline_status=pipeline_status)
140+
141+
with pytest.raises(NotImplementedError):
142+
data_pipeline.save_to_database()
143+
144+
@patch("libs.pipeline.entities.pipeline_data.StorageBlobHelper")
145+
def test_save_to_persistent_storage(self, mock_storage_helper):
146+
"""Test saving pipeline to persistent storage."""
147+
mock_instance = MagicMock()
148+
mock_storage_helper.return_value = mock_instance
149+
150+
pipeline_status = PipelineStatus(process_id="test-123")
151+
data_pipeline = DataPipeline(process_id="test-123", pipeline_status=pipeline_status)
152+
153+
data_pipeline.save_to_persistent_storage("https://storage.blob.core.windows.net", "container")
154+
155+
mock_storage_helper.assert_called_once()
156+
mock_instance.upload_text.assert_called_once()

0 commit comments

Comments
 (0)