Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: Test Workflow with Coverage

on:
push:
branches:
- main
- dev
paths:
- 'src/backend-api/**/*.py'
- 'src/backend-api/pyproject.toml'
- 'src/backend-api/pytest.ini'
- '.github/workflows/test.yml'
pull_request:
types:
- opened
- ready_for_review
- reopened
- synchronize
branches:
- main
- dev
paths:
- 'src/backend-api/**/*.py'
- 'src/backend-api/pyproject.toml'
- 'src/backend-api/pytest.ini'
- '.github/workflows/test.yml'

permissions:
contents: read
actions: read
pull-requests: write

jobs:
backend_tests:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Install Backend Dependencies
run: |
python -m pip install --upgrade pip
cd src/backend-api
pip install -e .
pip install pytest pytest-cov

- name: Check if Backend Test Files Exist
id: check_backend_tests
run: |
if [ -z "$(find src/backend-api/src/tests -type f -name 'test_*.py' 2>/dev/null)" ]; then
echo "No backend test files found, skipping backend tests."
echo "skip_backend_tests=true" >> $GITHUB_ENV
else
echo "Backend test files found, running tests."
echo "skip_backend_tests=false" >> $GITHUB_ENV
fi

- name: Run Backend Tests with Coverage
if: env.skip_backend_tests == 'false'
run: |
cd src/backend-api
PYTHONPATH=src/app pytest src/tests \
-c /dev/null \
--rootdir=. \
--cov=src/app \
--cov-report=term-missing \
--cov-report=xml:reports/coverage.xml \
--junitxml=pytest.xml \
-v

- name: Pytest Coverage Comment
if: |
always() &&
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.fork == false &&
env.skip_backend_tests == 'false'
uses: MishaKav/pytest-coverage-comment@26f986d2599c288bb62f623d29c2da98609e9cd4 # v1.6.0
with:
pytest-xml-coverage-path: src/backend-api/reports/coverage.xml
junitxml-path: src/backend-api/pytest.xml
report-only-changed-files: true

- name: Skip Backend Tests
if: env.skip_backend_tests == 'true'
run: |
echo "Skipping backend tests because no test files were found."
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,18 @@ def test_configuration_fields():
assert isinstance(config.app_sample_variable, str)
assert (
config.app_sample_variable
== "Application Template Sample Variable from App Configuration Store" # "Hello World!" # Default value from Configuration class
== "Hello World!" # Default value from Configuration class
)
# 'Application Template Sample Variable from App Configuration Store'
assert hasattr(config, "app_logging_enable")
assert isinstance(config.app_logging_enable, bool)
assert config.app_logging_enable is True
assert config.app_logging_enable is False # Default from Configuration class

assert hasattr(config, "azure_package_logging_level")
assert isinstance(config.azure_package_logging_level, str)
assert config.azure_package_logging_level == "INFO"
assert config.azure_package_logging_level == "WARNING" # Default from Configuration class

assert hasattr(config, "azure_logging_packages")
assert isinstance(config.azure_logging_packages, list)
assert config.azure_logging_packages == []
assert config.azure_logging_packages is None # Default from Configuration class

assert hasattr(config, "app_logging_level")
assert isinstance(config.app_logging_level, str)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ def test_application_context_configuration():
assert app_context.configuration is not None
assert (
app_context.configuration.app_sample_variable
== "Application Template Sample Variable from App Configuration Store"
) # "Hello World!"
== "Hello World!" # Default value when App Configuration Store is not connected
)


def test_application_context_credential():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ def test_app_context_dependency_injection():
assert app_context.is_registered(IHttpService)

# Test service resolution
data_service = app_context.get_typed_service(IDataService)
logger_service = app_context.get_typed_service(ILoggerService)
http_service = app_context.get_typed_service(IHttpService)
data_service = app_context.get_service(IDataService)
logger_service = app_context.get_service(ILoggerService)
http_service = app_context.get_service(IHttpService)

assert isinstance(data_service, InMemoryDataService)
assert isinstance(logger_service, ConsoleLoggerService)
Expand All @@ -42,8 +42,8 @@ def test_singleton_lifetime():
app_context = AppContext()
app_context.add_singleton(IDataService, InMemoryDataService)

instance1 = app_context.get_typed_service(IDataService)
instance2 = app_context.get_typed_service(IDataService)
instance1 = app_context.get_service(IDataService)
instance2 = app_context.get_service(IDataService)

assert instance1 is instance2
assert id(instance1) == id(instance2)
Expand All @@ -54,8 +54,8 @@ def test_transient_lifetime():
app_context = AppContext()
app_context.add_transient(IHttpService, HttpClientService)

instance1 = app_context.get_typed_service(IHttpService)
instance2 = app_context.get_typed_service(IHttpService)
instance1 = app_context.get_service(IHttpService)
instance2 = app_context.get_service(IHttpService)

assert instance1 is not instance2
assert id(instance1) != id(instance2)
Expand All @@ -77,7 +77,7 @@ def mock_factory():
app_context.add_singleton(ILoggerService, mock_factory)

# Get service and test
logger_service = app_context.get_typed_service(ILoggerService)
logger_service = app_context.get_service(ILoggerService)
assert logger_service is mock_logger

# Test mock functionality
Expand All @@ -90,7 +90,7 @@ def test_service_not_registered():
app_context = AppContext()

with pytest.raises(KeyError, match="Service IDataService is not registered"):
app_context.get_typed_service(IDataService)
app_context.get_service(IDataService)


def test_get_registered_services():
Expand Down Expand Up @@ -128,7 +128,7 @@ def test_factory_registration():
# Register with factory function
app_context.add_singleton(IDataService, lambda: InMemoryDataService())

data_service = app_context.get_typed_service(IDataService)
data_service = app_context.get_service(IDataService)
assert isinstance(data_service, InMemoryDataService)


Expand All @@ -143,7 +143,7 @@ def test_instance_registration():
app_context.add_singleton(IDataService, existing_instance)

# Get service
retrieved_instance = app_context.get_typed_service(IDataService)
retrieved_instance = app_context.get_service(IDataService)

assert retrieved_instance is existing_instance

Expand All @@ -161,11 +161,11 @@ def test_concrete_class_registration():
assert app_context.is_registered(HttpClientService)

# Test service resolution
data_service1 = app_context.get_typed_service(InMemoryDataService)
data_service2 = app_context.get_typed_service(InMemoryDataService)
data_service1 = app_context.get_service(InMemoryDataService)
data_service2 = app_context.get_service(InMemoryDataService)

http_service1 = app_context.get_typed_service(HttpClientService)
http_service2 = app_context.get_typed_service(HttpClientService)
http_service1 = app_context.get_service(HttpClientService)
http_service2 = app_context.get_service(HttpClientService)

# Test types
assert isinstance(data_service1, InMemoryDataService)
Expand Down
26 changes: 18 additions & 8 deletions src/backend-api/src/tests/model/test_model_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

sys.path.append(os.path.join(os.path.dirname(__file__), "..", ".."))

from routers.model.model_process import FileInfo, enlist_process_queue_response # noqa: E402
from routers.models.files import FileInfo # noqa: E402
from routers.models.processes import enlist_process_queue_response # noqa: E402


class TestFileInfo:
Expand Down Expand Up @@ -100,9 +101,10 @@ def test_response_creation(self):
]

response = enlist_process_queue_response(
user_id="test-user",
message="Files uploaded successfully",
process_id="123e4567-e89b-12d3-a456-426614174000",
files=files,
files=[f.model_dump() for f in files],
)

assert response.message == "Files uploaded successfully"
Expand All @@ -121,7 +123,8 @@ def test_to_base64_basic(self):
]

response = enlist_process_queue_response(
message="Test message", process_id="test-id", files=files
user_id="test-user",
message="Test message", process_id="test-id", files=[f.model_dump() for f in files]
)

# Test the to_base64 method
Expand Down Expand Up @@ -149,7 +152,8 @@ def test_to_base64_content_exclusion(self):
]

response = enlist_process_queue_response(
message="Files with secret content", process_id="secret-test", files=files
user_id="test-user",
message="Files with secret content", process_id="secret-test", files=[f.model_dump() for f in files]
)

# Get base64 and decode
Expand Down Expand Up @@ -187,9 +191,10 @@ def test_to_base64_multiple_files(self):
]

response = enlist_process_queue_response(
user_id="test-user",
message="Multiple EKS files uploaded",
process_id="eks-migration-123",
files=files,
files=[f.model_dump() for f in files],
)

base64_result = response.to_base64()
Expand Down Expand Up @@ -225,6 +230,7 @@ def test_to_base64_multiple_files(self):
def test_to_base64_empty_files_list(self):
"""Test to_base64 with empty files list"""
response = enlist_process_queue_response(
user_id="test-user",
message="No files uploaded", process_id="empty-process", files=[]
)

Expand All @@ -248,9 +254,10 @@ def test_to_base64_special_characters(self):
]

response = enlist_process_queue_response(
user_id="test-user",
message="Special chars: éñüíçødé",
process_id="unicode-test-123",
files=files,
files=[f.model_dump() for f in files],
)

# Should handle special characters without error
Expand Down Expand Up @@ -278,7 +285,8 @@ def test_to_base64_large_response(self):
)

response = enlist_process_queue_response(
message="Bulk file upload", process_id="bulk-upload-test", files=files
user_id="test-user",
message="Bulk file upload", process_id="bulk-upload-test", files=[f.model_dump() for f in files]
)

base64_result = response.to_base64()
Expand Down Expand Up @@ -306,6 +314,7 @@ def test_to_base64_large_response(self):
def test_to_base64_edge_cases(self, message, process_id):
"""Test to_base64 with edge cases"""
response = enlist_process_queue_response(
user_id="test-user",
message=message, process_id=process_id, files=[]
)

Expand Down Expand Up @@ -353,9 +362,10 @@ def test_eks_migration_scenario(self):
]

response = enlist_process_queue_response(
user_id="test-user",
message="EKS migration files processed successfully",
process_id="eks-migration-d173cea5-b1a5-4fab-929c-7379165cc96e",
files=eks_files,
files=[f.model_dump() for f in eks_files],
)

# Convert to base64 for queue message
Expand Down
Loading