Files
DevRunbook-Public/tests/integration/generated-artifact.integration.test.ts
T
DevRunbook release export cfd2804e27
Managed validation / full (push) Successful in 3m18s
Publish DevRunbook source
2026-09-03 04:09:17 +02:00

161 lines
5.6 KiB
TypeScript

import { randomUUID } from 'node:crypto'
import { mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import path from 'node:path'
import {
createGeneratedArtifact,
downloadGeneratedArtifact,
} from '../../packages/application/src/index'
import { LocalArtifactStorage } from '../../packages/artifacts/src/index'
import {
closeDatabase,
DrizzleGeneratedArtifactStore,
DrizzleWorkspaceAuthorizationLookup,
getSqlClient,
} from '../../packages/db/src/index'
import { afterAll, describe, expect, it } from 'vitest'
const databaseUrl = process.env.DATABASE_URL
const integration = databaseUrl ? describe : describe.skip
const cleanupRoots: string[] = []
afterAll(async () => {
await closeDatabase()
await Promise.all(
cleanupRoots.splice(0).map((root) => rm(root, { recursive: true })),
)
})
integration('generated artifact persistence', () => {
it('persists authorized metadata and restart-readable verified bytes', async () => {
const sql = getSqlClient(databaseUrl)
const userId = randomUUID()
const workspaceId = randomUUID()
const otherWorkspaceId = randomUUID()
const playbookId = randomUUID()
const versionId = randomUUID()
const runId = randomUUID()
const artifactId = randomUUID()
const namespace = `artifact-integration-${randomUUID()}`
const artifactRoot = await mkdtemp(
path.join(tmpdir(), 'devrunbook-artifact-integration-'),
)
cleanupRoots.push(artifactRoot)
await sql.begin(async (transaction) => {
await transaction`
insert into users (id, email, display_name, password_hash, instance_role)
values (${userId}, ${`${userId}@example.invalid`}, 'Artifact Integration', 'not-a-login-credential', 'user')
`
await transaction`
insert into workspaces (id, name)
values (${workspaceId}, 'Artifact Workspace'), (${otherWorkspaceId}, 'Other Workspace')
`
await transaction`
insert into workspace_memberships (user_id, workspace_id, role)
values (${userId}, ${workspaceId}, 'editor')
`
await transaction`
insert into playbooks (id, workspace_id, logical_id, slug, namespace, source_type)
values (${playbookId}, ${workspaceId}, 'artifact-integration', 'artifact-integration', ${namespace}, 'private')
`
await transaction`
insert into playbook_versions (
id, playbook_id, semantic_version, lifecycle, package_api_version,
title, summary, category, risk_tier, package_json, template_text,
content_digest, created_by
) values (
${versionId}, ${playbookId}, '1.0.0', 'validated',
'devrunbook.playbook/v1.2', 'Artifact integration',
'Integration fixture', 'test', 'low', '{}'::jsonb, '# Test',
${'a'.repeat(64)}, ${userId}
)
`
await transaction`
insert into generated_runs (
id, workspace_id, playbook_version_id, playbook_snapshot_json,
repository_profile_snapshot_json, normalized_input_json,
policy_snapshot_json, provenance_json, lint_result_json,
rendered_prompt, render_digest, idempotency_key, generated_by
) values (
${runId}, ${workspaceId}, ${versionId}, '{}'::jsonb, null,
'{}'::jsonb, '{}'::jsonb, '[]'::jsonb,
'{"exportReadiness":"ready","findings":[]}'::jsonb,
'# Test', ${'b'.repeat(64)}, ${`artifact-${artifactId}`}, ${userId}
)
`
})
const metadata = new DrizzleGeneratedArtifactStore()
const authorization = new DrizzleWorkspaceAuthorizationLookup()
const content = new TextEncoder().encode('# Test\n')
const created = await createGeneratedArtifact(
{
authorization,
metadata,
storage: new LocalArtifactStorage(artifactRoot),
now: () => new Date('2026-07-27T12:00:00.000Z'),
},
{
actor: { userId },
workspaceId,
artifactId,
runId,
artifactType: 'markdown',
filename: 'test.md',
mediaType: 'text/markdown; charset=utf-8',
content,
},
)
expect(created.created).toBe(true)
const [persisted] = await sql<
{ id: string; runId: string; sha256: string; sizeBytes: string }[]
>`
select id, run_id as "runId", sha256, size_bytes as "sizeBytes"
from generated_artifacts
where id = ${artifactId}
`
expect(persisted).toMatchObject({
id: artifactId,
runId,
sha256: created.artifact.sha256,
sizeBytes: String(content.byteLength),
})
const downloaded = await downloadGeneratedArtifact(
{
authorization,
metadata: new DrizzleGeneratedArtifactStore(),
storage: new LocalArtifactStorage(artifactRoot),
},
{ actor: { userId }, workspaceId, artifactId },
)
expect(downloaded.content).toEqual(content)
expect(downloaded.artifact.sha256).toHaveLength(64)
await expect(
metadata.listByRunInWorkspace(runId, workspaceId),
).resolves.toMatchObject([
{ id: artifactId, runId, workspaceId, sha256: created.artifact.sha256 },
])
await expect(
metadata.listByRunInWorkspace(runId, otherWorkspaceId),
).resolves.toEqual([])
await expect(
downloadGeneratedArtifact(
{
authorization,
metadata,
storage: new LocalArtifactStorage(artifactRoot),
},
{ actor: { userId }, workspaceId: otherWorkspaceId, artifactId },
),
).rejects.toMatchObject({ code: 'workspace_access_denied' })
await sql`delete from workspaces where id in (${workspaceId}, ${otherWorkspaceId})`
await sql`delete from users where id = ${userId}`
})
})