feat(chat): sync multi-role backend APIs

This commit is contained in:
2026-07-20 11:29:54 +08:00
parent 16b5c16e76
commit b6fdc912ae
84 changed files with 1488 additions and 439 deletions
+50 -1
View File
@@ -8,7 +8,9 @@ import {
DEFAULT_CHARACTER,
getCharacterById,
getCharacterBySlug,
mergeRemoteCharacterCatalog,
} from "@/data/constants/character";
import { CharacterListResponseSchema } from "@/data/schemas/character";
describe("local character catalog", () => {
it("contains immutable and uniquely addressable character profiles", () => {
@@ -17,6 +19,11 @@ describe("local character catalog", () => {
"maya",
"nayeli",
]);
expect(CHARACTERS.map((character) => character.id)).toEqual([
"elio",
"maya-tan",
"nayeli-cervantes",
]);
expect(new Set(CHARACTERS.map((character) => character.id)).size).toBe(
CHARACTERS.length,
);
@@ -27,6 +34,48 @@ describe("local character catalog", () => {
expect(Object.isFrozen(DEFAULT_CHARACTER.copy)).toBe(true);
});
it("merges backend authority with local routes and assets", () => {
const snapshot = mergeRemoteCharacterCatalog(
CharacterListResponseSchema.parse({
defaultCharacterId: "elio",
items: [
{
id: "maya-tan",
displayName: "Maya Backend",
isActive: true,
sortOrder: 5,
capabilities: { chat: true, privateContent: false },
},
{
id: "elio",
displayName: "Elio Backend",
isActive: true,
sortOrder: 10,
capabilities: { chat: true, privateContent: true },
},
{
id: "unknown",
displayName: "Unknown",
isActive: true,
sortOrder: 1,
capabilities: { chat: true, privateContent: true },
},
],
}),
);
expect(snapshot.catalog.characters.map((item) => item.slug)).toEqual([
"maya",
"elio",
]);
expect(snapshot.catalog.getById("maya-tan")).toMatchObject({
displayName: "Maya Backend",
assets: { avatar: "/images/avatar/maya.png" },
capabilities: { chat: true, privateRoom: false, tip: true },
});
expect(snapshot.defaultCharacter.id).toBe("elio");
});
it("sorts catalog entries and rejects duplicate identities", () => {
const catalog = createCharacterCatalog([...CHARACTERS].reverse());
expect(catalog.characters.map((character) => character.slug)).toEqual([
@@ -40,7 +89,7 @@ describe("local character catalog", () => {
});
it("resolves characters by id and normalized slug", () => {
expect(getCharacterById("character_maya")?.displayName).toBe("Maya Tan");
expect(getCharacterById("maya-tan")?.displayName).toBe("Maya Tan");
expect(getCharacterBySlug(" NAYELI ")?.displayName).toBe(
"Nayeli Cervantes",
);