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:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user