Organized

This commit is contained in:
Jason Thistlethwaite
2026-10-17 20:50:35 -04:00
parent bd26c8894f
commit 9ee9eb3ec6
130 changed files with 24172 additions and 140 deletions
+178
View File
@@ -0,0 +1,178 @@
"""End-to-end tests for the Redmine -> CRM sync pipeline."""
from __future__ import annotations
import json
import os
import tempfile
from pathlib import Path
from unittest import mock
import pytest
import responses
import contact_enrichment
import sync_redmine_contacts
from redmine_crm.crm_client import CrmClient
from redmine_crm.redmine_client import RedmineClient
BASE_URL = "https://redmine.example.com"
@responses.activate
def test_pipeline_full_run_creates_contact():
contacts = [
{
"id": 1,
"first_name": "Grace",
"last_name": "Hopper",
"email": "grace@example.com",
"updated_at": "2026-07-04T12:00:00Z",
}
]
responses.add(
responses.GET,
f"{BASE_URL}/contacts.json",
json={"contacts": contacts, "total_count": 1},
status=200,
)
with tempfile.TemporaryDirectory() as tmpdir:
state_file = Path(tmpdir) / "state.json"
redmine = RedmineClient(BASE_URL, "secret", project_id=1)
crm = CrmClient()
crm._store.clear()
state = sync_redmine_contacts.FileSyncState(state_file)
config = contact_enrichment.EnrichmentConfig()
result = sync_redmine_contacts.run_pipeline(
redmine=redmine,
crm=crm,
enrichment_config=config,
state=state,
batch_size=100,
dry_run=False,
)
assert result["metrics"]["fetched"] == 1
assert result["metrics"]["enriched"] == 1
assert result["metrics"]["created"] == 1
assert result["metrics"]["errors"] == 0
assert state.last_sync_timestamp is not None
@responses.activate
def test_dry_run_does_not_write_crm():
contacts = [
{
"id": 2,
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com",
"updated_at": "2026-07-04T12:00:00Z",
}
]
responses.add(
responses.GET,
f"{BASE_URL}/contacts.json",
json={"contacts": contacts, "total_count": 1},
status=200,
)
with tempfile.TemporaryDirectory() as tmpdir:
state_file = Path(tmpdir) / "state.json"
redmine = RedmineClient(BASE_URL, "secret", project_id=1)
crm = CrmClient()
crm._store.clear()
state = sync_redmine_contacts.FileSyncState(state_file)
result = sync_redmine_contacts.run_pipeline(
redmine=redmine,
crm=crm,
state=state,
batch_size=100,
dry_run=True,
)
assert result["dry_run"] is True
assert result["metrics"]["created"] == 0
assert not crm._store
@responses.activate
def test_pipeline_uses_last_sync_timestamp():
contacts = [
{
"id": 3,
"first_name": "Alan",
"last_name": "Turing",
"email": "alan@example.com",
"updated_at": "2026-07-05T12:00:00Z",
}
]
responses.add(
responses.GET,
f"{BASE_URL}/contacts.json",
json={"contacts": contacts, "total_count": 1},
status=200,
)
with tempfile.TemporaryDirectory() as tmpdir:
state_file = Path(tmpdir) / "state.json"
state_file.write_text(json.dumps({"last_sync_timestamp": "2026-07-04T00:00:00Z"}), encoding="utf-8")
redmine = RedmineClient(BASE_URL, "secret", project_id=1)
crm = CrmClient()
crm._store.clear()
state = sync_redmine_contacts.FileSyncState(state_file)
result = sync_redmine_contacts.run_pipeline(
redmine=redmine,
crm=crm,
state=state,
batch_size=100,
dry_run=False,
)
assert result["metrics"]["fetched"] == 1
request = responses.calls[0].request
assert "updated_on" in (request.url or "")
def test_cli_requires_api_key():
env = {"REDMINE_API_KEY": ""}
with mock.patch.dict(os.environ, env, clear=True):
with pytest.raises(RuntimeError, match="REDMINE_API_KEY"):
sync_redmine_contacts._redmine_client_from_args(
sync_redmine_contacts.build_argument_parser().parse_args(["--base-url", BASE_URL, "--dry-run"])
)
def test_file_sync_state_persists_and_reads():
with tempfile.TemporaryDirectory() as tmpdir:
state_file = Path(tmpdir) / "state.json"
state = sync_redmine_contacts.FileSyncState(state_file)
metrics = sync_redmine_contacts.SyncMetrics()
metrics.fetched = 5
state.record_run("2026-07-05T08:00:00Z", metrics)
new_state = sync_redmine_contacts.FileSyncState(state_file)
assert new_state.last_sync_timestamp == "2026-07-05T08:00:00Z"
assert new_state.last_metrics is not None
assert new_state.last_metrics["fetched"] == 5
def test_metrics_aggregate_counts():
metrics = sync_redmine_contacts.SyncMetrics()
metrics.fetched = 10
metrics.enriched = 9
metrics.skipped = 1
metrics.created = 5
metrics.updated = 3
metrics.unchanged = 1
metrics.errors = 0
metrics.finished_at = sync_redmine_contacts._utc_now_iso()
d = metrics.to_dict()
assert d["fetched"] == 10
assert d["enriched"] == 9
assert d["created"] + d["updated"] + d["unchanged"] == 9