| """Tests for the comprehensive admin analytics layer (informal-market science + |
| platform aggregation). Pure math is tested directly; _collect_platform_stats is |
| tested against a fake Firestore. Run: python test_admin_analytics.py""" |
| import os, sys, math |
| os.environ.setdefault("FIREBASE", "{}") |
| from unittest import mock |
|
|
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) |
|
|
| import analytics |
|
|
| PASS = FAIL = 0 |
| def check(desc, got, want): |
| global PASS, FAIL |
| ok = got == want |
| PASS += ok; FAIL += (not ok) |
| print(f"PASS {desc}" if ok else f"FAIL {desc}\n got={got!r} want={want!r}") |
| def approx(desc, got, want, tol=1e-6): |
| global PASS, FAIL |
| ok = got is not None and abs(got - want) <= tol |
| PASS += ok; FAIL += (not ok) |
| print(f"PASS {desc}" if ok else f"FAIL {desc}\n got={got!r} want≈{want!r}") |
| def check_true(desc, got): check(desc, bool(got), True) |
|
|
| print("== Gini coefficient ==") |
| check(" empty → None", analytics.gini_coefficient([]), None) |
| check(" all zero → None", analytics.gini_coefficient([0, 0, 0]), None) |
| check(" perfect equality → 0", analytics.gini_coefficient([5, 5, 5, 5]), 0.0) |
| |
| approx(" total concentration", analytics.gini_coefficient([0, 0, 0, 100]), 0.75, tol=0.001) |
| g = analytics.gini_coefficient([1, 2, 3, 4, 5]) |
| check_true(" mild inequality in (0,1)", 0 < g < 1) |
|
|
| print("== HHI concentration ==") |
| check(" monopoly", analytics.hhi_concentration({"a": 100}), 1.0) |
| approx(" 4 equal → 0.25", analytics.hhi_concentration({"a": 1, "b": 1, "c": 1, "d": 1}), 0.25, tol=1e-9) |
| check(" empty → None", analytics.hhi_concentration({}), None) |
|
|
| print("== top_share / median ==") |
| check(" top-10% of 10 equal earners = 10%", analytics.top_share([10]*10, 0.10), 10.0) |
| approx(" top earner dominates", analytics.top_share([1, 1, 1, 97], 0.25), 97.0, tol=0.5) |
| check(" median odd", analytics.median([3, 1, 2]), 2.0) |
| check(" median even", analytics.median([1, 2, 3, 4]), 2.5) |
|
|
| print("== price dispersion (law of one price) ==") |
| disp = analytics.price_dispersion({ |
| "tomato": [1.00, 1.00, 1.00], |
| "dovi": [2.0, 4.0, 6.0], |
| "rare": [5.0], |
| }) |
| check(" items compared (>=2 traders)", disp["itemsCompared"], 2) |
| check(" most dispersed first", disp["mostDispersed"][0]["item"], "dovi") |
| check(" tomato zero CV", [d for d in disp["mostDispersed"] if d["item"] == "tomato"][0]["cv"], 0.0) |
| check(" dovi spread%", disp["mostDispersed"][0]["spreadPct"], 100.0) |
|
|
| print("== scan_transactions ==") |
| txns = [ |
| {"transaction_type": "sale", "created_at": "2026-07-01T10:00:00Z", |
| "details": {"customer_credit": 5.0}}, |
| {"transaction_type": "sale", "created_at": "2026-07-02T10:00:00Z", "details": {}}, |
| {"transaction_type": "stock_in", "created_at": "2026-07-02T09:00:00Z", "details": {}}, |
| {"transaction_type": "expense", "created_at": "2026-07-03T09:00:00Z", "details": {}}, |
| ] |
| scan = analytics.scan_transactions(txns) |
| check(" type mix sale", scan["byType"]["sale"], 2) |
| check(" type mix stock_in", scan["byType"]["stock_in"], 1) |
| check(" total", scan["total"], 4) |
| check(" sale count", scan["saleCount"], 2) |
| check(" credit sale count", scan["creditSaleCount"], 1) |
| check(" credit value", scan["creditSalesValue"], 5.0) |
| check(" last activity", scan["lastActivity"][:10], "2026-07-03") |
| check(" daily buckets", scan["daily"]["2026-07-02"], 2) |
|
|
| print("== model_health accuracy-by-modality ==") |
| ex = [ |
| {"modality": "audio", "verdict": "confirmed", "task": "intent_parse"}, |
| {"modality": "audio", "verdict": "rejected", "task": "intent_parse"}, |
| {"modality": "text", "verdict": "confirmed", "task": "intent_parse"}, |
| {"modality": "text", "verdict": "confirmed", "task": "intent_parse"}, |
| ] |
| mh = analytics.model_health(ex) |
| check(" audio 50%", mh["accuracyByModality"]["audio"], 50.0) |
| check(" text 100%", mh["accuracyByModality"]["text"], 100.0) |
| check(" overall confirmRate", mh["confirmRate"], 75.0) |
|
|
| print("== report prompt + fallback ==") |
| prompt = analytics.build_admin_report_prompt({"platform": {"totalUsers": 3}}, "market", "2026-07-04T00:00:00Z") |
| check_true(" market focus present", "law of one price" in prompt) |
| check_true(" stats embedded", "totalUsers" in prompt) |
| fb = analytics.fallback_admin_report("full", "2026-07-04T00:00:00Z") |
| check(" fallback flags aiError", fb["aiError"], True) |
| check(" fallback keeps type", fb["reportType"], "full") |
|
|
|
|
| |
| print("== _collect_platform_stats (fake Firestore) ==") |
|
|
| class _Doc: |
| def __init__(self, data, _id="x"): self._d = data; self.id = _id |
| @property |
| def exists(self): return self._d is not None |
| def to_dict(self): return dict(self._d) if self._d else None |
|
|
| class _Col: |
| def __init__(self, docs=None, kv=None): self._docs = docs or []; self._kv = kv or {} |
| def stream(self): |
| if self._kv: |
| return [_Doc(v, k) for k, v in self._kv.items()] |
| return [_Doc(d, d.get("_id", "d")) for d in self._docs] |
| def where(self, *a, **k): return self |
| def limit(self, n): return self |
| def order_by(self, *a, **k): return self |
|
|
| class _UserDocRef: |
| def __init__(self, subs): self.subs = subs |
| def collection(self, name): return _Col(kv={} if name not in self.subs else None, |
| docs=self.subs.get(name, [])) |
|
|
| class _DB: |
| def __init__(self, users, ledgers, examples): |
| self.users = users; self.ledgers = ledgers; self.examples = examples |
| def collection(self, name): |
| if name == "users": |
| outer = self |
| class _UsersCol(_Col): |
| def __init__(s): super().__init__(docs=outer.users) |
| def document(s, pid): |
| return _UserDocRef(outer.ledgers.get(pid, {})) |
| return _UsersCol() |
| if name == "organizations": |
| return _Col(docs=[{"_id": "o1"}]) |
| if name == "distillation_examples": |
| return _Col(docs=self.examples) |
| if name == "admin_reports": |
| return _Col(docs=[]) |
| return _Col() |
|
|
| users = [ |
| {"_id": "u1", "email": "a@x.com", "displayName": "Rutendo", "phoneStatus": "approved", |
| "phone": "+263771000001", "defaultCurrency": "USD", "createdAt": "2026-06-01T00:00:00Z"}, |
| {"_id": "u2", "email": "b@x.com", "displayName": "Chipo", "phoneStatus": "approved", |
| "phone": "+263771000002", "defaultCurrency": "USD", "createdAt": "2026-06-02T00:00:00Z"}, |
| {"_id": "u3", "email": "c@x.com", "displayName": "Admin", "isAdmin": True, |
| "phoneStatus": "pending", "createdAt": "2026-06-03T00:00:00Z"}, |
| ] |
| from datetime import datetime, timezone, timedelta |
| recent = (datetime.now(timezone.utc) - timedelta(days=1)).isoformat() |
| ledgers = { |
| "263771000001": { |
| "transactions": [ |
| {"transaction_type": "sale", "created_at": recent, |
| "details": {"currency": "USD", "amount": 100.0, |
| "items": [{"item": "dovi", "quantity": 25, "price_per_unit": 4.0}]}}, |
| {"transaction_type": "sale", "created_at": recent, |
| "details": {"currency": "USD", "amount": 20.0, "customer_credit": 20.0, |
| "items": [{"item": "tomato", "quantity": 20, "price_per_unit": 1.0}]}}, |
| ], |
| "stock_batches": [{"name": "dovi", "quantity_remaining": 100, "cost_each": 2.0, |
| "price_each": 4.0, "stocked_at": "2026-06-01"}], |
| "customers": [{"name": "Tariro", "receivable": 20.0}], |
| "item_prices": [{"name": "dovi", "price": 4.0, "on_sale": False}, |
| {"name": "tomato", "price": 1.0, "on_sale": False}], |
| "services": [{"_id": "s1", "name": "braiding"}], |
| }, |
| "263771000002": { |
| "transactions": [ |
| {"transaction_type": "sale", "created_at": recent, |
| "details": {"currency": "USD", "amount": 10.0, |
| "items": [{"item": "dovi", "quantity": 5, "price_per_unit": 2.0}]}}, |
| ], |
| "stock_batches": [{"name": "dovi", "quantity_remaining": 10, "cost_each": 1.5, |
| "price_each": 2.0, "stocked_at": "2026-06-05"}], |
| "customers": [], |
| "item_prices": [{"name": "dovi", "price": 2.0, "on_sale": False}], |
| "services": [], |
| }, |
| } |
| examples = [{"modality": "audio", "verdict": "confirmed", "task": "asr"}, |
| {"modality": "text", "verdict": "confirmed", "task": "intent_parse"}] |
|
|
| fake_db = _DB(users, ledgers, examples) |
| with mock.patch.object(analytics, "money_opt", analytics.money_opt): |
| import importlib |
| with mock.patch.dict(os.environ, {"FIREBASE": "{}"}): |
| |
| with mock.patch("firebase_admin.credentials.Certificate", return_value=mock.MagicMock()), \ |
| mock.patch("firebase_admin.initialize_app", return_value=mock.MagicMock()), \ |
| mock.patch("firebase_admin.firestore.client", return_value=fake_db): |
| import main |
| main.db = fake_db |
|
|
| stats = main._collect_platform_stats() |
| print(" platform:", stats["platform"]) |
| print(" economy.byCurrency:", stats["economy"]["byCurrency"]) |
| print(" creditEconomy:", stats["informalMarketScience"]["creditEconomy"]) |
| print(" inequality:", stats["informalMarketScience"]["traderInequality"]) |
| print(" priceDiscovery:", stats["informalMarketScience"]["priceDiscovery"]) |
| check(" approved traders", stats["platform"]["approvedTraders"], 2) |
| check(" pending", stats["platform"]["pendingApproval"], 1) |
| check(" admins", stats["platform"]["admins"], 1) |
| check(" active 7d (recent txns)", stats["platform"]["activeTraders7d"], 2) |
| check(" USD sales summed", stats["economy"]["byCurrency"]["USD"]["sales"], 130.0) |
| check(" txn mix sale count", stats["economy"]["transactionMix"]["sale"], 3) |
| check(" receivables", stats["informalMarketScience"]["creditEconomy"]["receivables"], 20.0) |
| check_true(" credit-sale share computed", |
| stats["informalMarketScience"]["creditEconomy"]["creditSaleShareOfSalesTxns"] is not None) |
| check_true(" gini computed", stats["informalMarketScience"]["traderInequality"]["giniRevenue"] is not None) |
| |
| disp_items = [d["item"] for d in stats["informalMarketScience"]["priceDiscovery"]["mostDispersed"]] |
| check(" dovi price dispersion tracked", "dovi" in disp_items, True) |
| check(" services counted", stats["informalMarketScience"]["serviceEconomy"]["registeredServices"], 1) |
| check(" model health wired", stats["modelHealth"]["totalCaptured"], 2) |
| check(" stock retail > cost", stats["economy"]["stockValueRetail"] > stats["economy"]["stockValueCost"], True) |
|
|
| print(f"\n{'='*40}\nTOTAL: {PASS} passed, {FAIL} failed") |
| sys.exit(1 if FAIL else 0) |
|
|