收敛客户端静态发布链路

This commit is contained in:
2026-08-10 17:04:29 +08:00
parent 2e737dee31
commit 4df047708b
37 changed files with 604 additions and 3071 deletions

View File

@@ -40,17 +40,17 @@ async function writeLegacyPromotionPlan(projectPath: string, content: string): P
await writeFile(join(projectPath, 'PROMOTION_PLAN.md'), content, 'utf8');
}
function createServerPublishFile(appId: string): string {
function createServerBindingFile(projectId: string, appId: string): string {
return `${JSON.stringify({
schema_version: 2,
project_id: projectId,
status: 'submitted',
requested_at: '2026-08-10T00:00:00.000Z',
updated_at: '2026-08-10T00:00:00.000Z',
app_id: appId,
title: '测试游戏',
summary: '合规同步测试',
category: 'game',
age_band: '10-16',
difficulty: 'beginner',
version_id: 'version-1',
version_name: 'v0.1.0',
change_log: 'test',
zip_file_path: 'dist/test.zip',
review_status: 'building',
})}\n`;
}
@@ -95,11 +95,14 @@ describe('project progress compliance synchronization', () => {
const localPath = await createProjectDirectory('local');
await writeGameDocuments(serverPath, '# server gdd', '# server tasks');
await writeGameDocuments(localPath, '# local gdd', '# local tasks');
await writeFile(join(serverPath, 'works-publish.json'), createServerPublishFile('server-game'), 'utf8');
const projectStore = createProjectStore(createMemoryProjectStorage());
const serverProject = await projectStore.rememberProject(serverPath);
const localProject = await projectStore.rememberProject(localPath);
await writeFile(
join(serverPath, 'works-cloud-deploy.json'),
createServerBindingFile(serverProject.id, 'server-game'),
'utf8',
);
const harness = createWatcherHarness();
const fetchMock = vi.fn().mockResolvedValue(new Response('{}', { status: 200 }));
const sync = createProjectProgressSync(projectStore, {
@@ -129,7 +132,7 @@ describe('project progress compliance synchronization', () => {
project_key: localProject.id,
});
const serverChanged = harness.callbacks.get(serverPath);
const serverChanged = harness.callbacks.get(serverProject.path);
expect(serverChanged).toBeDefined();
serverChanged?.('GDD.md');
await vi.advanceTimersByTimeAsync(25);
@@ -149,10 +152,13 @@ describe('project progress compliance synchronization', () => {
const productOverview = '# 产品运营介绍\n\n一句话价值让玩家在三分钟内完成一次太空清洁任务。';
await writeGameDocuments(projectPath, '# gdd', '# tasks');
await writeProductOverview(projectPath, productOverview);
await writeFile(join(projectPath, 'works-publish.json'), createServerPublishFile('promotion-game'), 'utf8');
const projectStore = createProjectStore(createMemoryProjectStorage());
const project = await projectStore.rememberProject(projectPath);
await writeFile(
join(projectPath, 'works-cloud-deploy.json'),
createServerBindingFile(project.id, 'promotion-game'),
'utf8',
);
const harness = createWatcherHarness();
const fetchMock = vi.fn().mockResolvedValue(new Response('{}', { status: 200 }));
const sync = createProjectProgressSync(projectStore, {
@@ -180,10 +186,13 @@ describe('project progress compliance synchronization', () => {
await writeGameDocuments(projectPath, '# gdd', '# tasks');
await writeProductOverview(projectPath, productOverview);
await writeLegacyPromotionPlan(projectPath, '# 旧运营宣传计划\n\n不应覆盖新文档');
await writeFile(join(projectPath, 'works-publish.json'), createServerPublishFile('overview-preferred-game'), 'utf8');
const projectStore = createProjectStore(createMemoryProjectStorage());
const project = await projectStore.rememberProject(projectPath);
await writeFile(
join(projectPath, 'works-cloud-deploy.json'),
createServerBindingFile(project.id, 'overview-preferred-game'),
'utf8',
);
const harness = createWatcherHarness();
const fetchMock = vi.fn().mockResolvedValue(new Response('{}', { status: 200 }));
const sync = createProjectProgressSync(projectStore, {
@@ -206,10 +215,13 @@ describe('project progress compliance synchronization', () => {
const legacyPromotionPlan = '# 旧运营宣传计划\n\n卖点用简单操作理解环保行动。';
await writeGameDocuments(projectPath, '# gdd', '# tasks');
await writeLegacyPromotionPlan(projectPath, legacyPromotionPlan);
await writeFile(join(projectPath, 'works-publish.json'), createServerPublishFile('legacy-promotion-game'), 'utf8');
const projectStore = createProjectStore(createMemoryProjectStorage());
const project = await projectStore.rememberProject(projectPath);
await writeFile(
join(projectPath, 'works-cloud-deploy.json'),
createServerBindingFile(project.id, 'legacy-promotion-game'),
'utf8',
);
const harness = createWatcherHarness();
const fetchMock = vi.fn().mockResolvedValue(new Response('{}', { status: 200 }));
const sync = createProjectProgressSync(projectStore, {
@@ -250,8 +262,12 @@ describe('project progress compliance synchronization', () => {
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock.mock.calls.some(([url]) => String(url).includes('/agent/prompt'))).toBe(false);
await writeFile(join(projectPath, 'works-publish.json'), createServerPublishFile('bound-promotion-game'), 'utf8');
harness.callbacks.get(projectPath)?.('works-publish.json');
await writeFile(
join(projectPath, 'works-cloud-deploy.json'),
createServerBindingFile(project.id, 'bound-promotion-game'),
'utf8',
);
harness.callbacks.get(project.path)?.('works-cloud-deploy.json');
await sync.flushProject(project.id);
expect(fetchMock).toHaveBeenCalledTimes(3);
@@ -268,10 +284,13 @@ describe('project progress compliance synchronization', () => {
const secondOverview = '# 更新后的产品运营介绍\n\n新增真实试玩证据。';
await writeGameDocuments(projectPath, '# gdd', '# tasks');
await writeProductOverview(projectPath, firstOverview);
await writeFile(join(projectPath, 'works-publish.json'), createServerPublishFile('retry-promotion-game'), 'utf8');
const projectStore = createProjectStore(createMemoryProjectStorage());
const project = await projectStore.rememberProject(projectPath);
await writeFile(
join(projectPath, 'works-cloud-deploy.json'),
createServerBindingFile(project.id, 'retry-promotion-game'),
'utf8',
);
const harness = createWatcherHarness();
const fetchMock = vi.fn()
.mockResolvedValueOnce(new Response('{}', { status: 200 }))
@@ -396,13 +415,13 @@ describe('project progress compliance synchronization', () => {
synchronizers.push(sync);
await sync.start();
expect(harness.closeMocks.get(projectPath)).toBeDefined();
expect(harness.closeMocks.get(project.path)).toBeDefined();
await projectStore.removeProject(project.id);
expect(sync.getWatchedProjectIds()).toEqual([]);
expect(harness.closeMocks.get(projectPath)).toHaveBeenCalledTimes(1);
expect(harness.closeMocks.get(project.path)).toHaveBeenCalledTimes(1);
sync.stop();
expect(harness.closeMocks.get(projectPath)).toHaveBeenCalledTimes(1);
expect(harness.closeMocks.get(project.path)).toHaveBeenCalledTimes(1);
});
it('does not expose document content in error logs or request URLs', async () => {