mirror of
https://github.com/coleam00/Archon.git
synced 2025-12-23 18:29:18 -05:00
Update unit tests
This commit is contained in:
@@ -63,20 +63,17 @@ class RepositoryConfigRepository:
|
||||
self._logger = logger.bind(table=self.table_name)
|
||||
self._logger.info("repository_config_repository_initialized")
|
||||
|
||||
def _row_to_model(self, row: dict[str, Any]) -> ConfiguredRepository | None:
|
||||
def _row_to_model(self, row: dict[str, Any]) -> ConfiguredRepository:
|
||||
"""Convert database row to ConfiguredRepository model
|
||||
|
||||
Args:
|
||||
row: Database row dictionary
|
||||
|
||||
Returns:
|
||||
ConfiguredRepository model instance, or None if row contains invalid enum values
|
||||
that cannot be converted (allows callers to skip invalid rows)
|
||||
ConfiguredRepository model instance
|
||||
|
||||
Note:
|
||||
Invalid enum values are logged but do not raise exceptions, allowing operations
|
||||
to continue with valid data. This prevents the entire table from becoming unreadable
|
||||
due to schema mismatches or corrupted data.
|
||||
Raises:
|
||||
ValueError: If row contains invalid enum values that cannot be converted
|
||||
"""
|
||||
repository_id = row.get("id", "unknown")
|
||||
|
||||
@@ -90,10 +87,11 @@ class RepositoryConfigRepository:
|
||||
repository_id=repository_id,
|
||||
invalid_commands=default_commands_raw,
|
||||
error=str(e),
|
||||
exc_info=True,
|
||||
action="Skipping invalid row - consider running data migration to fix enum values"
|
||||
exc_info=True
|
||||
)
|
||||
return None
|
||||
raise ValueError(
|
||||
f"Database contains invalid workflow steps for repository {repository_id}: {default_commands_raw}"
|
||||
) from e
|
||||
|
||||
# Convert default_sandbox_type from string to SandboxType enum
|
||||
sandbox_type_raw = row.get("default_sandbox_type", "git_worktree")
|
||||
@@ -105,10 +103,11 @@ class RepositoryConfigRepository:
|
||||
repository_id=repository_id,
|
||||
invalid_type=sandbox_type_raw,
|
||||
error=str(e),
|
||||
exc_info=True,
|
||||
action="Skipping invalid row - consider running data migration to fix enum values"
|
||||
exc_info=True
|
||||
)
|
||||
return None
|
||||
raise ValueError(
|
||||
f"Database contains invalid sandbox type for repository {repository_id}: {sandbox_type_raw}"
|
||||
) from e
|
||||
|
||||
return ConfiguredRepository(
|
||||
id=row["id"],
|
||||
@@ -137,22 +136,7 @@ class RepositoryConfigRepository:
|
||||
try:
|
||||
response = self.client.table(self.table_name).select("*").order("created_at", desc=True).execute()
|
||||
|
||||
repositories = []
|
||||
skipped_count = 0
|
||||
for row in response.data:
|
||||
repository = self._row_to_model(row)
|
||||
if repository is not None:
|
||||
repositories.append(repository)
|
||||
else:
|
||||
skipped_count += 1
|
||||
|
||||
if skipped_count > 0:
|
||||
self._logger.warning(
|
||||
"repositories_skipped_due_to_invalid_data",
|
||||
skipped_count=skipped_count,
|
||||
total_rows=len(response.data),
|
||||
valid_count=len(repositories)
|
||||
)
|
||||
repositories = [self._row_to_model(row) for row in response.data]
|
||||
|
||||
self._logger.info(
|
||||
"repositories_listed",
|
||||
@@ -175,10 +159,11 @@ class RepositoryConfigRepository:
|
||||
repository_id: UUID of the repository
|
||||
|
||||
Returns:
|
||||
ConfiguredRepository model or None if not found or if data is invalid
|
||||
ConfiguredRepository model or None if not found
|
||||
|
||||
Raises:
|
||||
Exception: If database query fails
|
||||
ValueError: If repository data contains invalid enum values
|
||||
"""
|
||||
try:
|
||||
response = self.client.table(self.table_name).select("*").eq("id", repository_id).execute()
|
||||
@@ -192,15 +177,6 @@ class RepositoryConfigRepository:
|
||||
|
||||
repository = self._row_to_model(response.data[0])
|
||||
|
||||
if repository is None:
|
||||
# Invalid enum values in database - treat as not found
|
||||
self._logger.warning(
|
||||
"repository_has_invalid_data",
|
||||
repository_id=repository_id,
|
||||
message="Repository exists but contains invalid enum values - consider data migration"
|
||||
)
|
||||
return None
|
||||
|
||||
self._logger.info(
|
||||
"repository_retrieved",
|
||||
repository_id=repository_id,
|
||||
@@ -257,16 +233,6 @@ class RepositoryConfigRepository:
|
||||
response = self.client.table(self.table_name).insert(data).execute()
|
||||
|
||||
repository = self._row_to_model(response.data[0])
|
||||
if repository is None:
|
||||
# This should not happen for newly created repositories with valid data
|
||||
# but handle defensively
|
||||
error_msg = "Failed to convert newly created repository to model - data corruption detected"
|
||||
self._logger.error(
|
||||
"repository_creation_model_conversion_failed",
|
||||
repository_url=repository_url,
|
||||
error=error_msg
|
||||
)
|
||||
raise ValueError(error_msg)
|
||||
|
||||
self._logger.info(
|
||||
"repository_created",
|
||||
@@ -331,18 +297,6 @@ class RepositoryConfigRepository:
|
||||
return None
|
||||
|
||||
repository = self._row_to_model(response.data[0])
|
||||
if repository is None:
|
||||
# Repository exists but has invalid enum values - cannot update
|
||||
error_msg = (
|
||||
f"Repository {repository_id} exists but contains invalid enum values. "
|
||||
"Cannot update - consider fixing data first via migration."
|
||||
)
|
||||
self._logger.error(
|
||||
"repository_update_failed_invalid_data",
|
||||
repository_id=repository_id,
|
||||
error=error_msg
|
||||
)
|
||||
raise ValueError(error_msg)
|
||||
|
||||
self._logger.info(
|
||||
"repository_updated",
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
"""Pytest configuration for agent_work_orders tests"""
|
||||
|
||||
import os
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
# Set ENABLE_AGENT_WORK_ORDERS=true for all tests so health endpoint populates dependencies
|
||||
os.environ.setdefault("ENABLE_AGENT_WORK_ORDERS", "true")
|
||||
|
||||
# Mock get_supabase_client before any modules import it
|
||||
# This prevents Supabase credential validation during test collection
|
||||
mock_client = MagicMock()
|
||||
mock_get_client = patch(
|
||||
"src.agent_work_orders.state_manager.repository_config_repository.get_supabase_client",
|
||||
return_value=mock_client
|
||||
)
|
||||
mock_get_client.start()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset_structlog():
|
||||
|
||||
@@ -157,12 +157,15 @@ def test_config_explicit_url_overrides_discovery_mode():
|
||||
def test_config_state_storage_type():
|
||||
"""Test STATE_STORAGE_TYPE configuration"""
|
||||
import os
|
||||
import importlib
|
||||
|
||||
# Temporarily set the environment variable
|
||||
old_value = os.environ.get("STATE_STORAGE_TYPE")
|
||||
os.environ["STATE_STORAGE_TYPE"] = "file"
|
||||
|
||||
try:
|
||||
import src.agent_work_orders.config as config_module
|
||||
importlib.reload(config_module)
|
||||
from src.agent_work_orders.config import AgentWorkOrdersConfig
|
||||
config = AgentWorkOrdersConfig()
|
||||
assert config.STATE_STORAGE_TYPE == "file"
|
||||
|
||||
@@ -46,6 +46,7 @@ def test_server_root_endpoint():
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("src.agent_work_orders.server.subprocess.run")
|
||||
@patch.dict("os.environ", {"ENABLE_AGENT_WORK_ORDERS": "true"})
|
||||
def test_health_check_claude_cli_available(mock_run):
|
||||
"""Test health check detects Claude CLI availability"""
|
||||
from src.agent_work_orders.server import app
|
||||
@@ -65,6 +66,7 @@ def test_health_check_claude_cli_available(mock_run):
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("src.agent_work_orders.server.subprocess.run")
|
||||
@patch.dict("os.environ", {"ENABLE_AGENT_WORK_ORDERS": "true"})
|
||||
def test_health_check_claude_cli_unavailable(mock_run):
|
||||
"""Test health check handles missing Claude CLI"""
|
||||
from src.agent_work_orders.server import app
|
||||
@@ -84,6 +86,7 @@ def test_health_check_claude_cli_unavailable(mock_run):
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("src.agent_work_orders.server.shutil.which")
|
||||
@patch.dict("os.environ", {"ENABLE_AGENT_WORK_ORDERS": "true"})
|
||||
def test_health_check_git_availability(mock_which):
|
||||
"""Test health check detects git availability"""
|
||||
from src.agent_work_orders.server import app
|
||||
@@ -102,7 +105,7 @@ def test_health_check_git_availability(mock_which):
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("src.agent_work_orders.server.httpx.AsyncClient")
|
||||
@patch.dict("os.environ", {"ARCHON_SERVER_URL": "http://localhost:8181"})
|
||||
@patch.dict("os.environ", {"ARCHON_SERVER_URL": "http://localhost:8181", "ENABLE_AGENT_WORK_ORDERS": "true"})
|
||||
async def test_health_check_server_connectivity(mock_client_class):
|
||||
"""Test health check validates server connectivity"""
|
||||
from src.agent_work_orders.server import health_check
|
||||
@@ -121,7 +124,7 @@ async def test_health_check_server_connectivity(mock_client_class):
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("src.agent_work_orders.server.httpx.AsyncClient")
|
||||
@patch.dict("os.environ", {"ARCHON_MCP_URL": "http://localhost:8051"})
|
||||
@patch.dict("os.environ", {"ARCHON_MCP_URL": "http://localhost:8051", "ENABLE_AGENT_WORK_ORDERS": "true"})
|
||||
async def test_health_check_mcp_connectivity(mock_client_class):
|
||||
"""Test health check validates MCP connectivity"""
|
||||
from src.agent_work_orders.server import health_check
|
||||
@@ -140,7 +143,7 @@ async def test_health_check_mcp_connectivity(mock_client_class):
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("src.agent_work_orders.server.httpx.AsyncClient")
|
||||
@patch.dict("os.environ", {"ARCHON_SERVER_URL": "http://localhost:8181"})
|
||||
@patch.dict("os.environ", {"ARCHON_SERVER_URL": "http://localhost:8181", "ENABLE_AGENT_WORK_ORDERS": "true"})
|
||||
async def test_health_check_server_unavailable(mock_client_class):
|
||||
"""Test health check handles unavailable server"""
|
||||
from src.agent_work_orders.server import health_check
|
||||
|
||||
@@ -61,6 +61,7 @@ async def test_execute_workflow_default_commands(mock_dependencies):
|
||||
with patch("src.agent_work_orders.workflow_engine.workflow_operations.run_create_branch_step") as mock_branch, \
|
||||
patch("src.agent_work_orders.workflow_engine.workflow_operations.run_planning_step") as mock_plan, \
|
||||
patch("src.agent_work_orders.workflow_engine.workflow_operations.run_execute_step") as mock_execute, \
|
||||
patch("src.agent_work_orders.workflow_engine.workflow_operations.run_review_step") as mock_review, \
|
||||
patch("src.agent_work_orders.workflow_engine.workflow_operations.run_commit_step") as mock_commit, \
|
||||
patch("src.agent_work_orders.workflow_engine.workflow_operations.run_create_pr_step") as mock_pr:
|
||||
|
||||
@@ -89,6 +90,14 @@ async def test_execute_workflow_default_commands(mock_dependencies):
|
||||
duration_seconds=30.0,
|
||||
)
|
||||
|
||||
mock_review.return_value = StepExecutionResult(
|
||||
step=WorkflowStep.REVIEW,
|
||||
agent_name="Reviewer",
|
||||
success=True,
|
||||
output="Review completed, all checks passed",
|
||||
duration_seconds=10.0,
|
||||
)
|
||||
|
||||
mock_commit.return_value = StepExecutionResult(
|
||||
step=WorkflowStep.COMMIT,
|
||||
agent_name="Committer",
|
||||
@@ -114,10 +123,11 @@ async def test_execute_workflow_default_commands(mock_dependencies):
|
||||
selected_commands=None, # Should use default
|
||||
)
|
||||
|
||||
# Verify all 5 default commands were executed
|
||||
# Verify all 6 default commands were executed in order
|
||||
assert mock_branch.called
|
||||
assert mock_plan.called
|
||||
assert mock_execute.called
|
||||
assert mock_review.called
|
||||
assert mock_commit.called
|
||||
assert mock_pr.called
|
||||
|
||||
|
||||
Reference in New Issue
Block a user