452 lines
21 KiB
Python
452 lines
21 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""ksjsb.py 单文件脚本的行为锁定测试。
|
|
|
|
只 import ksjsb 一个项目文件(证明单文件自洽), 黄金值以字面量固化, 来自:
|
|
- 2026-08-27 五份抓包(签到/签到看视频/开宝箱/开宝箱看广告/看广告得金币):
|
|
URL query 键序编码、JSON body、真实 sig3 内嵌摘要字段(CRC 交叉验证)。
|
|
- core/ 算法模块的已知输出(10418/10400/xfalcon/H5 sig3/kww/kas 黄金值)。
|
|
|
|
ksjsb.py 运行时不读取 HAR, 也不依赖 main/core; 本测试同样只依赖 ksjsb。
|
|
"""
|
|
|
|
import io
|
|
import contextlib
|
|
import os
|
|
import unittest
|
|
import urllib.parse
|
|
|
|
from ksjsb import (
|
|
KsJsB,
|
|
build_h5_sign_input,
|
|
compact_json,
|
|
h5_sig3_digest_field,
|
|
parse_h5_sig3,
|
|
with_source,
|
|
)
|
|
|
|
# 抓包 cookie 中参与 sign_input 的子集(H5_COOKIE_KEYS, 非敏感设备字段)。
|
|
# 真实 WebView 对 document.cookie 的编码值(%28/+ 等)签名, 这里保持原样。
|
|
CAPTURED_COOKIE = (
|
|
"kpn=NEBULA; kpf=ANDROID_PHONE; userId=5402308233; did=ANDROID_f05497e9cef09a7f; "
|
|
"c=OPPO; appver=14.6.50.11735; language=zh-cn; mod=OnePlus%28PJZ110%29; did_tag=0; "
|
|
"egid=DFP280DA456E3BBF93A99727CB5A97D727E442E38694913A197D1C201C5CF982; "
|
|
"oDid=ANDROID_46a032e0a2af8184; androidApiLevel=36; newOc=OPPO; browseType=3; "
|
|
"socName=Qualcomm+Snapdragon+8750; ftt=; abi=arm64; userRecoBit=0; device_abi=arm64; "
|
|
"grant_browse_type=AUTHORIZED; iuid=; rdid=ANDROID_741de4351c44850d; "
|
|
"oaid=700ED68650EF49ECB686B2AAA9994C1Dca31c779ee5acb7d4c3b03337599a591"
|
|
)
|
|
|
|
# ---- 真实请求 query(去掉 __NS_sig3, 保留原始顺序) ----
|
|
SHOW_POPUP_QUERY = (
|
|
"sigCatVer=1&source=bottom_guide_first&ladderRedPackGuide=false"
|
|
"&bizParams=%7B%22source%22%3A%22bottom_guide_first%22%2C%22ladderRedPackGuide%22%3Afalse%7D"
|
|
)
|
|
REPORT_QUERY = (
|
|
"sigCatVer=1&eventTrackingLogInfo=%7B%22deliverOrderId%22%3A%225118%22%2C%22eventTrackingTaskId%22"
|
|
"%3A20022%2C%22resourceId%22%3A%22externalFeed_externalSignPopup%22%2C%22extParams%22%3A%7B%22signIn"
|
|
"ThresholdTaskType%22%3A0%2C%22autoSignInSuccess%22%3Afalse%2C%22source%22%3A%22bottom_guide_first%22"
|
|
"%7D%2C%22resourceFirstLevelName%22%3A%22EXTERNAL_FEED%22%2C%22resourceSecondLevelName%22%3A"
|
|
"%22EXTERNAL_SIGN_POPUP%22%7D"
|
|
)
|
|
TREASURE_INFO_QUERY = "sigCatVer=1&source=bottom_guide_first"
|
|
|
|
# ---- 真实 sig3 样本(摘要字段交叉验证) ----
|
|
CAPTURED_SIG3 = {
|
|
"show_popup": "4151162679c8e853bd1d741e19181571a0bed5eefa41a30358df0e0e08080b0a3515",
|
|
"sign_in_report": "9d8dcafaa514348f61c1a9c2c5c474fbd628113d269d7fdf1771d2d2d4d4d7d6e9c9",
|
|
"treasure_info": "5b4b0c3c63d2f249a70780040302289a4b2effd11e58b9193b6f1414121211102f0f",
|
|
"treasure_report": "8797d0e0bf0e2e957bdb5ed8dfde440eb392cd11c28465c56be7c8c8cececdccf3d3",
|
|
"matrix_treasure": "9181c6f6a91838836dcd4fcec9c883c4a4d83813d49273d389d3deded8d8dbdae5c5",
|
|
"matrix_task": "d0c087b7e85979c22c8c2c8f8889dcd94f6e33ba97d332924b7d9f9f99999a9ba484",
|
|
}
|
|
|
|
TREASURE_TRACKING = {
|
|
"eventTrackingTaskId": 20035,
|
|
"resourceId": "externalFeed_externalTreasureChestWidget",
|
|
"extParams": {"isServerRecordClickAction": True},
|
|
"deliverOrderId": "5128",
|
|
"resourceFirstLevelName": "EXTERNAL_FEED",
|
|
"resourceSecondLevelName": "EXTERNAL_TREASURE_CHEST_WIDGET",
|
|
}
|
|
TASKLIST_TREASURE_SLOT = {
|
|
"eventTrackingTaskId": 20022,
|
|
"resourceId": "externalFeed_externalTreasureChestWidget",
|
|
"extParams": {"isServerRecordClickAction": True},
|
|
"deliverOrderId": "5128",
|
|
"resourceFirstLevelName": "EXTERNAL_FEED",
|
|
"resourceSecondLevelName": "EXTERNAL_TREASURE_CHEST_WIDGET",
|
|
}
|
|
TASKLIST_AD_SLOT = {
|
|
"deliverOrderId": "422",
|
|
"materialKey": "TASK_LIST_17_672_CD_PROGRESSING",
|
|
"eventTrackingTaskId": 17,
|
|
"resourceId": "earnPage_taskList_1",
|
|
"extParams": {
|
|
"isServerRecordClickAction": True,
|
|
"businessPriceUniqueId": "EPuAgIDgi5CfChiuBCDBxpyQhDQo2O7EtgM=",
|
|
},
|
|
"resourceFirstLevelName": "EARN_PAGE",
|
|
"resourceSecondLevelName": "TASK_LIST",
|
|
}
|
|
CAPTURED_OAID = "700ED68650EF49ECB686B2AAA9994C1Dca31c779ee5acb7d4c3b03337599a591"
|
|
|
|
GOLDEN_TREASURE_BODY = (
|
|
'{"imei":"","idfa":"","oaid":"' + CAPTURED_OAID + '","eventTrackingLogInfo":"{\\"eventTrackingTaskId\\":20035,'
|
|
'\\"resourceId\\":\\"externalFeed_externalTreasureChestWidget\\",\\"extParams\\":{\\"isServerRecordClickAction\\":true,'
|
|
'\\"source\\":\\"bottom_guide_first\\"},\\"deliverOrderId\\":\\"5128\\",\\"resourceFirstLevelName\\":\\"EXTERNAL_FEED\\",'
|
|
'\\"resourceSecondLevelName\\":\\"EXTERNAL_TREASURE_CHEST_WIDGET\\"}"}'
|
|
)
|
|
GOLDEN_MATRIX_TREASURE_BODY = (
|
|
'{"actionType":1,"resourceSlotInfo":{"eventTrackingTaskId":20022,'
|
|
'"resourceId":"externalFeed_externalTreasureChestWidget","extParams":{"isServerRecordClickAction":true,'
|
|
'"threshold":0,"thresholdTaskId":0,"source":"bottom_guide_first"},"deliverOrderId":"5128",'
|
|
'"resourceFirstLevelName":"EXTERNAL_FEED","resourceSecondLevelName":"EXTERNAL_TREASURE_CHEST_WIDGET"}}'
|
|
)
|
|
GOLDEN_MATRIX_AD_BODY = (
|
|
'{"actionType":1,"resourceSlotInfo":{"deliverOrderId":"422","materialKey":"TASK_LIST_17_672_CD_PROGRESSING",'
|
|
'"eventTrackingTaskId":17,"resourceId":"earnPage_taskList_1","extParams":{"isServerRecordClickAction":true,'
|
|
'"businessPriceUniqueId":"EPuAgIDgi5CfChiuBCDBxpyQhDQo2O7EtgM=","source":"bottom_guide_first"},'
|
|
'"resourceFirstLevelName":"EARN_PAGE","resourceSecondLevelName":"TASK_LIST"}}'
|
|
)
|
|
|
|
|
|
def make_client() -> KsJsB:
|
|
return KsJsB(CAPTURED_COOKIE, "", timeout=5, dry_run=True)
|
|
|
|
|
|
def query_without_sig3(url: str) -> str:
|
|
return "&".join(p for p in url.split("?", 1)[1].split("&") if not p.startswith("__NS_sig3="))
|
|
|
|
|
|
class UrlAndBodyTests(unittest.TestCase):
|
|
def test_show_popup_query_matches_capture(self):
|
|
self.assertEqual(query_without_sig3(make_client().sign_in_show_popup().url), SHOW_POPUP_QUERY)
|
|
|
|
def test_sign_in_report_query_matches_capture(self):
|
|
tracking = {
|
|
"deliverOrderId": "5118",
|
|
"eventTrackingTaskId": 20022,
|
|
"resourceId": "externalFeed_externalSignPopup",
|
|
"extParams": {"signInThresholdTaskType": 0, "autoSignInSuccess": False},
|
|
"resourceFirstLevelName": "EXTERNAL_FEED",
|
|
"resourceSecondLevelName": "EXTERNAL_SIGN_POPUP",
|
|
}
|
|
self.assertEqual(query_without_sig3(make_client().sign_in_report(tracking).url), REPORT_QUERY)
|
|
|
|
def test_treasure_info_query_matches_capture(self):
|
|
self.assertEqual(query_without_sig3(make_client().treasure_box_info().url), TREASURE_INFO_QUERY)
|
|
|
|
def test_treasure_report_body_matches_capture(self):
|
|
self.assertEqual(
|
|
compact_json({
|
|
"imei": "",
|
|
"idfa": "",
|
|
"oaid": CAPTURED_OAID,
|
|
"eventTrackingLogInfo": compact_json(with_source(TREASURE_TRACKING)),
|
|
}),
|
|
GOLDEN_TREASURE_BODY,
|
|
)
|
|
record = make_client().treasure_box_report(TREASURE_TRACKING)
|
|
self.assertEqual(query_without_sig3(record.url), "sigCatVer=1")
|
|
|
|
def test_matrix_treasure_body_matches_capture(self):
|
|
resource = dict(TASKLIST_TREASURE_SLOT)
|
|
ext = dict(resource["extParams"])
|
|
ext.setdefault("threshold", 0)
|
|
ext.setdefault("thresholdTaskId", 0)
|
|
resource["extParams"] = ext
|
|
self.assertEqual(compact_json({"actionType": 1, "resourceSlotInfo": with_source(resource)}), GOLDEN_MATRIX_TREASURE_BODY)
|
|
|
|
def test_matrix_ad_body_matches_capture(self):
|
|
self.assertEqual(compact_json({"actionType": 1, "resourceSlotInfo": with_source(TASKLIST_AD_SLOT)}), GOLDEN_MATRIX_AD_BODY)
|
|
|
|
|
|
class Sig3CrossValidationTests(unittest.TestCase):
|
|
"""抓包 query/body + cookie 重建 sign_input, 摘要字段与真实 sig3 内嵌值一致。"""
|
|
|
|
def test_captured_digest_fields(self):
|
|
cases = {
|
|
"show_popup": (dict(urllib.parse.parse_qsl(SHOW_POPUP_QUERY, keep_blank_values=True)), None, "GET"),
|
|
"sign_in_report": (dict(urllib.parse.parse_qsl(REPORT_QUERY, keep_blank_values=True)), None, "GET"),
|
|
"treasure_info": (dict(urllib.parse.parse_qsl(TREASURE_INFO_QUERY, keep_blank_values=True)), None, "GET"),
|
|
"treasure_report": ({"sigCatVer": "1"}, GOLDEN_TREASURE_BODY, "POST"),
|
|
"matrix_treasure": ({"sigCatVer": "1"}, GOLDEN_MATRIX_TREASURE_BODY, "POST"),
|
|
"matrix_task": ({"sigCatVer": "1"}, GOLDEN_MATRIX_AD_BODY, "POST"),
|
|
}
|
|
cookie = dict(part.split("=", 1) for part in CAPTURED_COOKIE.split("; "))
|
|
for label, (query, body, method) in cases.items():
|
|
with self.subTest(label=label):
|
|
sign_input = build_h5_sign_input(cookie, query=query, body=body, method=method, request_type="json")
|
|
calc = h5_sig3_digest_field(sign_input)
|
|
captured = parse_h5_sig3(CAPTURED_SIG3[label])["digest_field"]
|
|
self.assertEqual(calc, captured)
|
|
|
|
def test_generated_sig3_roundtrip_and_counter_increments(self):
|
|
client = make_client()
|
|
query = dict(urllib.parse.parse_qsl(SHOW_POPUP_QUERY, keep_blank_values=True))
|
|
s1 = client._h5_sig3(query=query)
|
|
s2 = client._h5_sig3(query=query)
|
|
f1, f2 = parse_h5_sig3(s1), parse_h5_sig3(s2)
|
|
cookie = dict(part.split("=", 1) for part in CAPTURED_COOKIE.split("; "))
|
|
expected = h5_sig3_digest_field(build_h5_sign_input(cookie, query=query))
|
|
self.assertEqual(f1["digest_field"], expected)
|
|
self.assertEqual(f2["digest_field"], expected)
|
|
# 2026-08-27 抓包: counter 进程级单调递增(101->162)
|
|
self.assertEqual(f2["counter"], f1["counter"] + 1)
|
|
self.assertEqual(len(s1), 68)
|
|
|
|
|
|
class AlgorithmGoldenTests(unittest.TestCase):
|
|
"""内联算法黄金值(与已逆向 core 实现逐位一致的固化输出)。"""
|
|
|
|
def test_kwsg_10418_digest(self):
|
|
from ksjsb import KWSG_10418_SIG3_HMAC_KEY, KWSG_10418_SIG3_PREFIX_CODE, _kwsg_tables, kwsg_10418_digest24, kwsg_10418_prefix8
|
|
|
|
t1, t2 = _kwsg_tables("B")
|
|
got = kwsg_10418_digest24(
|
|
"test-input-中文-10418".encode(), 7, 1787813423, t1, t2,
|
|
prefix8=kwsg_10418_prefix8(KWSG_10418_SIG3_PREFIX_CODE, 0x0610FDA7),
|
|
hmac_key=KWSG_10418_SIG3_HMAC_KEY,
|
|
).hex()
|
|
self.assertEqual(got, "56473214b4ef0116181e1d1cecfaa8cc28d88a6e030f0117")
|
|
|
|
def test_kwsg_10400_raw(self):
|
|
from ksjsb import REWARD_SDK, _kwsg_tables, kwsg_10400_raw
|
|
|
|
t1, t2 = _kwsg_tables("A")
|
|
got = kwsg_10400_raw('{"x":"encData-黄金值"}'.encode(), REWARD_SDK, t1, t2, epoch_seconds=1787813423).hex()
|
|
self.assertEqual(
|
|
got,
|
|
"5a54eecde4d4ea6193f79db96e3254547f6c770401437d63823030fad0f3d66671d4550214754963c06e257b78857c73a4fff39f4b37ea8996c1b0878f69cef97898e2bebf872583",
|
|
)
|
|
|
|
def test_xfalcon_value(self):
|
|
from ksjsb import xfalcon_value_from_input_bytes
|
|
|
|
value = xfalcon_value_from_input_bytes(b"/rest/e/reward/mixed/adclient_key=2ac2a76dos=android")
|
|
self.assertTrue(value.startswith("HUDR_sFnX+n5uAUNVsMPNK3DOP5wnti1Lc8Axjy5z88T61A==$TE_"))
|
|
self.assertEqual(
|
|
value.split("$TE_")[1],
|
|
"1d029bfdfd210e0c6c7356210e0c6c7356575154566189cefaec418425e348f87e0fdf570c00688c2d0068ad56",
|
|
)
|
|
|
|
def test_kww_generation(self):
|
|
from ksjsb import kwf_generate_kww
|
|
|
|
kww = kwf_generate_kww(3, 1787813423000)
|
|
self.assertTrue(kww.startswith("PnGU+9+Y8008"))
|
|
self.assertEqual(
|
|
kww,
|
|
"PnGU+9+Y8008S+nH0U+0mjPf8fP08f+98f+nLlwnrIP9Pl+9rh+/WA+/mD+9PAPAHh+/Z7G/SD8e+DPBLh8fzS8/rU8/SYGAPAP/GMPBr7+0pSGn8DweDUGAPAPepDG/qAG9+0w/LlG/40+Bz0+nzDweLM+0P7GfzDPe4jG0LA+/P=",
|
|
)
|
|
|
|
def test_weapon_kas(self):
|
|
from ksjsb import weapon_proof_headers
|
|
|
|
headers = weapon_proof_headers("https://api.e.kuaishou.com/rest/e/reward/mixed/ad?__NS_sig3=abcd", "kaw-value")
|
|
self.assertEqual(headers["kas"], "00418873e6dae68db455e61dca1fdfd426")
|
|
|
|
def test_signed_api_url_sig3(self):
|
|
from ksjsb import Kwsg10418State, signed_api_url
|
|
|
|
state = Kwsg10418State(session_seed=0x0610FDA7, counter=0x60)
|
|
url = signed_api_url(
|
|
"/rest/e/reward/mixed/ad",
|
|
{"client_key": "2ac2a76d", "os": "android", "ud": "5402308233"},
|
|
[("encData", "AAA"), ("kuaishou.api_st", "ST")],
|
|
"salt",
|
|
state,
|
|
unix_time=1787813423,
|
|
)
|
|
self.assertIn("sig=4df442297f332ac4bce562f7b720229c", url)
|
|
self.assertIn("__NS_sig3=8d9ce9cf6f34dacda5c5c6c70c09b185f30351b5d8d4dacc", url)
|
|
self.assertIn("__NStokensig=7e2cf6b51f8e8b86ea855ee1ed3e0be550eba81d5ef7effc76a54216fb65f2fa", url)
|
|
self.assertIn("__NS_xfalcon=", url)
|
|
|
|
|
|
class ApiParamSetTests(unittest.TestCase):
|
|
"""API query 参数集与真机抓包逐键一致(2026-08-27 在线 6001 排查结论)。"""
|
|
|
|
CAPTURED_AD_QUERY_KEYS = [
|
|
"earphoneMode", "mod", "appver", "isp", "language", "ud", "did_tag", "egid",
|
|
"thermal", "net", "kcv", "app", "kpf", "bottom_navigation", "ver", "android_os",
|
|
"oDid", "boardPlatform", "kpn", "newOc", "androidApiLevel", "slh", "country_code",
|
|
"nbh", "hotfix_ver", "did_gt", "keyconfig_state", "cdid_tag", "sys", "max_memory",
|
|
"cold_launch_time_ms", "oc", "sh", "deviceBit", "browseType", "ddpi", "socName",
|
|
"is_background", "c", "sw", "ftt", "apptype", "abi", "userRecoBit", "device_abi",
|
|
"icaver", "totalMemory", "grant_browse_type", "iuid", "rdid", "sbh", "darkMode",
|
|
"did",
|
|
]
|
|
|
|
def test_ad_fetch_query_keys_match_capture(self):
|
|
client = make_client()
|
|
params = client._api_params()
|
|
# 真机广告请求: 上述设备键 + sig/__NS_sig3/__NS_xfalcon/__NStokensig
|
|
self.assertNotIn("sid", params) # 真机广告请求从不携带(2026-07 抓包实证)
|
|
self.assertNotIn("deviceName", params)
|
|
expected = set(self.CAPTURED_AD_QUERY_KEYS)
|
|
self.assertTrue(expected.issubset(params), f"缺失: {expected - set(params)}")
|
|
|
|
def test_uqatag_default_is_device_neutral_null_variant(self):
|
|
"""uQaTag 默认用 QoE 未采集回退形态(2026-07-08 抓包 result=1 接受)。"""
|
|
from ksjsb import API_COMMON_BODY
|
|
|
|
self.assertEqual(API_COMMON_BODY["uQaTag"], "1##DP:null#ecBl:00#ecPp:--#cmNt:-1#cmMnsl:-0")
|
|
client = make_client()
|
|
pairs = dict(client._api_body_common())
|
|
self.assertEqual(pairs["uQaTag"], API_COMMON_BODY["uQaTag"])
|
|
# 字段序对齐真机: cs, client_key, videoModelCrowdTag, os, api_st, uQaTag, token
|
|
self.assertEqual(
|
|
[k for k, _ in client._api_body_common()],
|
|
["cs", "client_key", "videoModelCrowdTag", "os", "kuaishou.api_st", "uQaTag", "token"],
|
|
)
|
|
|
|
|
|
class WithSourceTests(unittest.TestCase):
|
|
def test_appends_source_and_preserves_key_order(self):
|
|
tracking = dict(TREASURE_TRACKING)
|
|
out = with_source(tracking)
|
|
self.assertEqual(list(out.keys()), list(tracking.keys()))
|
|
self.assertEqual(list(out["extParams"].keys()), ["isServerRecordClickAction", "source"])
|
|
self.assertNotIn("source", tracking["extParams"])
|
|
|
|
|
|
class TaskSlotTests(unittest.TestCase):
|
|
def test_update_ad_contexts_extracts_slots_and_scene(self):
|
|
import base64 as b64
|
|
|
|
link_url = b64.b64encode(
|
|
compact_json({"pageId": 11101, "subPageId": 100026367, "posId": 24067, "businessId": 672}).encode()
|
|
).decode()
|
|
client = make_client()
|
|
client._update_ad_contexts({
|
|
"data": {
|
|
"dailyTasks": [
|
|
{"eventTrackingLogInfo": TASKLIST_AD_SLOT, "linkUrl": link_url, "extParam": {"descCountDown": 30000}},
|
|
{"extParam": {"eventTrackingLogInfo": TASKLIST_TREASURE_SLOT}},
|
|
]
|
|
}
|
|
})
|
|
self.assertEqual(client.task_ad_slot, TASKLIST_AD_SLOT)
|
|
self.assertEqual(client.task_treasure_slot, TASKLIST_TREASURE_SLOT)
|
|
self.assertEqual(client.ad_fetch_scenes["normal"], (11101, 100026367, 24067))
|
|
self.assertEqual(client.ad_task_countdown_ms["normal"], 30000)
|
|
|
|
def test_update_ad_contexts_extracts_hf_and_dsp_tasks(self):
|
|
import base64 as b64
|
|
|
|
goods_link = b64.b64encode(
|
|
compact_json({"pageId": 11101, "subPageId": 100074584, "posId": 96134, "businessId": 7038}).encode()
|
|
).decode()
|
|
drama_link = b64.b64encode(
|
|
compact_json({"pageId": 11101, "subPageId": 100134702, "posId": 175158, "businessId": 7052}).encode()
|
|
).decode()
|
|
walk_slot = {"materialKey": "TASK_LIST_20048_WALKING_NOT_REWARDED", "eventTrackingTaskId": 20048}
|
|
goods_slot = {"materialKey": "TASK_LIST_60151_PROGRESS", "eventTrackingTaskId": 60151}
|
|
search_slot = {"materialKey": "TASK_LIST_176_ACTIVE_PASSIVE_TASK_UNFINISH", "eventTrackingTaskId": 176}
|
|
dsp_slot = {"materialKey": "60494_TASK_LIST_EXCHANGE_DSP_1", "eventTrackingTaskId": 60494}
|
|
dsp_task = {
|
|
"name": "打开千问必得金币",
|
|
"desc": "千问智能解答,高效省时间",
|
|
"link": {"linkUrl": "tongyi://deeplink?x=1"},
|
|
"eventTrackingLogInfo": dsp_slot,
|
|
"extParam": {
|
|
"reportUrl": "/rest/n/nebula/exchange/dsp/report",
|
|
"adId": "2162", "appIdentifier": "com.aliyun.tongyi",
|
|
"downloadUrl": "https://aisite.wejianzhan.com/site/x",
|
|
"reqId": "dsctREQ", "impId": "imp-1", "bidId": None,
|
|
"adBidType": 1, "dspProvider": "happygame",
|
|
"implTrackersUrl": ["http://t/1"], "downEndTrackersUrl": None, "dplkTrackersUrl": None,
|
|
},
|
|
}
|
|
client = make_client()
|
|
client._update_ad_contexts({
|
|
"data": {
|
|
"dailyTasks": [
|
|
{"eventTrackingLogInfo": walk_slot},
|
|
# linkType=101 + pageId=11101 + businessId 7038 -> goods 场景(搜好货)
|
|
{"eventTrackingLogInfo": goods_slot, "linkUrl": goods_link, "linkType": 101, "name": "搜好货看广告"},
|
|
# 后出现的同形态任务(搜短剧 7052)覆盖 goods 场景(取最新)
|
|
{"eventTrackingLogInfo": search_slot, "linkType": 101, "linkUrl": drama_link, "name": "搜短剧看广告"},
|
|
dsp_task,
|
|
]
|
|
}
|
|
})
|
|
self.assertEqual(client.hf_task_slots["TASK_LIST_20048_"], walk_slot)
|
|
self.assertEqual(client.hf_task_slots["TASK_LIST_60151_"], goods_slot)
|
|
self.assertEqual(client.hf_task_slots["TASK_LIST_176_"], search_slot)
|
|
self.assertEqual(client.ad_fetch_scenes["goods"], (11101, 100134702, 175158))
|
|
self.assertEqual(client.ad_fetch_business["goods"], 7052)
|
|
self.assertEqual(client.goods_task_name, "搜短剧看广告")
|
|
self.assertEqual(len(client.dsp_tasks), 1)
|
|
self.assertEqual(client.dsp_tasks[0]["task"], dsp_task)
|
|
|
|
def test_dsp_body_assembly(self):
|
|
"""admInfo 拼装规则与 2026-08-27 抓包映射一致。"""
|
|
entry = {
|
|
"task": {
|
|
"name": "打开千问必得金币",
|
|
"desc": "千问智能解答,高效省时间",
|
|
"link": {"linkUrl": "tongyi://www.tongyi.cn/x?src_pkg=zslytylhpl"},
|
|
},
|
|
"ext": {
|
|
"adId": "2162", "appIdentifier": "com.aliyun.tongyi",
|
|
"downloadUrl": "https://aisite.wejianzhan.com/site/y",
|
|
"reqId": "dsctREQ", "impId": "imp-1", "bidId": None,
|
|
"adBidType": 1, "dspProvider": "happygame",
|
|
"implTrackersUrl": ["http://t/1"], "downEndTrackersUrl": None, "dplkTrackersUrl": None,
|
|
},
|
|
}
|
|
body = KsJsB.build_dsp_body(entry)
|
|
self.assertEqual(
|
|
list(body.keys()),
|
|
["reqId", "bidId", "impId", "admInfo", "adBidType", "dspProvider", "reportType", "downEndTrackersUrl", "dplkTrackersUrl", "implTrackersUrl"],
|
|
)
|
|
self.assertEqual(body["reportType"], 1)
|
|
self.assertEqual(
|
|
body["admInfo"],
|
|
{
|
|
"adId": "2162",
|
|
"title": "打开千问必得金币",
|
|
"subTitle": "千问智能解答,高效省时间",
|
|
"dpLink": "tongyi://www.tongyi.cn/x?src_pkg=zslytylhpl",
|
|
"h5Link": "https://aisite.wejianzhan.com/site/y",
|
|
"appIdentifier": "com.aliyun.tongyi",
|
|
},
|
|
)
|
|
|
|
|
|
class SequentialRunTests(unittest.TestCase):
|
|
def test_run_dry_run_completes_all_steps_without_network(self):
|
|
from ksjsb import run
|
|
|
|
env = {"ksck": f"test#{CAPTURED_COOKIE}#salt", "KS_DRY_RUN": "1", "KS_TIMEOUT": "5"}
|
|
buf = io.StringIO()
|
|
old = {k: os.environ.get(k) for k in env}
|
|
os.environ.update(env)
|
|
try:
|
|
with contextlib.redirect_stdout(buf):
|
|
code = run()
|
|
finally:
|
|
for k, v in old.items():
|
|
if v is None:
|
|
os.environ.pop(k, None)
|
|
else:
|
|
os.environ[k] = v
|
|
self.assertEqual(code, 0)
|
|
text = buf.getvalue()
|
|
for marker in (
|
|
"1/7 查询金币/余额", "3/7 签到", "4/7 开宝箱",
|
|
"5/7 高频任务", "6/7 看广告得金币", "7/7 搜好货看广告", # dry-run 下无任务列表, 标签为默认名
|
|
"最终余额", "任务执行完成",
|
|
):
|
|
self.assertIn(marker, text)
|
|
self.assertIn("开宝箱 dry-run method=POST", text)
|
|
self.assertIn("/rest/e/reward/mixed/ad", text)
|
|
self.assertIn("/rest/r/ad/task/report", text)
|
|
self.assertIn("搜好货广告拉取", text)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|