fix: atomic upsert for import runs (Issue #12)

Replace check-then-insert race condition with INSERT ON CONFLICT.
- upsert_pending uses RETURNING id for atomic upsert
- UNIQUE(source_id, content_hash) constraint from migration 6
- blocklist_import_workflow updated to use upsert_pending
- test_import_source_success fixed for async mock patterns

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-05-02 23:39:43 +02:00
parent 1285bc8571
commit e436727942
11 changed files with 144 additions and 164 deletions

View File

@@ -14,6 +14,7 @@ from app.models.blocklist import (
ScheduleConfig,
ScheduleFrequency,
)
from app.repositories import import_log_repo
from app.services import blocklist_service
# ---------------------------------------------------------------------------
@@ -115,6 +116,27 @@ class TestSourceCRUD:
result = await blocklist_service.delete_source(db, 9999)
assert result is False
@patch("app.utils.ip_utils.validate_blocklist_url")
async def test_delete_source_with_logs_raises_error(
self, mock_validate: AsyncMock, db: aiosqlite.Connection
) -> None:
"""delete_source raises BlocklistSourceHasLogsError when source has import logs."""
mock_validate.return_value = None
source = await blocklist_service.create_source(db, "HasLogs", "https://haslogs.test/")
# Create an import log for this source
await import_log_repo.add_log(
db,
source_id=source.id,
source_url="https://haslogs.test/",
ips_imported=10,
ips_skipped=0,
errors=None,
)
from app.exceptions import BlocklistSourceHasLogsError
with pytest.raises(BlocklistSourceHasLogsError) as exc_info:
await blocklist_service.delete_source(db, source.id)
assert exc_info.value.source_id == source.id
# ---------------------------------------------------------------------------
# Preview