update: 表改动 主页改动
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
victo
2026-04-14 18:58:33 +08:00
parent 0981d6ee1b
commit 3d6f31433a
37 changed files with 2594 additions and 699 deletions

View File

@@ -1565,9 +1565,164 @@ test('runtime persistence is isolated by user', async () => {
},
);
const userBLibraryPayload = (await userBLibrary.json()) as {
profiles: unknown[];
entries: unknown[];
};
assert.deepEqual(userBLibraryPayload.profiles, []);
assert.deepEqual(userBLibraryPayload.entries, []);
});
});
test('custom worlds stay private until published and then appear in the public gallery', async () => {
await withTestServer('custom-world-gallery', async ({ baseUrl }) => {
const owner = await authEntry(baseUrl, 'gallery_owner', 'secret123');
const viewer = await authEntry(baseUrl, 'gallery_viewer', 'secret123');
const upsertResponse = await httpRequest(
`${baseUrl}/api/runtime/custom-world-library/world-a`,
withBearer(owner.token, {
method: 'PUT',
body: JSON.stringify({
profile: {
id: 'world-a',
name: '裂桥前线',
subtitle: '边境上空的断层回响',
summary: '围绕裂桥哨线与失序潮汐展开的前线世界。',
tone: '压迫、冷峻、持续失衡',
playerGoal: '在裂桥崩塌前守住归路',
majorFactions: ['裂桥守军'],
coreConflicts: ['断层外压正在逼近城线'],
playableNpcs: [
{
id: 'role-1',
name: '沈昼',
},
],
storyNpcs: [],
landmarks: [
{
id: 'landmark-1',
name: '裂桥前哨',
description: '裂谷边缘的前线哨卡。',
dangerLevel: '高',
sceneNpcIds: [],
connections: [],
},
],
},
}),
}),
);
const upsertPayload = (await upsertResponse.json()) as {
entry: {
visibility: 'draft' | 'published';
authorDisplayName: string;
};
entries: unknown[];
};
assert.equal(upsertResponse.status, 200);
assert.equal(upsertPayload.entry.visibility, 'draft');
assert.equal(upsertPayload.entry.authorDisplayName, 'gallery_owner');
const galleryBeforePublish = await httpRequest(
`${baseUrl}/api/runtime/custom-world-gallery`,
{
headers: {
Authorization: `Bearer ${viewer.token}`,
},
},
);
const galleryBeforePayload = (await galleryBeforePublish.json()) as {
entries: unknown[];
};
assert.deepEqual(galleryBeforePayload.entries, []);
const publishResponse = await httpRequest(
`${baseUrl}/api/runtime/custom-world-library/world-a/publish`,
withBearer(owner.token, {
method: 'POST',
}),
);
const publishPayload = (await publishResponse.json()) as {
entry: {
visibility: 'draft' | 'published';
publishedAt: string | null;
};
};
assert.equal(publishResponse.status, 200);
assert.equal(publishPayload.entry.visibility, 'published');
assert.ok(publishPayload.entry.publishedAt);
const galleryAfterPublish = await httpRequest(
`${baseUrl}/api/runtime/custom-world-gallery`,
{
headers: {
Authorization: `Bearer ${viewer.token}`,
},
},
);
const galleryAfterPayload = (await galleryAfterPublish.json()) as {
entries: Array<{
ownerUserId: string;
profileId: string;
worldName: string;
authorDisplayName: string;
}>;
};
assert.equal(galleryAfterPublish.status, 200);
assert.equal(galleryAfterPayload.entries.length, 1);
assert.equal(galleryAfterPayload.entries[0]?.worldName, '裂桥前线');
assert.equal(galleryAfterPayload.entries[0]?.authorDisplayName, 'gallery_owner');
const galleryDetail = await httpRequest(
`${baseUrl}/api/runtime/custom-world-gallery/${encodeURIComponent(galleryAfterPayload.entries[0]?.ownerUserId || '')}/${encodeURIComponent(galleryAfterPayload.entries[0]?.profileId || '')}`,
{
headers: {
Authorization: `Bearer ${viewer.token}`,
},
},
);
const galleryDetailPayload = (await galleryDetail.json()) as {
entry: {
worldName: string;
profile: {
name: string;
};
};
};
assert.equal(galleryDetail.status, 200);
assert.equal(galleryDetailPayload.entry.worldName, '裂桥前线');
assert.equal(galleryDetailPayload.entry.profile.name, '裂桥前线');
const unpublishResponse = await httpRequest(
`${baseUrl}/api/runtime/custom-world-library/world-a/unpublish`,
withBearer(owner.token, {
method: 'POST',
}),
);
const unpublishPayload = (await unpublishResponse.json()) as {
entry: {
visibility: 'draft' | 'published';
};
};
assert.equal(unpublishResponse.status, 200);
assert.equal(unpublishPayload.entry.visibility, 'draft');
const galleryAfterUnpublish = await httpRequest(
`${baseUrl}/api/runtime/custom-world-gallery`,
{
headers: {
Authorization: `Bearer ${viewer.token}`,
},
},
);
const galleryAfterUnpublishPayload = (await galleryAfterUnpublish.json()) as {
entries: unknown[];
};
assert.deepEqual(galleryAfterUnpublishPayload.entries, []);
});
});