scripts/__tests__/skills-admin.test.ts
Rama main · réplica de lectura del repositorio original.
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { createHash } from 'node:crypto'
import yaml from 'js-yaml'
import { createQueuedMockSupabase } from '@/tests/helpers'
vi.mock('@supabase/supabase-js', () => ({ createClient: vi.fn() }))
vi.mock('node:fs/promises', () => ({ writeFile: vi.fn(), mkdir: vi.fn() }))
vi.mock('node:child_process', () => ({ execFileSync: vi.fn() }))
import { createClient } from '@supabase/supabase-js'
import { writeFile } from 'node:fs/promises'
import { execFileSync } from 'node:child_process'
import { runSkillsAdmin } from '../skills-admin'
const { supabase, enqueue, reset, findCall, findCalls } = createQueuedMockSupabase()
const id = 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa'
const body = 'Explain the evidence. Stage changes for approval.'
const bodyHash = createHash('sha256').update(body).digest('hex')
const submission = { id, body, submission_body_hash: bodyHash, name: 'Review workflow', description: 'Explain scoped proposals.', author_handle: 'public-author', share_confirmed_at: '2026-09-17', share_status: 'submitted', company_id: 'PRIVATE-COMPANY', created_by: 'PRIVATE-USER', published_atom_id: null }
const entry = { author: submission.author_handle, submissionHash: bodyHash, title: submission.name, description: submission.description, mcp_exposed: true, reviewedAt: '2026-09-17' }
const env = { SKILLS_SUPABASE_URL: 'https://metjnjrhvujscngnpzdv.supabase.co', SKILLS_SERVICE_ROLE_KEY: 'test-only' }
const run = (args: string[]) => runSkillsAdmin([...args, '--project', 'metjnjrhvujscngnpzdv'], env, '/fixture')
const markdown = (data: object) => `---\n${yaml.dump(data)}---\n\nPublic summary.\n`
beforeEach(() => {
vi.clearAllMocks(); reset()
vi.mocked(createClient).mockReturnValue(supabase as never)
vi.mocked(writeFile).mockResolvedValue(undefined)
vi.spyOn(console, 'log').mockImplementation(() => {})
})
afterEach(() => vi.restoreAllMocks())
describe('local human-gated skill review', () => {
it('requires explicit matching credentials and specific production write approval', async () => {
await expect(runSkillsAdmin(['list', '--project', 'different'], env)).rejects.toThrow('matching project')
await expect(runSkillsAdmin(['disable', '--project', 'pwxtzglxptnnvjrpixpg', '--slug', 'community/test'], { ...env, SKILLS_SUPABASE_URL: 'https://pwxtzglxptnnvjrpixpg.supabase.co' })).rejects.toThrow('production write approval')
expect(supabase.from).not.toHaveBeenCalled()
})
it('exports only reviewed public fields, never tenant or user identifiers', async () => {
enqueue({ data: submission })
await run(['prepare', '--id', id, '--slug', 'review-workflow', '--reviewed-at', '2026-09-17', '--review-confirmed'])
const writes = vi.mocked(writeFile).mock.calls
expect(writes).toHaveLength(3)
const content = writes.map((call) => String(call[1])).join('\n')
expect(content).toContain('public-author')
expect(content).toContain(bodyHash)
expect(content).not.toContain('PRIVATE-')
expect(content).not.toContain(id)
expect(writes.every((call) => (call[2] as { flag: string }).flag === 'wx')).toBe(true)
expect(findCall('company_skills', 'update')).toBeUndefined()
expect(execFileSync).not.toHaveBeenCalled()
})
it('rejects changed submitted text before any public file is written', async () => {
enqueue({ data: { ...submission, body: 'Changed after consent' } })
await expect(run(['prepare', '--id', id, '--slug', 'review-workflow', '--reviewed-at', '2026-09-17', '--review-confirmed'])).rejects.toThrow('frozen submission')
expect(writeFile).not.toHaveBeenCalled()
})
it('has no withdraw command: the author\'s withdrawal hides the text in the database', async () => {
await expect(run(['withdraw', '--id', id, '--slug', 'review-workflow'])).rejects.toThrow('Unknown command')
expect(findCall('agent_atom_registry', 'update')).toBeUndefined()
})
it('does not report an open PR as published', async () => {
enqueue({ data: submission })
vi.mocked(execFileSync).mockReturnValue(JSON.stringify({ state: 'OPEN', mergeCommit: null, files: [] }))
await expect(run(['published', '--id', id, '--slug', 'review-workflow', '--pr', '123'])).rejects.toThrow('not merged')
expect(findCall('company_skills', 'update')).toBeUndefined()
})
it('requires merged attribution and deployed body before marking published', async () => {
enqueue({ data: submission }); enqueue({ data: { body, reviewed_at: '2026-09-17T00:00:00+00:00' } }); enqueue({ data: { id } })
vi.mocked(execFileSync)
.mockReturnValueOnce(JSON.stringify({ state: 'MERGED', mergeCommit: { oid: 'commit' }, files: [{ path: 'registry/skills/review-workflow/SKILL.md' }, { path: 'registry/entries/review-workflow.mdx' }] }))
.mockReturnValueOnce(JSON.stringify({ content: Buffer.from(body).toString('base64') }))
.mockReturnValueOnce(JSON.stringify({ content: Buffer.from(markdown(entry)).toString('base64') }))
await run(['published', '--id', id, '--slug', 'review-workflow', '--pr', '123'])
expect(findCall('company_skills', 'update')?.[0]).toMatchObject({ share_status: 'published', published_atom_id: 'community/review-workflow', reviewed_at: '2026-09-17T00:00:00+00:00' })
expect(findCalls('company_skills', 'eq')).toContainEqual(['submission_body_hash', bodyHash])
expect(findCalls('company_skills', 'eq')).toContainEqual(['share_status', 'submitted'])
})
})