import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { createWorksSubmissionBindingStore, readWorksSubmissionBinding, } from '@electron/services/works-submission-binding'; import type { OpencodeProject } from '@electron/opencode/project-store'; import { WORKS_SUBMISSION_BINDING_FILE_NAME, WORKS_SUBMISSION_BINDING_SCHEMA_VERSION, } from '../../shared/works-submission-binding'; const temporaryDirectories: string[] = []; async function createProject(): Promise { const projectPath = await mkdtemp(join(tmpdir(), 'makelore-submission-binding-')); temporaryDirectories.push(projectPath); return { id: 'project-1', path: projectPath, name: 'submission binding test', createdAt: '2026-08-10T00:00:00.000Z', updatedAt: '2026-08-10T00:00:00.000Z', lastOpenedAt: '2026-08-10T00:00:00.000Z', }; } function createStore(project: OpencodeProject) { return { listProjects: vi.fn(async () => [project]) }; } afterEach(async () => { await Promise.all(temporaryDirectories.splice(0).map((directory) => ( rm(directory, { recursive: true, force: true }) ))); }); describe('Works Square submission binding store', () => { it('records the exact one-click submitted version without owning upload behavior', async () => { const project = await createProject(); const store = createWorksSubmissionBindingStore(createStore(project)); const record = await store.recordSubmitted(project.id, { appId: 'planet-game', versionId: 'version-7', versionName: 'v0.7.0', reviewStatus: 'building', zipSha256: 'a'.repeat(64), }); expect(record).toMatchObject({ schema_version: WORKS_SUBMISSION_BINDING_SCHEMA_VERSION, project_id: project.id, status: 'submitted', app_id: 'planet-game', version_id: 'version-7', version_name: 'v0.7.0', review_status: 'building', zip_sha256: 'a'.repeat(64), }); await expect(store.get(project.id)).resolves.toEqual(record); await expect(readWorksSubmissionBinding(project.path)).resolves.toEqual(record); }); it.each(['armed', 'waiting_for_package', 'waiting_for_login', 'uploading', 'failed'])( 'retires a legacy %s task with an understandable next step', async (status) => { const project = await createProject(); await writeFile( join(project.path, WORKS_SUBMISSION_BINDING_FILE_NAME), `${JSON.stringify({ schema_version: 1, project_id: project.id, status, requested_at: '2026-08-01T00:00:00.000Z', updated_at: '2026-08-01T00:05:00.000Z', })}\n`, 'utf8', ); const store = createWorksSubmissionBindingStore(createStore(project)); await store.start(); const record = await store.get(project.id); expect(record).toMatchObject({ schema_version: WORKS_SUBMISSION_BINDING_SCHEMA_VERSION, project_id: project.id, status: 'legacy_retired', error_code: 'LEGACY_AUTO_DEPLOY_RETIRED', message: '旧版自动部署任务已停用,请在项目配置中点击“提交审核”重新提交。', }); expect(JSON.parse(await readFile( join(project.path, WORKS_SUBMISSION_BINDING_FILE_NAME), 'utf8', ))).toEqual(record); }, ); it('preserves a submitted legacy version binding while upgrading its schema', async () => { const project = await createProject(); await writeFile( join(project.path, WORKS_SUBMISSION_BINDING_FILE_NAME), `${JSON.stringify({ schema_version: 1, project_id: project.id, status: 'submitted', requested_at: '2026-08-01T00:00:00.000Z', updated_at: '2026-08-01T00:05:00.000Z', app_id: 'planet-game', version_id: 'version-6', version_name: 'v0.6.0', review_status: 'approved', })}\n`, 'utf8', ); const record = await readWorksSubmissionBinding(project.path); expect(record).toMatchObject({ schema_version: WORKS_SUBMISSION_BINDING_SCHEMA_VERSION, status: 'submitted', app_id: 'planet-game', version_id: 'version-6', version_name: 'v0.6.0', review_status: 'approved', }); }); it.each([ ['submitted without a version id', { status: 'submitted', app_id: 'planet-game', version_name: 'v0.7.0', }], ['retired without its fixed error code', { status: 'legacy_retired', message: '请重新提交', }], ])('rejects a malformed schema 2 record: %s', async (_case, fields) => { const project = await createProject(); await writeFile( join(project.path, WORKS_SUBMISSION_BINDING_FILE_NAME), `${JSON.stringify({ schema_version: WORKS_SUBMISSION_BINDING_SCHEMA_VERSION, project_id: project.id, requested_at: '2026-08-10T00:00:00.000Z', updated_at: '2026-08-10T00:05:00.000Z', ...fields, })}\n`, 'utf8', ); await expect(readWorksSubmissionBinding(project.path, project.id)).resolves.toBeNull(); }); it('rejects a copied binding whose project id does not match the current project', async () => { const project = await createProject(); await writeFile( join(project.path, WORKS_SUBMISSION_BINDING_FILE_NAME), `${JSON.stringify({ schema_version: WORKS_SUBMISSION_BINDING_SCHEMA_VERSION, project_id: 'another-project', status: 'submitted', requested_at: '2026-08-10T00:00:00.000Z', updated_at: '2026-08-10T00:05:00.000Z', app_id: 'planet-game', version_id: 'version-7', version_name: 'v0.7.0', })}\n`, 'utf8', ); const store = createWorksSubmissionBindingStore(createStore(project)); await expect(store.get(project.id)).resolves.toBeNull(); }); });