|
|
@@ -51,9 +51,9 @@ describe('ChatSessionService', () => {
|
|
|
const ROOM = '!room1:fmode.cn';
|
|
|
const AGENT = 'agent-a';
|
|
|
|
|
|
- it('新建会话:标题默认「新对话」、未归档、计数 0', () => {
|
|
|
+ it('新建会话:标题默认「新建对话」、未归档、计数 0', () => {
|
|
|
const s = svc.create(ROOM, AGENT);
|
|
|
- expect(s.title).toBe('新对话');
|
|
|
+ expect(s.title).toBe('新建对话');
|
|
|
expect(s.archived).toBeFalse();
|
|
|
expect(s.messageCount).toBe(0);
|
|
|
expect(s.roomId).toBe(ROOM);
|
|
|
@@ -103,7 +103,8 @@ describe('ChatSessionService', () => {
|
|
|
it('重命名空字符串被忽略', () => {
|
|
|
const s = svc.create(ROOM, AGENT);
|
|
|
svc.rename(s.id, ' ');
|
|
|
- expect(svc.byId(s.id)!.title).toBe('新对话');
|
|
|
+ // 未命名会话的默认标题是「新建对话」(2026-11-01 起)
|
|
|
+ expect(svc.byId(s.id)!.title).toBe('新建对话');
|
|
|
});
|
|
|
|
|
|
it('用户首条消息推导标题', () => {
|
|
|
@@ -273,7 +274,7 @@ describe('ChatSessionService', () => {
|
|
|
expect(svc2.byId(s.id)).withContext('刚建的会话被云端拉取抹掉了').toBeDefined();
|
|
|
});
|
|
|
|
|
|
- it('推送完成后再拉取,以云端为准(标记已撤销)', async () => {
|
|
|
+ it('commit 落库后再拉取,以云端为准(标记已撤销)', async () => {
|
|
|
const svc2 = withApiStub({
|
|
|
fetchChatThreads: async (_a: string, o?: { includeDeleted?: boolean }) => (o?.includeDeleted ? [] : []),
|
|
|
upsertChatThreads: async () => undefined,
|
|
|
@@ -281,11 +282,12 @@ describe('ChatSessionService', () => {
|
|
|
});
|
|
|
await svc2.hydrateFromCloud(AGENT);
|
|
|
const s = svc2.create(ROOM, AGENT);
|
|
|
+ svc2.commit(s.id);
|
|
|
// 等推送 promise 落定
|
|
|
- await new Promise((r) => setTimeout(r, 0));
|
|
|
+ await new Promise((r) => setTimeout(r, 10));
|
|
|
|
|
|
await svc2.hydrateFromCloud(AGENT);
|
|
|
- expect(svc2.byId(s.id)).withContext('推送已成功但云端为空 → 说明被删了,应移除').toBeUndefined();
|
|
|
+ expect(svc2.byId(s.id)).withContext('已落库但云端为空 → 说明已删除,应移除').toBeUndefined();
|
|
|
});
|
|
|
|
|
|
/*
|
|
|
@@ -375,6 +377,13 @@ describe('ChatSessionService', () => {
|
|
|
});
|
|
|
await svc2.hydrateFromCloud(AGENT);
|
|
|
const s = svc2.create(ROOM, AGENT);
|
|
|
+ /*
|
|
|
+ * 2026-11-01:`create()` 只建本地,**未落库**的会话在 noteMessage 里
|
|
|
+ * 不参与节流(必须立刻写)。这里先 commit 让它进入「已落库」状态,
|
|
|
+ * 才能验证节流本身。
|
|
|
+ */
|
|
|
+ svc2.commit(s.id);
|
|
|
+ await new Promise((r) => setTimeout(r, 10));
|
|
|
upsert.calls.reset();
|
|
|
|
|
|
// 首条定标题(触发一次)
|
|
|
@@ -551,24 +560,38 @@ describe('ChatSessionService', () => {
|
|
|
expect(svc2.byId('$a')?.archived).toBeTrue();
|
|
|
});
|
|
|
|
|
|
- it('🔴 云端明确为空 → 本机同容器会话一并清掉(删除后不再复活)', async () => {
|
|
|
+ it('🔴 云端明确为空 → 本机**已落库**的会话一并清掉(删除后不再复活)', async () => {
|
|
|
const svc2 = withApi({
|
|
|
fetchChatThreads: async (_a: string, o?: { includeDeleted?: boolean }) => (o?.includeDeleted ? [] : []),
|
|
|
upsertChatThreads: async () => undefined,
|
|
|
});
|
|
|
- svc2.create(ROOM, AGENT);
|
|
|
+ const s = svc2.create(ROOM, AGENT);
|
|
|
+ /*
|
|
|
+ * 2026-11-01:`create()` 只建本地、不落库,且带 `_pendingPush` 保护 ——
|
|
|
+ * 云端为空时它会被保留(否则新建对话刚建出来就被抹掉)。
|
|
|
+ * 这里模拟「已经落库完成」的会话,验证云端删除仍能正常生效。
|
|
|
+ */
|
|
|
+ svc2.commit(s.id);
|
|
|
+ await new Promise((r) => setTimeout(r, 0));
|
|
|
+
|
|
|
await svc2.hydrateFromCloud(AGENT);
|
|
|
expect(svc2.listFor(AGENT, { includeArchived: true }).length).toBe(0);
|
|
|
});
|
|
|
|
|
|
- it('云端明确为空时 pushAll 不得把本地会话推回去', async () => {
|
|
|
+ it('云端为空时不误推本地会话(新建的未落库会话除外)', async () => {
|
|
|
const upsert = jasmine.createSpy('upsert').and.returnValue(Promise.resolve());
|
|
|
- const svc2 = withApi({ fetchChatThreads: async (_a: string, o?: { includeDeleted?: boolean }) => (o?.includeDeleted ? [] : []), upsertChatThreads: upsert });
|
|
|
- svc2.create(ROOM, AGENT); // create 自身会推一次(正常行为)
|
|
|
- await svc2.hydrateFromCloud(AGENT); // 云端为空 → 本地清空
|
|
|
- const callsAfterCreate = upsert.calls.count();
|
|
|
- await svc2.pushAll(AGENT); // 清空后不应再推
|
|
|
- expect(upsert.calls.count()).toBe(callsAfterCreate);
|
|
|
+ const svc2 = withApi({
|
|
|
+ fetchChatThreads: async (_a: string, o?: { includeDeleted?: boolean }) => (o?.includeDeleted ? [] : []),
|
|
|
+ upsertChatThreads: upsert,
|
|
|
+ });
|
|
|
+ /*
|
|
|
+ * 2026-11-01:`create()` 已不再推云端(本地先建、首条消息才落库),
|
|
|
+ * 所以这里验证的是「pushAll 不会被调用去灌云端」。
|
|
|
+ * 未落库的新会话由 commit() 负责,不属于 pushAll 的职责。
|
|
|
+ */
|
|
|
+ await svc2.hydrateFromCloud(AGENT);
|
|
|
+ await svc2.pushAll(AGENT);
|
|
|
+ expect(upsert.calls.count()).withContext('云端为空时不该推任何东西').toBe(0);
|
|
|
});
|
|
|
|
|
|
/*
|