完成50%

This commit is contained in:
2026-07-23 08:23:59 +08:00
parent 9b0ad62df4
commit f1edc6b533
218 changed files with 24318 additions and 5514 deletions
+202
View File
@@ -0,0 +1,202 @@
"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 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 = () => globalThis.__runtimeMode === "remote";
const resolveRuntimeMode = () => {
if (globalThis.__runtimeMode === "mock") return "mock";
if (globalThis.__runtimeMode === "remote") return "remote";
throw new Error("运行模式配置无效:只允许 mock 或配置完整的 remote");
};
const AUTH_TAC_SCENE = Object.freeze({
SMS_LOGIN: "APP_SMS_LOGIN",
REGISTER: "APP_REGISTER",
FORGOT_PASSWORD: "APP_FORGOT_PASSWORD",
});
const assertSmsCode = (value) => value;
const GENEALOGY_ACCESS_PRESET = Object.freeze({ MEMBER_ONLY: "MEMBER_ONLY" });
const session = {
getToken: () => "session-token",
saveToken: () => {},
};
`;
globalThis.__runtimeMode = "remote";
const requests = [];
let nextResponse = null;
let nextFailure = 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(() => {
if (nextFailure) options.fail(nextFailure);
else options.success(nextResponse);
});
}
return task;
},
};
const { appApi, createRequestController, isRequestCancelled } = await import(
toDataModuleUrl(`${prelude}\n${moduleBody}`),
);
const submit = (payload = {
feedbackType: "功能问题",
feedbackContent: "上传照片时出现异常",
contactInfo: "user@example.com",
}) => appApi.submitFeedback(payload);
for (const invalid of [
null,
[],
{},
{ feedbackContent: " " },
{ feedbackContent: 123 },
{ feedbackContent: "内容", feedbackType: 1 },
{ feedbackContent: "内容", contactInfo: false },
{ feedbackContent: "内容", type: "旧字段" },
Object.assign(Object.create({ feedbackType: "继承字段" }), { feedbackContent: "内容" }),
]) {
await assert.rejects(submit(invalid), /反馈|字段|对象|字符串/);
}
nextResponse = { statusCode: 200, data: { code: 200, msg: "成功", data: { accepted: true } } };
const contentOnlyResult = await submit({ feedbackContent: " 仅反馈内容 " });
assert.deepStrictEqual(contentOnlyResult, { accepted: true });
assert.deepStrictEqual(requests.at(-1).data, { feedbackContent: "仅反馈内容" });
nextResponse = { statusCode: 200, data: { code: 200, msg: "成功", data: { feedbackId: 1 } } };
const result = await submit({ feedbackContent: " 有效反馈 ", feedbackType: " 使用建议 ", contactInfo: " " });
assert.deepStrictEqual(result, { feedbackId: 1 });
assert.deepStrictEqual(requests.at(-1).data, {
feedbackContent: "有效反馈",
feedbackType: "使用建议",
});
assert.strictEqual(requests.at(-1).url, "https://backend-api.ddxcjp.cn/genealogy/app/feedback");
assert.strictEqual(requests.at(-1).method, "POST");
assert.strictEqual(requests.at(-1).timeout, 15000);
assert.strictEqual(requests.at(-1).header.clientid, "client-1");
assert.strictEqual(requests.at(-1).header.Authorization, "Bearer session-token");
nextResponse = { statusCode: 200, data: { code: 500, msg: "业务拒绝", data: null } };
await assert.rejects(submit(), (error) =>
error.code === "BUSINESS_ERROR" && error.businessCode === 500,
);
nextResponse = { statusCode: 401, data: { code: 401, msg: "登录失效", data: null } };
await assert.rejects(submit(), (error) =>
error.code === "HTTP_ERROR" && error.httpStatus === 401,
);
for (const statusCode of [201, 204]) {
nextResponse = { statusCode, data: { code: 200, msg: "成功", data: null } };
await assert.rejects(submit(), (error) =>
error.code === "HTTP_ERROR" && error.httpStatus === statusCode,
);
}
for (const invalidResponse of [
{ statusCode: 200, data: null },
{ statusCode: 200, data: {} },
{ statusCode: 200, data: { code: "200", data: null } },
{ statusCode: 200, data: { code: null, data: null } },
{ statusCode: 200, data: { code: false, data: null } },
]) {
nextResponse = invalidResponse;
await assert.rejects(submit(), (error) => error.code === "RESPONSE_INVALID");
}
nextResponse = { statusCode: 200, data: { code: 200, msg: "成功" } };
assert.deepStrictEqual(
await submit({ feedbackContent: "响应无需 data" }),
{ code: 200, msg: "成功" },
"反馈响应未声明 data 必填,不得把合法成功误报为未知",
);
const requestCountBeforeUnsafeOptions = requests.length;
await assert.rejects(
appApi.submitFeedback(
{ feedbackContent: "不得关闭认证头" },
{ authenticated: false },
),
/请求选项|字段/,
);
assert.strictEqual(requests.length, requestCountBeforeUnsafeOptions);
nextFailure = { errMsg: "request:fail timeout" };
await assert.rejects(submit(), (error) => error.code === "REQUEST_TIMEOUT");
nextFailure = null;
holdResponse = true;
const controller = createRequestController();
const cancelled = appApi.submitFeedback(
{ feedbackContent: "离页取消" },
{ requestController: controller },
);
const activeTask = requests.at(-1).__task;
controller.abort();
await assert.rejects(cancelled, (error) => isRequestCancelled(error));
assert.strictEqual(activeTask.aborted, true);
holdResponse = false;
globalThis.__runtimeMode = "mock";
const requestCount = requests.length;
await assert.rejects(
appApi.submitFeedback({ feedbackContent: "本地不得伪提交" }),
(error) => error.code === "WRITE_UNAVAILABLE",
);
assert.strictEqual(requests.length, requestCount, "mock 模式不得发送或伪造反馈请求");
globalThis.__runtimeMode = "invalid";
await assert.rejects(
appApi.submitFeedback({ feedbackContent: "非法环境不得降级成 mock" }),
/运行模式配置无效/,
);
assert.strictEqual(requests.length, requestCount, "非法环境不得发送请求或伪装成本地预览");
delete globalThis.uni;
delete globalThis.__runtimeMode;
process.stdout.write("FEEDBACK-API-RUNTIME-SMOKE PASS\n");
};
run().catch((error) => {
process.stderr.write(`${error.stack || error.message}\n`);
process.exit(1);
});