import { spawnSync } from 'node:child_process'; import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { describe, expect, it } from 'vitest'; const repositoryRoot = resolve(import.meta.dirname, '../..'); function readRepositoryFile(path: string): string { return readFileSync(resolve(repositoryRoot, path), 'utf8'); } describe('Learning player packaging', () => { it('requires an explicit artifact or explicit local source opt-in', () => { const manifest = JSON.parse(readRepositoryFile('scripts/learning-player-build-manifest.json')); expect(manifest.localSource).toEqual({ environmentVariable: 'MAKELORE_LEARNING_SOURCE', requiredScripts: ['build', 'build:makelore-player'], }); const result = spawnSync(process.execPath, ['scripts/sync-learning-player.mjs'], { cwd: repositoryRoot, encoding: 'utf8', env: { ...process.env, CI: '', MAKELORE_RELEASE_BUILD: '', MAKELORE_LEARNING_PLAYER_ARTIFACT: '', MAKELORE_LEARNING_SOURCE: '', }, }); expect(result.status).not.toBe(0); expect(result.stderr).toContain('explicitly opt in to a local source build with MAKELORE_LEARNING_SOURCE'); expect(result.stderr).not.toContain('麦洛学习'); }); it('keeps CI and release builds artifact-only', () => { for (const releaseEnvironment of [ { CI: 'true', MAKELORE_RELEASE_BUILD: '' }, { CI: '', MAKELORE_RELEASE_BUILD: '1' }, ]) { const result = spawnSync(process.execPath, ['scripts/sync-learning-player.mjs'], { cwd: repositoryRoot, encoding: 'utf8', env: { ...process.env, ...releaseEnvironment, MAKELORE_LEARNING_PLAYER_ARTIFACT: '', MAKELORE_LEARNING_SOURCE: resolve(repositoryRoot, 'arbitrary-local-source'), }, }); expect(result.status).not.toBe(0); expect(result.stderr).toContain('Release builds require MAKELORE_LEARNING_PLAYER_ARTIFACT'); } }); it('uses frozen installs in learning-player packaging workflows', () => { for (const workflow of [ '.github/workflows/package-win-manual.yml', '.github/workflows/release.yml', '.github/workflows/win-build-test.yml', ]) { expect(readRepositoryFile(workflow)).toContain('run: pnpm install --frozen-lockfile'); } }); it('blocks non-default manual refs before checkout or artifact secrets', () => { const workflow = readRepositoryFile('.github/workflows/package-win-manual.yml'); const guardIndex = workflow.indexOf('- name: Require the default branch'); const checkoutIndex = workflow.indexOf('- name: Checkout code'); const artifactIndex = workflow.indexOf('- name: Download verified OpenMAIC player artifact'); expect(workflow).not.toContain('inputs:'); expect(workflow).not.toContain('inputs.ref'); expect(workflow).toContain('if ("${{ github.ref }}" -ne $expectedRef)'); expect(workflow).toContain('ref: ${{ github.event.repository.default_branch }}'); expect(guardIndex).toBeGreaterThanOrEqual(0); expect(guardIndex).toBeLessThan(checkoutIndex); expect(checkoutIndex).toBeLessThan(artifactIndex); }); });