完成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
+169
View File
@@ -0,0 +1,169 @@
"use strict";
const assert = require("assert");
const fs = require("fs");
const path = require("path");
const run = async () => {
const source = fs.readFileSync(path.join(__dirname, "../data/mock.js"), "utf8");
const contractSource = fs.readFileSync(
path.join(__dirname, "../utils/genealogy-contracts.js"),
"utf8",
);
const contractUrl = `data:text/javascript;base64,${Buffer.from(contractSource).toString("base64")}`;
const moduleUrl = `data:text/javascript;base64,${Buffer.from(source.replace("../utils/genealogy-contracts.js", contractUrl)).toString("base64")}`;
const {
treeMembers,
findTreeMemberFixture,
findTreeMemberPresentationFixture,
listTreeMemberFixtures,
listTreeMemberPresentationFixtures,
} = await import(moduleUrl);
assert.deepStrictEqual(
treeMembers.map((member) => member.id),
["101", "102", "103", "104", "105", "106"],
"T01/T03-T08 must consume the same six-member fixture identity set",
);
assert.strictEqual(new Set(treeMembers.map((member) => member.id)).size, treeMembers.length);
const ids = new Set(treeMembers.map((member) => member.id));
const supportedStatuses = new Set(["privacy", "deceased", "forbidden"]);
for (const member of treeMembers) {
for (const field of [
"genealogyId",
"name",
"relation",
"generationName",
"branch",
"years",
"birthDate",
"deathDate",
"birthplace",
"status",
"summary",
"note",
]) {
assert.strictEqual(typeof member[field], "string", `${member.id}.${field} must be a string`);
}
assert.strictEqual(member.genealogyId, "1001");
assert(Number.isInteger(member.generation) && member.generation > 0);
assert.strictEqual(
Object.prototype.hasOwnProperty.call(member, "canEdit"),
false,
`${member.id} must not publish a fixture-only backend capability`,
);
assert(
member.parentId === "" || typeof member.parentId === "string",
`${member.id}.parentId must be an empty string or a string identity`,
);
assert(supportedStatuses.has(member.status), `${member.id} has an unsupported status`);
assert(Array.isArray(member.relatives));
if (member.parentId) {
assert(ids.has(member.parentId), `${member.id} has an unknown parent`);
const parent = treeMembers.find((candidate) => candidate.id === member.parentId);
assert.strictEqual(
member.generation,
parent.generation + 1,
`${member.id} must be exactly one generation below its parent`,
);
}
assert.notStrictEqual(member.parentId, member.id, `${member.id} cannot be its own parent`);
assert.strictEqual(
new Set(member.relatives.map((relative) => relative.personId)).size,
member.relatives.length,
`${member.id} cannot repeat a relative identity`,
);
for (const relative of member.relatives) {
assert(ids.has(relative.personId), `${member.id} has an unknown relative`);
assert(relative.personId !== member.id, `${member.id} cannot be its own relative`);
assert.strictEqual(typeof relative.relation, "string");
}
}
for (const member of treeMembers) {
const visited = new Set([member.id]);
let parentId = member.parentId;
while (parentId) {
assert(!visited.has(parentId), `${member.id} has a cyclic parent chain`);
visited.add(parentId);
parentId = treeMembers.find((candidate) => candidate.id === parentId)?.parentId || "";
}
}
assert.strictEqual(findTreeMemberFixture("1001", "unknown"), null);
assert.strictEqual(findTreeMemberFixture("1002", "101"), null);
assert.deepStrictEqual(listTreeMemberFixtures("1002"), []);
for (const id of ids) assert.strictEqual(findTreeMemberFixture("1001", id)?.id, id);
const firstList = listTreeMemberFixtures("1001");
firstList[0].name = "changed";
firstList[0].relatives[0].relation = "changed";
assert.notStrictEqual(findTreeMemberFixture("1001", "101").name, "changed");
assert.notStrictEqual(findTreeMemberFixture("1001", "101").relatives[0].relation, "changed");
const firstMember = findTreeMemberFixture("1001", "101");
firstMember.name = "changed again";
firstMember.relatives[0].relation = "changed again";
assert.notStrictEqual(findTreeMemberFixture("1001", "101").name, "changed again");
assert.notStrictEqual(
findTreeMemberFixture("1001", "101").relatives[0].relation,
"changed again",
);
const restrictedPresentation = findTreeMemberPresentationFixture("1001", "102");
assert.deepStrictEqual(
Object.keys(restrictedPresentation).sort(),
["appUserId", "genealogyId", "generation", "id", "name", "relation", "status"],
"restricted member presentation must not carry private profile fields",
);
assert.strictEqual(restrictedPresentation.status, "privacy");
assert.strictEqual(findTreeMemberPresentationFixture("1001", "101").summary.length > 0, true);
assert.deepStrictEqual(listTreeMemberPresentationFixtures("1002"), []);
const restrictedListMember = listTreeMemberPresentationFixtures("1001").find(
(member) => member.id === "103",
);
assert.strictEqual(Object.prototype.hasOwnProperty.call(restrictedListMember, "note"), false);
globalThis.__treeFixtureOwner = await import(moduleUrl);
const apiSource = fs
.readFileSync(path.join(__dirname, "../utils/api.js"), "utf8")
.replace(
/^import \{[\s\S]*?\} from '@\/data\/mock\.js'\r?\n/,
"const { currentUser, genealogies, publicGenealogies, treeMembers, familyContent, notifications, joinApplications, listFamilyFeedFixtures, listFamilyArticleFixtures, listFamilyAlbumFixtures } = globalThis.__treeFixtureOwner;\n",
)
.replace(
/^import \{ hasRemoteConfig[^\n]+\r?\n/m,
"const hasRemoteConfig = () => false; const resolveRuntimeMode = () => 'mock'; const runtimeConfig = { baseUrl: '', clientId: 'test', tenantId: 'test' };\n",
)
.replace(
/^import \{ AUTH_TAC_SCENE, assertSmsCode \}[^\n]+\r?\n/m,
"const AUTH_TAC_SCENE = {}; const assertSmsCode = (value) => value;\n",
)
.replace(
/^import \{ GENEALOGY_ACCESS_PRESET \}[^\n]+\r?\n/m,
"const GENEALOGY_ACCESS_PRESET = { MEMBER_ONLY: 'MEMBER_ONLY' };\n",
)
.replace(
/^import \{ session \}[^\n]+\r?\n/m,
"const session = { getToken: () => '', saveToken: () => {} };\n",
);
const apiUrl = `data:text/javascript;base64,${Buffer.from(apiSource).toString("base64")}`;
const { appApi } = await import(apiUrl);
const lengthBeforeCreate = treeMembers.length;
await assert.rejects(
appApi.createPerson("1001", { name: "汤新", generation: 15, fatherId: "104" }),
(error) => error?.code === "WRITE_UNAVAILABLE" && /不会保存/.test(error.message),
);
assert.strictEqual(
treeMembers.length,
lengthBeforeCreate,
"local createPerson must reject without mutating the shared fixture owner",
);
process.stdout.write("TREE-MEMBER-FIXTURE-RUNTIME-SMOKE PASS\n");
};
run().catch((error) => {
process.stderr.write(`${error.stack || error.message}\n`);
process.exit(1);
});