import { expect, test } from 'bun:test'; import { readFileSync } from 'node:fs'; const workflow = readFileSync('.gitea/workflows/ci.yml', 'utf8'); const compose = readFileSync('compose.yaml', 'utf8'); const dockerfile = readFileSync('Dockerfile', 'utf8'); const dockerignore = readFileSync('.dockerignore', 'utf8'); const convexConfig = readFileSync('scripts/configure-convex.ts', 'utf8'); const chatRoute = readFileSync('src/routes/api/chat/+server.ts', 'utf8'); const syncRoute = readFileSync('src/routes/api/sync/+server.ts', 'utf8'); const layoutRoute = readFileSync('src/routes/+layout.server.ts', 'utf8'); test('Gitea workflow keeps source checks separate and holds unverified container CI', () => { expect(workflow).toContain('pull_request:'); expect(workflow).toContain('if: ${{ false }}'); expect(workflow).toContain('GITHUB_RUN_ID is required'); expect(workflow).toContain('RELEASE_ENV=staging'); expect(workflow).toContain('bun run test:release-gates'); expect(workflow).toContain('docker compose config --quiet'); expect(workflow).toContain('docker build --build-arg APP_RELEASE_ID=fixture-ci'); expect(workflow).toContain('GITHUB_RUN_ATTEMPT'); expect(workflow).toContain('docker image rm "$image"'); expect(workflow).toContain('--network none'); }); test('Authenticated route limits remain user-keyed after rollout gates', () => { expect(chatRoute).toContain('chat:${current.userId}'); expect(syncRoute).toContain('sync:${current.userId}'); expect(chatRoute.indexOf('protectMutation')).toBeLessThan(chatRoute.indexOf('const current = await session(event)')); expect(syncRoute.indexOf('protectMutation')).toBeLessThan(syncRoute.indexOf('const current = await session(event)')); expect(chatRoute.indexOf("capabilityEnabled('assistant_dispatch'")).toBeLessThan(chatRoute.indexOf('const current = await session(event)')); expect(syncRoute.indexOf("capabilityEnabled('asset_sync'")).toBeLessThan(syncRoute.indexOf('const current = await session(event)')); expect(layoutRoute).toContain('assistant_dispatch'); expect(layoutRoute).toContain('sessionNeeded'); }); test('Compose and Docker defaults are isolated and digest-pinned', () => { expect(compose).toMatch(/sync-worker:\n\s+profiles: \["worker"\]/); expect(compose).toContain('CONVEX_SYNC_WORKER_SECRET: ${CONVEX_SYNC_WORKER_SECRET:-}'); expect(compose).toContain('CONVEX_BIND_ADDRESS:-127.0.0.1'); expect(compose).toContain('WEB_BIND_ADDRESS:-127.0.0.1'); expect(compose).toMatch(/CONVEX_IMAGE:-[^\n]*@sha256:[a-f0-9]{64}/); expect(compose).toMatch(/CONVEX_DASHBOARD_IMAGE:-[^\n]*@sha256:[a-f0-9]{64}/); const bases = [...dockerfile.matchAll(/^FROM\s+([^\s]+)/gim)].map((match) => match[1]); expect(bases.length).toBeGreaterThan(0); expect(bases.every((image) => /@sha256:[a-f0-9]{64}$/i.test(image))).toBe(true); for (const pattern of ['*.pem', '*.key', '*.p12', '*.pfx', 'credentials*', 'secrets*', '.npmrc']) { expect(dockerignore).toContain(pattern); } expect(convexConfig).toContain("'APP_CAPABILITY_ALLOWLIST'"); });