93 lines
3.6 KiB
Python
93 lines
3.6 KiB
Python
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()
|