diff --git a/backend/src/cyclone/store.py b/backend/src/cyclone/store.py index 81a654f..5079658 100644 --- a/backend/src/cyclone/store.py +++ b/backend/src/cyclone/store.py @@ -2302,6 +2302,49 @@ class CycloneStore: "updated_at": row.updated_at, }) + def update_clearhouse(self, block: Clearhouse) -> Clearhouse: + """Replace the singleton clearhouse row. SP25. + + Used by ``PATCH /api/clearhouse`` to flip ``sftp_block.stub`` + and adjust host/port/paths without touching SQLite directly. + Raises ``LookupError`` if the singleton row is missing — the + caller is expected to run the lifespan seed first. + """ + from cyclone.db import ClearhouseORM + with db.SessionLocal()() as s: + row = s.get(ClearhouseORM, 1) + if row is None: + raise LookupError( + "clearhouse singleton row missing; run the lifespan " + "seed (ensure_clearhouse_seeded) before PATCH /api/clearhouse" + ) + row.name = block.name + row.tpid = block.tpid + row.submitter_name = block.submitter_name + row.submitter_id_qual = block.submitter_id_qual + row.submitter_contact_name = block.submitter_contact_name + row.submitter_contact_email = block.submitter_contact_email + row.filename_block_json = json.loads( + json.dumps(block.filename_block.model_dump()) + ) + row.sftp_block_json = json.loads( + json.dumps(block.sftp_block.model_dump()) + ) + row.updated_at = datetime.now(timezone.utc).isoformat() + s.commit() + return Clearhouse.model_validate({ + "id": 1, + "name": row.name, + "tpid": row.tpid, + "submitter_name": row.submitter_name, + "submitter_id_qual": row.submitter_id_qual, + "submitter_contact_name": row.submitter_contact_name, + "submitter_contact_email": row.submitter_contact_email, + "filename_block": dict(row.filename_block_json), + "sftp_block": dict(row.sftp_block_json), + "updated_at": row.updated_at, + }) + def ensure_clearhouse_seeded(self) -> None: """Insert the default clearhouse singleton + 3 providers + CO_TXIX payer if they don't exist. Idempotent. Called from the API lifespan.""" diff --git a/backend/tests/test_store_update_clearhouse.py b/backend/tests/test_store_update_clearhouse.py new file mode 100644 index 0000000..51fe765 --- /dev/null +++ b/backend/tests/test_store_update_clearhouse.py @@ -0,0 +1,89 @@ +"""SP25 — store.update_clearhouse() round-trip test. + +The PATCH /api/clearhouse endpoint needs a way to replace the singleton +clearhouse row inside one session. This test exercises the method +directly against a fresh in-memory DB. +""" +from __future__ import annotations + +import pytest + +from cyclone import db +from cyclone.providers import Clearhouse, SftpBlock +from cyclone.store import store as cycl_store + + +def _seed_clearhouse(): + """Insert the default clearhouse row used by SP9's lifespan seed.""" + cycl_store.ensure_clearhouse_seeded() + + +def test_update_clearhouse_round_trip(): + """Replace all fields on the singleton row; GET returns the new values.""" + _seed_clearhouse() + + new_block = SftpBlock( + host="mft.gainwelltechnologies.com", + port=22, + username="colorado-fts\\coxix_prod_11525703", + paths={ + "outbound": "/CO XIX/PROD/coxix_prod_11525703/FromHPE", + "inbound": "/CO XIX/PROD/coxix_prod_11525703/ToHPE", + }, + stub=False, + staging_dir="./var/sftp/staging", + auth={"password_keychain_account": "sftp.gainwell.password"}, + ) + + new_clearhouse = Clearhouse.model_validate({ + "id": 1, + "name": "dzinesco", + "tpid": "11525703", + "submitter_name": "Dzinesco", + "submitter_id_qual": "46", + "submitter_contact_name": "Tyler Martinez", + "submitter_contact_email": "tyler@dzinesco.com", + "filename_block": { + "tz": "America/Denver", + "outbound_template": "{tpid}-{tx}-{ts_mt}-1of1.{ext}", + "inbound_template": "TP{tpid}-{orig_tx}_M{tracking}-{ts}-1of1_{file_type}.x12", + }, + "sftp_block": new_block.model_dump(), + "updated_at": "2026-06-24T00:00:00+00:00", + }) + + cycl_store.update_clearhouse(new_clearhouse) + + fetched = cycl_store.get_clearhouse() + assert fetched is not None + assert fetched.sftp_block.stub is False + assert fetched.sftp_block.host == "mft.gainwelltechnologies.com" + assert fetched.sftp_block.auth == {"password_keychain_account": "sftp.gainwell.password"} + + +def test_update_clearhouse_missing_row_raises(): + """If the singleton row was never seeded, update raises LookupError.""" + # Wipe the DB. + db._reset_for_tests() + db.init_db() + + new_block = SftpBlock( + host="mft.example.com", port=22, username="u", + paths={"outbound": "/o", "inbound": "/i"}, + stub=True, staging_dir="/tmp", auth={}, + ) + new_clearhouse = Clearhouse.model_validate({ + "id": 1, "name": "x", "tpid": "1", + "submitter_name": "X", "submitter_id_qual": "46", + "submitter_contact_name": "X", "submitter_contact_email": "x@x", + "filename_block": { + "tz": "America/Denver", + "outbound_template": "{tpid}-{tx}-{ts}-1of1.{ext}", + "inbound_template": "TP{tpid}-{tx}_{ts}.x12", + }, + "sftp_block": new_block.model_dump(), + "updated_at": "2026-06-24T00:00:00+00:00", + }) + + with pytest.raises(LookupError): + cycl_store.update_clearhouse(new_clearhouse)