fix(missing-skus): reconcile stale false positives against Oracle

SKUs mapped externally (via SSH script or direct SQL) never triggered
resolve_missing_sku(), leaving them stuck as unresolved=0 indefinitely.
New reconcile_unresolved_missing_skus() revalidates ALL unresolved SKUs
against Oracle at sync, rescan, and CSV import time. Fail-soft on Oracle
down. Clears the 7 prod false positives on next sync or manual rescan.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-04-21 11:42:43 +00:00
parent 3bcb26b0bd
commit d15f8b085d
5 changed files with 117 additions and 2 deletions

View File

@@ -618,3 +618,79 @@ def test_get_all_skus():
]
skus = get_all_skus(orders)
assert skus == {"A", "B", "C"}
# ── reconcile_unresolved_missing_skus unit tests ──────────────────────────────
@pytest.mark.asyncio
async def test_reconcile_empty_unresolved():
"""reconcile returns zeros immediately when no unresolved SKUs exist."""
from app.services import sqlite_service, validation_service
# Ensure any previously tracked SKUs are resolved
db = await sqlite_service.get_sqlite()
try:
await db.execute("UPDATE missing_skus SET resolved = 1 WHERE resolved = 0")
await db.commit()
finally:
await db.close()
rec = await validation_service.reconcile_unresolved_missing_skus()
assert rec == {"checked": 0, "resolved": 0, "error": None}
@pytest.mark.asyncio
async def test_reconcile_oracle_down(monkeypatch):
"""reconcile is fail-soft: returns resolved=0 and error string when Oracle raises."""
from app.services import sqlite_service, validation_service
await sqlite_service.track_missing_sku("ORACLE_DOWN_SKU", "Test product")
def _raise(*args, **kwargs):
raise RuntimeError("Oracle unavailable")
monkeypatch.setattr(validation_service, "validate_skus", _raise)
rec = await validation_service.reconcile_unresolved_missing_skus()
assert rec["resolved"] == 0
assert rec["error"] is not None
assert "Oracle" in rec["error"] or "unavailable" in rec["error"]
# Cleanup
db = await sqlite_service.get_sqlite()
try:
await db.execute("DELETE FROM missing_skus WHERE sku = 'ORACLE_DOWN_SKU'")
await db.commit()
finally:
await db.close()
@pytest.mark.asyncio
async def test_reconcile_resolves_stale(monkeypatch):
"""reconcile marks resolved=1 for SKUs that validate_skus says are mapped."""
from app.services import sqlite_service, validation_service
await sqlite_service.track_missing_sku("STALE_MAPPED_SKU", "Stale product")
def _mock_validate(skus, conn=None, id_gestiuni=None):
return {
"mapped": {"STALE_MAPPED_SKU"},
"direct": set(),
"missing": set(),
"direct_id_map": {},
}
monkeypatch.setattr(validation_service, "validate_skus", _mock_validate)
rec = await validation_service.reconcile_unresolved_missing_skus()
assert rec["resolved"] >= 1
db = await sqlite_service.get_sqlite()
try:
cursor = await db.execute(
"SELECT resolved FROM missing_skus WHERE sku = 'STALE_MAPPED_SKU'"
)
row = await cursor.fetchone()
assert row is not None and row[0] == 1
finally:
await db.close()