216 lines
6.5 KiB
TypeScript
216 lines
6.5 KiB
TypeScript
import {
|
|
applyRepositoryProfileServerMetadata,
|
|
type RepositoryProfile,
|
|
} from '@devrunbook/repository-intel'
|
|
import { describe, expect, it, vi } from 'vitest'
|
|
|
|
import type { SafePlaybookVersionProjection } from '../playbooks/playbook-catalog'
|
|
import type { repositoryProfileRevisions } from '../schema'
|
|
import {
|
|
DrizzleCompositionSourceReader,
|
|
type CompositionPlaybookCatalog,
|
|
type CompositionPlaybookVersionIdentityRowSource,
|
|
type CompositionProfileRevisionRowSource,
|
|
} from './composition-source-reader'
|
|
|
|
type RevisionRow = typeof repositoryProfileRevisions.$inferSelect
|
|
|
|
const workspaceId = '00000000-0000-4000-8000-000000000001'
|
|
const revisionId = '00000000-0000-4000-8000-000000000002'
|
|
|
|
function profile(): RepositoryProfile {
|
|
return applyRepositoryProfileServerMetadata(
|
|
{
|
|
apiVersion: 'devrunbook.io/v1alpha1',
|
|
kind: 'RepositoryProfile',
|
|
metadata: { name: 'Source fixture', revision: 1, source: 'manual' },
|
|
spec: {
|
|
repositoryType: 'single-app',
|
|
stack: {
|
|
languages: ['TypeScript'],
|
|
frameworks: [],
|
|
packageManagers: ['pnpm'],
|
|
databases: ['PostgreSQL'],
|
|
deploymentTypes: ['Docker'],
|
|
testFrameworks: ['Vitest'],
|
|
},
|
|
commands: [],
|
|
paths: {
|
|
applicationRoots: ['apps/web'],
|
|
testRoots: ['tests'],
|
|
documentationRoots: ['docs'],
|
|
generated: ['dist'],
|
|
protected: ['runtime'],
|
|
excluded: ['node_modules'],
|
|
},
|
|
policies: {
|
|
preserveBackwardCompatibility: true,
|
|
newDependencies: 'justify',
|
|
gitWrite: 'none',
|
|
migrations: 'reversible-only',
|
|
documentationRequired: true,
|
|
networkAccess: 'forbidden',
|
|
productionDataAccess: 'forbidden',
|
|
},
|
|
},
|
|
},
|
|
1,
|
|
)
|
|
}
|
|
|
|
function version(): SafePlaybookVersionProjection {
|
|
return {
|
|
id: '00000000-0000-4000-8000-000000000003',
|
|
playbookId: '00000000-0000-4000-8000-000000000004',
|
|
version: '1.2.3',
|
|
digest: 'a'.repeat(64),
|
|
lifecycle: 'validated',
|
|
manifest: {
|
|
metadata: {
|
|
slug: 'bounded-change',
|
|
version: '1.2.3',
|
|
title: 'Bounded change',
|
|
},
|
|
spec: { intent: { outcome: 'Make a bounded change.' } },
|
|
},
|
|
template: '# Task\n',
|
|
quality: {},
|
|
publishedAt: new Date('2026-07-27T12:00:00.000Z'),
|
|
}
|
|
}
|
|
|
|
function revision(document = profile()): RevisionRow {
|
|
return {
|
|
id: revisionId,
|
|
repositoryId: '00000000-0000-4000-8000-000000000005',
|
|
revisionNumber: 1,
|
|
profileJson: document,
|
|
sourceSnapshotId: null,
|
|
contentDigest: document.metadata.contentDigest!,
|
|
createdBy: '00000000-0000-4000-8000-000000000006',
|
|
createdAt: new Date('2026-07-27T12:00:00.000Z'),
|
|
}
|
|
}
|
|
|
|
function dependencies(
|
|
selectedVersion: SafePlaybookVersionProjection | null = version(),
|
|
selectedRevision: RevisionRow | null = revision(),
|
|
) {
|
|
const catalog: CompositionPlaybookCatalog = {
|
|
findVersionBySlug: vi.fn(async () => selectedVersion),
|
|
}
|
|
const rows: CompositionProfileRevisionRowSource = {
|
|
findRevisionForWorkspace: vi.fn(async () => selectedRevision),
|
|
}
|
|
const identities: CompositionPlaybookVersionIdentityRowSource = {
|
|
findPublishedIdentityForWorkspace: vi.fn(async () => ({
|
|
slug: 'bounded-change',
|
|
version: '1.2.3',
|
|
})),
|
|
}
|
|
return {
|
|
catalog,
|
|
rows,
|
|
identities,
|
|
reader: new DrizzleCompositionSourceReader(catalog, rows, identities),
|
|
}
|
|
}
|
|
|
|
describe('DrizzleCompositionSourceReader', () => {
|
|
it('uses exact published catalog semantics with workspace scope', async () => {
|
|
const target = dependencies()
|
|
await expect(
|
|
target.reader.findPublishedPlaybookVersion(
|
|
workspaceId,
|
|
'bounded-change',
|
|
'1.2.3',
|
|
),
|
|
).resolves.toEqual({
|
|
id: version().id,
|
|
slug: 'bounded-change',
|
|
version: '1.2.3',
|
|
digest: 'a'.repeat(64),
|
|
lifecycle: 'validated',
|
|
manifest: version().manifest,
|
|
template: '# Task\n',
|
|
})
|
|
expect(target.catalog.findVersionBySlug).toHaveBeenCalledWith(
|
|
'bounded-change',
|
|
'1.2.3',
|
|
undefined,
|
|
{ workspaceId },
|
|
)
|
|
})
|
|
|
|
it('returns generic null for missing or inaccessible playbooks', async () => {
|
|
const target = dependencies(null)
|
|
await expect(
|
|
target.reader.findPublishedPlaybookVersion(
|
|
workspaceId,
|
|
'bounded-change',
|
|
'1.2.3',
|
|
),
|
|
).resolves.toBeNull()
|
|
})
|
|
|
|
it('resolves a persisted version ID through its governed visible identity', async () => {
|
|
const target = dependencies()
|
|
await expect(
|
|
target.reader.findPublishedPlaybookVersionById(workspaceId, version().id),
|
|
).resolves.toMatchObject({ id: version().id, slug: 'bounded-change' })
|
|
expect(
|
|
target.identities.findPublishedIdentityForWorkspace,
|
|
).toHaveBeenCalledWith(workspaceId, version().id)
|
|
expect(target.catalog.findVersionBySlug).toHaveBeenCalledWith(
|
|
'bounded-change',
|
|
'1.2.3',
|
|
undefined,
|
|
{ workspaceId },
|
|
)
|
|
})
|
|
|
|
it('returns generic null for an inaccessible persisted version ID', async () => {
|
|
const target = dependencies()
|
|
vi.mocked(
|
|
target.identities.findPublishedIdentityForWorkspace,
|
|
).mockResolvedValue(null)
|
|
await expect(
|
|
target.reader.findPublishedPlaybookVersionById(workspaceId, version().id),
|
|
).resolves.toBeNull()
|
|
expect(target.catalog.findVersionBySlug).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('loads an exact immutable profile revision through workspace scoping', async () => {
|
|
const target = dependencies()
|
|
await expect(
|
|
target.reader.findRepositoryProfileRevision(workspaceId, revisionId),
|
|
).resolves.toMatchObject({
|
|
id: revisionId,
|
|
revisionNumber: 1,
|
|
contentDigest: profile().metadata.contentDigest,
|
|
profile: { metadata: { revision: 1 } },
|
|
})
|
|
expect(target.rows.findRevisionForWorkspace).toHaveBeenCalledWith(
|
|
workspaceId,
|
|
revisionId,
|
|
)
|
|
})
|
|
|
|
it('returns generic null for a missing or cross-workspace revision', async () => {
|
|
const target = dependencies(version(), null)
|
|
await expect(
|
|
target.reader.findRepositoryProfileRevision(workspaceId, revisionId),
|
|
).resolves.toBeNull()
|
|
})
|
|
|
|
it('fails closed when stored profile bytes and digest disagree', async () => {
|
|
const target = dependencies(version(), {
|
|
...revision(),
|
|
contentDigest: 'f'.repeat(64),
|
|
})
|
|
await expect(
|
|
target.reader.findRepositoryProfileRevision(workspaceId, revisionId),
|
|
).rejects.toThrow('failed integrity validation')
|
|
})
|
|
})
|