"""Fully offline: the network is injected via a fake `getter`.""" import json import sys import os import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from agent_cv import (build_cv, verify_cv, all_verified, render_markdown, make_nonce, parse_nonce, verify_link, VERIFIED, CLAIMED) from agent_cv.model import CV def fake_getter(routes): """routes: {url: (status, json_obj)} -> a getter(url, headers).""" def getter(url, headers=None): if url in routes: status, obj = routes[url] return status, json.dumps(obj) return 404, '{"error":"not found"}' return getter REP_URL = "https://gridmolt.org/api/reputation/alice" class TestGridmoltProvider(unittest.TestCase): def test_verified_account_from_reputation(self): g = fake_getter({REP_URL: (200, {"username": "alice", "pushes": 10, "reuse": 24, "stars": 8, "reputation": 42})}) cv = build_cv("alice", getter=g) acc = cv.accounts[0] self.assertEqual(acc.platform, "gridmolt") self.assertEqual(acc.karma, 42) self.assertEqual(acc.verification, VERIFIED) self.assertEqual(acc.source, REP_URL) self.assertEqual(acc.karma_path, "reputation") self.assertEqual(acc.detail["reuse"], 24) self.assertEqual(cv.verified_total(), 42) class TestExternalTiers(unittest.TestCase): def test_external_with_source_and_valid_proof_is_verified(self): mb = "https://moltbook.com/api/users/alice-mb" g = fake_getter({REP_URL: (200, {"reputation": 42}), mb: (200, {"handle": "alice-mb", "karma": 87})}) nonce = make_nonce("alice", "moltbook") externals = [{ "platform": "moltbook", "handle": "alice-mb", "source": mb, "karma_path": "karma", "proof": {"nonce": nonce, "url": "https://moltbook.com/p/1", "method": "post-nonce"}, "proof_text": f"linking my gridmolt: {nonce}", "proof_author": "alice-mb", }] cv = build_cv("alice", externals, getter=g) mbacc = cv.accounts[1] self.assertEqual(mbacc.verification, VERIFIED) self.assertEqual(mbacc.karma, 87) self.assertEqual(cv.verified_total(), 42 + 87) def test_external_with_failed_proof_falls_back_to_claimed(self): mb = "https://moltbook.com/api/users/alice-mb" g = fake_getter({REP_URL: (200, {"reputation": 42}), mb: (200, {"karma": 87})}) nonce = make_nonce("alice", "moltbook") externals = [{ "platform": "moltbook", "handle": "alice-mb", "source": mb, "karma_path": "karma", "proof": {"nonce": nonce, "url": "https://moltbook.com/p/1"}, "proof_text": "wrong post, no nonce", "proof_author": "someone-else", }] cv = build_cv("alice", externals, getter=g) mbacc = cv.accounts[1] self.assertEqual(mbacc.verification, CLAIMED) self.assertIsNone(mbacc.source) # not presented as verifiable self.assertEqual(cv.verified_total(), 42) # claimed karma excluded def test_external_without_source_is_claimed(self): g = fake_getter({REP_URL: (200, {"reputation": 42})}) externals = [{"platform": "clawdfeed", "handle": "alice", "karma": 300}] cv = build_cv("alice", externals, getter=g) self.assertEqual(cv.accounts[1].verification, CLAIMED) self.assertEqual(cv.accounts[1].karma, 300) self.assertEqual(cv.verified_total(), 42) class TestVerify(unittest.TestCase): def _cv(self): g = fake_getter({REP_URL: (200, {"reputation": 42})}) return build_cv("alice", [{"platform": "clawdfeed", "handle": "alice", "karma": 300}], getter=g) def test_reverify_pass(self): cv = self._cv() g = fake_getter({REP_URL: (200, {"reputation": 42})}) results = verify_cv(cv, getter=g) by = {r["platform"]: r for r in results} self.assertEqual(by["gridmolt"]["status"], "verified") self.assertEqual(by["clawdfeed"]["status"], "unverified-claim") self.assertTrue(all_verified(results)) def test_reverify_detects_tampered_karma(self): cv = self._cv() cv.accounts[0].karma = 9999 # forge a bigger number g = fake_getter({REP_URL: (200, {"reputation": 42})}) # source still says 42 results = verify_cv(cv, getter=g) gm = next(r for r in results if r["platform"] == "gridmolt") self.assertEqual(gm["status"], "FAILED") self.assertFalse(all_verified(results)) def test_reverify_detects_dead_source(self): cv = self._cv() results = verify_cv(cv, getter=fake_getter({})) # 404 for everything gm = next(r for r in results if r["platform"] == "gridmolt") self.assertEqual(gm["status"], "FAILED") class TestProof(unittest.TestCase): def test_nonce_roundtrip(self): n = make_nonce("alice", "moltbook") self.assertEqual(parse_nonce(n), ("alice", "moltbook")) self.assertNotEqual(make_nonce("alice", "moltbook"), n) # random each time def test_verify_link(self): n = make_nonce("alice", "moltbook") self.assertTrue(verify_link(n, "alice-mb", f"hello {n} world", "alice-mb")) self.assertTrue(verify_link(n, "Alice-MB", f"{n}", "alice-mb")) # case-insensitive self.assertFalse(verify_link(n, "alice-mb", "no nonce here", "alice-mb")) self.assertFalse(verify_link(n, "alice-mb", f"{n}", "impostor")) class TestRender(unittest.TestCase): def test_sections_and_exclusion(self): g = fake_getter({REP_URL: (200, {"reputation": 42})}) cv = build_cv("alice", [{"platform": "clawdfeed", "handle": "alice", "karma": 300}], getter=g) md = render_markdown(cv) self.assertIn("Verified karma total: 42", md) # claimed 300 excluded self.assertIn("## ✅ Verified", md) self.assertIn("## ⚠️ Claimed (unverified)", md) self.assertIn("clawdfeed", md) class TestSerialization(unittest.TestCase): def test_roundtrip_and_canonical_stable(self): g = fake_getter({REP_URL: (200, {"reputation": 42})}) cv = build_cv("alice", getter=g) cv2 = CV.from_json(cv.to_json()) self.assertEqual(cv2.agent, "alice") self.assertEqual(cv2.accounts[0].karma, 42) self.assertEqual(cv.canonical(), cv2.canonical()) # deterministic bytes if __name__ == "__main__": unittest.main()