"use strict"; const assert = require("assert"); const fs = require("fs"); const path = require("path"); const toDataModuleUrl = (source) => `data:text/javascript;base64,${Buffer.from(source).toString("base64")}`; const run = async () => { const rawSource = fs.readFileSync(path.join(__dirname, "../utils/api.js"), "utf8"); const moduleBody = rawSource.slice(rawSource.indexOf("const successCodes")); const savedTokens = []; const prelude = ` const currentUser = {}; const genealogies = []; const publicGenealogies = []; const treeMembers = []; const notifications = []; const joinApplications = []; const listFamilyFeedFixtures = () => []; const listFamilyArticleFixtures = () => []; const listFamilyAlbumFixtures = () => []; const listCeremonyFixtures = () => []; const listGrowthRecordFixtures = () => []; const listNotificationFixtures = () => []; const runtimeConfig = { mode: "remote", baseUrl: "https://backend-api.ddxcjp.cn", clientId: "client-1", tenantId: "000000", }; const hasRemoteConfig = () => true; const resolveRuntimeMode = () => "remote"; const AUTH_TAC_SCENE = Object.freeze({ SMS_LOGIN: "APP_SMS_LOGIN", REGISTER: "APP_REGISTER", FORGOT_PASSWORD: "APP_FORGOT_PASSWORD", }); const assertSmsCode = (value) => { if (typeof value !== "string" || !/^\\d{4}$/.test(value)) throw new Error("请输入 4 位短信验证码"); return value; }; const GENEALOGY_ACCESS_PRESET = Object.freeze({ MEMBER_ONLY: "MEMBER_ONLY" }); const session = { getToken: () => "", saveToken: (token) => globalThis.__savedTokens.push(token), }; `; globalThis.__savedTokens = savedTokens; const requests = []; let nextResponse = null; let holdResponse = false; globalThis.uni = { request(options) { requests.push(options); const task = { aborted: false, abort() { this.aborted = true; options.fail({ errMsg: "request:fail abort" }); }, }; options.__task = task; if (!holdResponse) queueMicrotask(() => options.success(nextResponse)); return task; }, }; const { appApi, createRequestController } = await import( toDataModuleUrl(`${prelude}\n${moduleBody}`) ); const respond = (response) => { nextResponse = response; }; const sendSms = () => appApi.sendSmsCode({ sceneCode: "APP_REGISTER", phone: "13800138000", validToken: "ticket-1", }); for (const invalid of [ { statusCode: 200, data: null }, { statusCode: 200, data: "ok" }, { statusCode: 200, data: {} }, { statusCode: 200, data: { code: "200", data: null } }, { statusCode: 200, data: { code: null, data: null } }, { statusCode: 200, data: { code: false, data: null } }, { statusCode: 204, data: { code: 200, msg: "成功", data: null } }, ]) { respond(invalid); await assert.rejects(sendSms(), /认证服务|响应|200/); } respond({ statusCode: 500, data: null }); await assert.rejects(sendSms(), /500/); respond({ statusCode: 200, data: { code: 500, msg: "业务拒绝", data: null } }); await assert.rejects(sendSms(), /业务拒绝/); respond({ statusCode: 200, data: { code: 200, msg: "操作成功", data: null } }); assert.strictEqual(await sendSms(), null, "合法 RVoid 必须精确解析为 null"); respond({ statusCode: 200, data: { code: 200 } }); assert.strictEqual(await sendSms(), null, "RVoid 未声明 data 必填,省略 data 仍必须解析为 null"); assert.strictEqual(requests.at(-1).header.clientid, "client-1"); respond({ statusCode: 200, data: null }); await assert.rejects( appApi.resetPassword({ phone: "13800138000", passwordHash: "a".repeat(32), smsCode: "1234", }), /认证服务|响应/, ); respond({ statusCode: 200, data: { code: 200 } }); assert.strictEqual( await appApi.resetPassword({ phone: "13800138000", passwordHash: "a".repeat(32), smsCode: "1234", }), null, "找回密码的 RVoid 省略 data 时仍必须解析为 null", ); respond({ statusCode: 200, data: { code: 200, msg: "操作成功", data: { access_token: "token-1" } }, }); const login = await appApi.loginWithSms({ phone: "13800138000", smsCode: "1234" }); assert.strictEqual(login.access_token, "token-1"); assert.deepStrictEqual(savedTokens, ["token-1"]); respond({ statusCode: 200, data: { code: 200, msg: "操作成功", data: { access_token: "token-password" } }, }); const passwordLogin = await appApi.loginWithPassword({ phone: "13800138000", passwordHash: "a".repeat(32), }); assert.strictEqual(passwordLogin.access_token, "token-password"); const passwordRequest = requests.at(-1); assert.strictEqual(passwordRequest.url, "https://backend-api.ddxcjp.cn/genealogy/app/auth/login"); assert.strictEqual(passwordRequest.method, "POST"); assert.strictEqual(passwordRequest.header.clientid, "client-1"); assert.strictEqual(passwordRequest.header.Authorization, undefined); assert.deepStrictEqual(passwordRequest.data, { clientId: "client-1", tenantId: "000000", grantType: "password", phone: "13800138000", password: "a".repeat(32), }); assert.deepStrictEqual(savedTokens, ["token-1", "token-password"]); holdResponse = true; const requestController = createRequestController(); const cancelled = appApi.sendSmsCode( { sceneCode: "APP_REGISTER", phone: "13800138000", validToken: "ticket-1", }, { requestController }, ); const activeRequest = requests.at(-1); const activeTask = activeRequest.__task; assert.strictEqual(activeRequest.timeout, 15000, "认证请求必须限制弱网等待时间"); requestController.abort(); await assert.rejects(cancelled, (error) => error.code === "REQUEST_CANCELLED"); assert.strictEqual(activeTask.aborted, true, "页面离开时必须真正中止 RequestTask"); delete globalThis.uni; delete globalThis.__savedTokens; process.stdout.write("AUTH-API-RUNTIME-SMOKE PASS\n"); }; run().catch((error) => { process.stderr.write(`${error.stack || error.message}\n`); process.exit(1); });