完成50%
This commit is contained in:
@@ -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);
|
||||
});
|
||||
Reference in New Issue
Block a user