import shutil import json import os import subprocess import unittest from core.h5_kww import H5KwwGenerator, PureH5KwwGenerator @unittest.skipIf(shutil.which("node") is None, "node is required for KWF VM bridge") class H5KwwTests(unittest.TestCase): def test_pure_generator_matches_deterministic_bridge_sequence(self): generator = PureH5KwwGenerator(start_collect_count=1, now_ms=1780000000123) first = generator.get() second = generator.get() self.assertEqual(first.kwfcv1, "2") self.assertEqual(second.kwfcv1, "3") self.assertEqual(first.kwfv1, first.kww) self.assertEqual(second.kwfv1, second.kww) self.assertEqual(first.kww[:87], second.kww[:87]) self.assertTrue(first.kww.startswith("PnGU+9+Y8008S+nH")) def test_generator_returns_pngu_kww_and_preserves_counter(self): generator = H5KwwGenerator(timeout=10) try: first = generator.get( url="https://nebula.kuaishou.com/rest/wd/encourage/unionTask/signIn/resource?sigCatVer=1", method="GET", cookie="kpn=NEBULA; kpf=ANDROID_PHONE", ) second = generator.get( url="https://nebula.kuaishou.com/rest/n/nebula/activity/earn/overview/tasks", method="GET", cookie="kpn=NEBULA; kpf=ANDROID_PHONE", ) finally: generator.close() self.assertEqual(len(first.kww), 174) self.assertEqual(len(second.kww), 174) self.assertTrue(first.kww.startswith("PnGU+9+Y8008S+nH")) self.assertTrue(second.kww.startswith("PnGU+9+Y8008S+nH")) self.assertEqual(first.kwfv1, first.kww) self.assertEqual(second.kwfv1, second.kww) self.assertGreater(int(second.kwfcv1), int(first.kwfcv1)) def test_server_deterministic_trace_dependency_surface(self): env = os.environ.copy() env["KS_H5_KWW_SEED"] = "20260712" env["KS_H5_KWW_NOW"] = "1780000000123" request = { "id": 1, "url": "https://nebula.kuaishou.com/rest/n/nebula/activity/earn/overview/tasks", "method": "GET", "cookie": "kpn=NEBULA", "trace": True, } proc = subprocess.run( ["node", "core/h5_kww_server.mjs"], input=json.dumps(request, separators=(",", ":")) + "\n", text=True, capture_output=True, encoding="utf-8", timeout=10, env=env, check=False, ) self.assertEqual(proc.returncode, 0, proc.stderr) data = json.loads(proc.stdout) event_types = [item["type"] for item in data["trace"]] self.assertEqual(len(data["kww"]), 174) self.assertEqual(data["kwfcv1"], "2") self.assertEqual(event_types.count("random"), 2) self.assertEqual(event_types.count("String.fromCharCode"), 1) self.assertEqual(event_types.count("localStorage.getItem"), 1) self.assertEqual(event_types.count("localStorage.setItem"), 2) self.assertIn("Object.assign", event_types) self.assertGreaterEqual(event_types.count("now"), 2) self.assertGreater(data["traceSummary"]["Math.floor"], event_types.count("Math.floor")) storage_events = [ item for item in data["trace"] if item["type"].startswith("localStorage.") ] self.assertEqual(storage_events[0]["key"], "kwfcv1") self.assertEqual(storage_events[1]["key"], "kwfcv1") self.assertEqual(storage_events[2]["key"], "kwfv1") if __name__ == "__main__": unittest.main()