feat: replace Aider with secure mini-SWE-agent worker
Some checks failed
CI / test (push) Has been cancelled
CI / test (pull_request) Has been cancelled

This commit is contained in:
Christian Meyer
2026-08-31 02:46:58 +02:00
parent 582db3a654
commit 25ed6a8439
30 changed files with 4215 additions and 2148 deletions

View File

@@ -1,85 +1,83 @@
from dataclasses import replace
from __future__ import annotations
from test_config import settings
from unittest.mock import Mock
from lai_automation.git import ChangeSummary
from lai_automation.models import FailureReport
from lai_automation.policy import evaluate_changes, evaluate_report_scope
import pytest
from lai_automation.models import ChangeSummary
from lai_automation.policy import validate_changes
def test_policy_accepts_small_application_change() -> None:
result = evaluate_changes(
ChangeSummary(("app/Services/Example.php", "tests/Feature/ExampleTest.php"), 40, 5),
settings(),
)
assert result.allowed
def workspace_with_no_symlinks() -> Mock:
workspace = Mock()
workspace.has_symlink.return_value = False
workspace.has_path.return_value = False
return workspace
def test_policy_rejects_secrets_and_vendor_paths() -> None:
result = evaluate_changes(
ChangeSummary((".env", "vendor/foo.php", ".aider.tags.cache.v4/cache.db"), 3, 0),
settings(),
)
assert not result.allowed
assert len(result.errors) == 3
def test_policy_rejects_test_only_repair() -> None:
result = evaluate_changes(
ChangeSummary(("tests/Unit/ExampleTest.php",), 20, 2),
settings(),
def test_accepts_small_application_change(settings) -> None:
summary = ChangeSummary(
("app/Services/AIService.php", "tests/Feature/AITest.php"),
25,
4,
)
assert not result.allowed
assert "production behavior was not fixed" in result.errors[0]
validate_changes(workspace_with_no_symlinks(), summary, settings)
def test_direct_main_blocks_validator_and_migrations() -> None:
config = replace(
settings(),
mode="direct-main",
dry_run=False,
allow_direct_main=True,
gitea_token="token",
)
result = evaluate_changes(
ChangeSummary(("app/Services/SQLValidatorService.php", "database/migrations/new.php"), 10, 2),
config,
)
assert not result.allowed
@pytest.mark.parametrize(
"filename",
[
".env",
".env.example",
".gitea/workflows/ci.yml",
"docker-compose.yml",
"docker/php/Dockerfile",
"storage/logs/output.log",
"vendor/package/file.php",
],
)
def test_rejects_protected_paths(settings, filename: str) -> None:
summary = ChangeSummary((filename, "app/Fix.php"), 2, 0)
with pytest.raises(ValueError, match="Protected"):
validate_changes(workspace_with_no_symlinks(), summary, settings)
def test_policy_rejects_symlinks(tmp_path) -> None:
(tmp_path / "link.php").symlink_to("/etc/passwd")
result = evaluate_changes(ChangeSummary(("link.php",), 1, 0), settings(), tmp_path)
assert not result.allowed
def test_rejects_git_metadata_created_in_worktree(settings) -> None:
workspace = workspace_with_no_symlinks()
workspace.has_path.return_value = True
with pytest.raises(ValueError, match="Git metadata"):
validate_changes(
workspace,
ChangeSummary(("app/Fix.php",), 1, 0),
settings,
)
def test_cross_database_report_requires_application_orchestration() -> None:
report = FailureReport(
id=56,
chat_message_id=1,
source="user_feedback",
category="incorrect_answer",
user_feedback="Use cockpit_config.time_registration",
feedback_type="wrong_result",
expected_behavior="Join cockpit_config manager_id and FK_AuftragPos",
prompt="Hours in April",
response="0 hours",
sql_query="SELECT 0",
error_message=None,
context={},
def test_rejects_tests_without_application_change(settings) -> None:
summary = ChangeSummary(("tests/Feature/FixTest.php",), 20, 0)
with pytest.raises(ValueError, match="application source"):
validate_changes(workspace_with_no_symlinks(), summary, settings)
def test_rejects_symlink(settings) -> None:
workspace = workspace_with_no_symlinks()
workspace.has_symlink.return_value = True
summary = ChangeSummary(("app/Fix.php",), 1, 0)
with pytest.raises(ValueError, match="Symbolic links"):
validate_changes(workspace, summary, settings)
def test_rejects_oversized_diff(settings) -> None:
summary = ChangeSummary(
("app/Fix.php",),
settings.max_changed_lines + 1,
0,
)
rejected = evaluate_report_scope(
report,
ChangeSummary(("config/erp_semantics.php", "tests/Unit/FixTest.php"), 20, 2),
)
accepted = evaluate_report_scope(
report,
ChangeSummary(("app/Http/Controllers/ChatController.php", "tests/Unit/FixTest.php"), 20, 2),
)
assert not rejected.allowed
assert "staged orchestration" in rejected.errors[0]
assert accepted.allowed
with pytest.raises(ValueError, match="line limit"):
validate_changes(workspace_with_no_symlinks(), summary, settings)