feat(sp16): live MFT polling scheduler
Adds an asyncio-based background scheduler that polls the Gainwell MFT inbound path, downloads new files, and routes them through the appropriate parser (999 / 835 / 277CA / TA1). Idempotent (re-ticks and restarts skip already-processed files via the new processed_inbound_files table). Crash-safe (per-file try/except so one bad file doesn't stop the loop). Lifespan auto-configures from the seeded dzinesco clearhouse's SFTP block; auto-start is opt-in via CYCLONE_SCHEDULER_AUTOSTART. Five admin endpoints added: GET /api/admin/scheduler/status POST /api/admin/scheduler/start POST /api/admin/scheduler/stop POST /api/admin/scheduler/tick GET /api/admin/scheduler/processed-files?status=&limit= 20 new tests (15 unit + 5 API).
This commit is contained in:
@@ -72,7 +72,23 @@ def test_stub_get_secret_returns_stub_when_keychain_empty(sftp_block):
|
||||
assert secret == "<stub-secret>"
|
||||
|
||||
|
||||
def test_stub_read_file_raises(sftp_block):
|
||||
def test_stub_read_file_returns_bytes(sftp_block, tmp_path):
|
||||
"""SP16: the stub read_file returns bytes from the staging dir.
|
||||
|
||||
Lets the inbound scheduler exercise the same code path on a
|
||||
workstation without a real MFT connection.
|
||||
"""
|
||||
inbound = tmp_path / "staging" / "ToHPE"
|
||||
inbound.mkdir(parents=True)
|
||||
(inbound / "TP11525703-837P_M019048402-20260618130000000-1of1_TA1.x12").write_bytes(
|
||||
b"hello-world",
|
||||
)
|
||||
client = SftpClient(sftp_block)
|
||||
with pytest.raises(RuntimeError, match="Stub SFTP cannot read"):
|
||||
client.read_file("/x.x12")
|
||||
body = client.read_file("/ToHPE/TP11525703-837P_M019048402-20260618130000000-1of1_TA1.x12")
|
||||
assert body == b"hello-world"
|
||||
|
||||
|
||||
def test_stub_read_file_missing_raises(sftp_block):
|
||||
client = SftpClient(sftp_block)
|
||||
with pytest.raises(FileNotFoundError):
|
||||
client.read_file("/ToHPE/does-not-exist.x12")
|
||||
|
||||
Reference in New Issue
Block a user