30c377d0f0
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 6m21s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m51s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 5m3s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 5m54s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 4m28s
Project CI / AI game creator shell Rust crates (push) Successful in 1m53s
Project CI / Repository checks (push) Successful in 5m33s
Project CI / Frontend tests (push) Successful in 7m20s
Project CI / Native shell tests (push) Successful in 8m29s
Project CI / Backend tests (push) Successful in 8m57s
Project CI / AI game creator shell web tests (push) Successful in 3m16s
说明: 在把工作交给段哥前还没有实现direct project聊天页面的迁移, 导致在 #375 里用很复杂的实现又做了一套事件流, 实测还有会话丢失的bug, 我在这里把数据获取的部分迁移到 #367 上 --------- Co-authored-by: 孔令弘 <ink29535@proton.me> Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/384 Reviewed-by: 孔令弘 <ink29535@proton.me> Co-authored-by: 王德宇 <kvtodev@outlook.com> Co-committed-by: 王德宇 <kvtodev@outlook.com>
109 lines
3.7 KiB
TypeScript
109 lines
3.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
directHistoryAnchorGateToWaitFor,
|
|
openDirectHistoryAnchorGate,
|
|
reuseOrOpenDirectHistoryAnchorGate,
|
|
} from '../src/features/project-workspace/directHistoryAnchorGate';
|
|
|
|
const projectA = '/tmp/项目A';
|
|
const projectB = '/tmp/项目B';
|
|
|
|
/** 订阅侧的回执:一次订阅只 settle 一次。 */
|
|
function subscribeReceipt(gate: { settle: (anchor: string | null) => void }) {
|
|
gate.settle('turn-2-user');
|
|
}
|
|
|
|
describe('openDirectHistoryAnchorGate', () => {
|
|
it('默认不解析:回执到达前首屏必须等它', async () => {
|
|
const gate = openDirectHistoryAnchorGate(projectA);
|
|
const settled = { value: 'pending' as string | null | 'pending' };
|
|
void gate.anchor.then((anchor) => {
|
|
settled.value = anchor;
|
|
});
|
|
|
|
// 让微任务跑一轮:没有回执就不该有结果。
|
|
await Promise.resolve();
|
|
await Promise.resolve();
|
|
expect(settled.value).toBe('pending');
|
|
expect(gate.projectPath).toBe(projectA);
|
|
expect(gate.consumed).toBe(false);
|
|
});
|
|
|
|
it('settle 之后 anchor 解析成回执里的 lastCompletedItemId', async () => {
|
|
const gate = openDirectHistoryAnchorGate(projectA);
|
|
subscribeReceipt(gate);
|
|
|
|
await expect(gate.anchor).resolves.toBe('turn-2-user');
|
|
});
|
|
|
|
it('订阅不可用时 settle(null):首屏退化成取文件尾', async () => {
|
|
const gate = openDirectHistoryAnchorGate(projectA);
|
|
gate.settle(null);
|
|
|
|
await expect(gate.anchor).resolves.toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('reuseOrOpenDirectHistoryAnchorGate', () => {
|
|
it('同一个项目复用同一道闸门:订阅侧要 settle 首屏已经开好的那道', () => {
|
|
const opened = openDirectHistoryAnchorGate(projectA);
|
|
|
|
expect(reuseOrOpenDirectHistoryAnchorGate(opened, projectA)).toBe(opened);
|
|
});
|
|
|
|
it('换项目开一道新闸门,不复用旧项目的回执', () => {
|
|
const opened = openDirectHistoryAnchorGate(projectA);
|
|
const next = reuseOrOpenDirectHistoryAnchorGate(opened, projectB);
|
|
|
|
expect(next).not.toBe(opened);
|
|
expect(next.projectPath).toBe(projectB);
|
|
expect(next.consumed).toBe(false);
|
|
});
|
|
|
|
it('还没有闸门时新开一道', () => {
|
|
expect(reuseOrOpenDirectHistoryAnchorGate(null, projectA).projectPath).toBe(
|
|
projectA,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('directHistoryAnchorGateToWaitFor', () => {
|
|
it('没有闸门(首屏比订阅 effect 先跑)时开一道给调用方登记', async () => {
|
|
const gate = directHistoryAnchorGateToWaitFor(null, projectA);
|
|
|
|
expect(gate).not.toBeNull();
|
|
expect(gate?.projectPath).toBe(projectA);
|
|
subscribeReceipt(gate!);
|
|
await expect(gate!.anchor).resolves.toBe('turn-2-user');
|
|
});
|
|
|
|
it('同项目未消费的闸门直接复用:首屏等到订阅回执', async () => {
|
|
const opened = openDirectHistoryAnchorGate(projectA);
|
|
const toWaitFor = directHistoryAnchorGateToWaitFor(opened, projectA);
|
|
|
|
expect(toWaitFor).toBe(opened);
|
|
subscribeReceipt(opened);
|
|
await expect(toWaitFor!.anchor).resolves.toBe('turn-2-user');
|
|
});
|
|
|
|
it('同项目已消费返回 null:重开项目没有新回执可等,退回取文件尾', () => {
|
|
const opened = openDirectHistoryAnchorGate(projectA);
|
|
opened.consumed = true;
|
|
|
|
expect(directHistoryAnchorGateToWaitFor(opened, projectA)).toBeNull();
|
|
});
|
|
|
|
it('换项目不复用旧闸门:新项目的首屏要等新订阅的回执', () => {
|
|
const opened = openDirectHistoryAnchorGate(projectA);
|
|
subscribeReceipt(opened);
|
|
opened.consumed = true;
|
|
|
|
const toWaitFor = directHistoryAnchorGateToWaitFor(opened, projectB);
|
|
|
|
expect(toWaitFor).not.toBeNull();
|
|
expect(toWaitFor).not.toBe(opened);
|
|
expect(toWaitFor?.projectPath).toBe(projectB);
|
|
});
|
|
});
|