867 lines
30 KiB
TypeScript
867 lines
30 KiB
TypeScript
import { afterEach, describe, expect, it } from 'vitest';
|
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
|
|
/**
|
|
* Red-first integration seam for #1051.
|
|
*
|
|
* Live broker grants and remote writes are deliberately not exercised here.
|
|
* The injected runner records exact commands and returns contract objects; the
|
|
* production path must call `mosaic cred`, never resolve a token itself.
|
|
*/
|
|
|
|
interface CommandRequest {
|
|
readonly program: 'git' | 'mosaic';
|
|
readonly args: readonly string[];
|
|
readonly cwd?: string;
|
|
readonly env: Readonly<Record<string, string>>;
|
|
readonly stdin?: Uint8Array;
|
|
}
|
|
|
|
interface CommandResult {
|
|
readonly status: number;
|
|
readonly stdout: string;
|
|
readonly stderr: string;
|
|
}
|
|
|
|
type CommandRunner = (request: CommandRequest) => CommandResult;
|
|
|
|
interface DoctorRuntimeReport {
|
|
readonly findings: readonly { code: string; repairable: boolean; reasonCode: string | null }[];
|
|
readonly access: {
|
|
readonly outcome: 'ok' | 'refused' | 'error' | 'indeterminate';
|
|
readonly exitCode: 0 | 10 | 20 | 30;
|
|
readonly reasonCode: string;
|
|
};
|
|
readonly refusalControl: {
|
|
readonly observed: boolean;
|
|
readonly reasonCode: string | null;
|
|
};
|
|
}
|
|
|
|
interface PublishEvidence {
|
|
readonly commit: string;
|
|
readonly remoteHead: string;
|
|
readonly reachable: boolean;
|
|
}
|
|
|
|
interface BrainRuntimeModule {
|
|
readonly systemCommandRunner: CommandRunner;
|
|
collectBrainDoctorReport(
|
|
input: {
|
|
readonly registrySource: string;
|
|
readonly targetGitUrl: string;
|
|
readonly brainNamespace: string;
|
|
readonly identity: string;
|
|
readonly root: string;
|
|
},
|
|
run: CommandRunner,
|
|
): DoctorRuntimeReport;
|
|
repairBrainDoctor(
|
|
input: {
|
|
readonly registrySource: string;
|
|
readonly targetGitUrl: string;
|
|
readonly brainNamespace: string;
|
|
readonly identity: string;
|
|
readonly root: string;
|
|
},
|
|
run: CommandRunner,
|
|
): DoctorRuntimeReport;
|
|
collectBrainRefusalControl(
|
|
input: {
|
|
readonly registrySource: string;
|
|
readonly targetGitUrl: string;
|
|
readonly brainNamespace: string;
|
|
readonly refusalIdentity: string;
|
|
},
|
|
run: CommandRunner,
|
|
): {
|
|
readonly observed: boolean;
|
|
readonly reasonCode: string | null;
|
|
readonly gitReasonCode: string;
|
|
readonly apiReasonCode: string;
|
|
};
|
|
publishBrainPaths(
|
|
input: {
|
|
readonly root: string;
|
|
readonly identity: string;
|
|
readonly entries: readonly { readonly path: string; readonly content: Uint8Array }[];
|
|
readonly message: string;
|
|
},
|
|
run: CommandRunner,
|
|
): PublishEvidence;
|
|
}
|
|
|
|
const MODULE_PATH = './brain-store-runtime.js';
|
|
const roots: string[] = [];
|
|
|
|
async function loadRuntime(requirement: string): Promise<BrainRuntimeModule> {
|
|
try {
|
|
return (await import(MODULE_PATH)) as BrainRuntimeModule;
|
|
} catch (error: unknown) {
|
|
const detail = error instanceof Error ? error.message : String(error);
|
|
throw new Error(`${requirement}: brain-store runtime is absent (${detail})`);
|
|
}
|
|
}
|
|
|
|
function tempRoot(): string {
|
|
const root = mkdtempSync(join(tmpdir(), 'mosaic-brain-runtime-'));
|
|
roots.push(root);
|
|
return root;
|
|
}
|
|
|
|
function registry(): string {
|
|
return JSON.stringify({
|
|
version: 1,
|
|
estates: [
|
|
{
|
|
name: 'homelab',
|
|
readOnlyControlIdentity: 'read-control',
|
|
hosts: [
|
|
{
|
|
host: 'git.mosaicstack.dev',
|
|
provider: 'gitea',
|
|
apiBaseUrl: 'https://git.mosaicstack.dev',
|
|
tokenPrefix: 'gitea-mosaicstack',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
function validateResult(
|
|
outcome: 'ok' | 'refused' | 'error' | 'indeterminate',
|
|
reasonCode: string,
|
|
): string {
|
|
const exits = { ok: 0, refused: 10, error: 20, indeterminate: 30 } as const;
|
|
return JSON.stringify({
|
|
schemaVersion: 1,
|
|
operation: 'validate',
|
|
outcome,
|
|
exitCode: exits[outcome],
|
|
retryable: false,
|
|
subject: {
|
|
identity: 'synthetic-no-token',
|
|
estate: 'homelab',
|
|
host: 'git.mosaicstack.dev',
|
|
repo: 'mosaicstack/mosaic-brain',
|
|
},
|
|
mutation: 'none',
|
|
reason: { code: reasonCode, message: 'non-secret' },
|
|
evidence: {
|
|
providerIdentity:
|
|
outcome === 'ok'
|
|
? {
|
|
login: 'synthetic-no-token',
|
|
endpoint: 'GET /api/v1/user',
|
|
contentType: 'application/json',
|
|
}
|
|
: null,
|
|
repositoryPermission:
|
|
outcome === 'ok'
|
|
? {
|
|
requested: 'write',
|
|
effective: 'write',
|
|
endpoint: 'GET /api/v1/repos/mosaicstack/mosaic-brain',
|
|
contentType: 'application/json',
|
|
}
|
|
: null,
|
|
writeDifferential:
|
|
outcome === 'ok'
|
|
? {
|
|
state: 'can-write',
|
|
credentialBinding: 'same-resolution',
|
|
transportPrincipal: 'synthetic-no-token',
|
|
authenticatedReceivePack: 'advertised',
|
|
readOnlyControl: {
|
|
identity: 'read-control',
|
|
providerPermission: 'read',
|
|
receivePack: 'refused',
|
|
},
|
|
unauthenticatedReceivePack: 'refused',
|
|
artifactCreated: false,
|
|
proves: 'non-secret evidence',
|
|
doesNotProve: 'branch update acceptance',
|
|
}
|
|
: null,
|
|
},
|
|
audit: { journalId: 'opaque', state: 'sealed' },
|
|
});
|
|
}
|
|
|
|
function requestHasSecretShape(request: CommandRequest): boolean {
|
|
return JSON.stringify(request).match(/authorization|password|\.token|token-dir/i) !== null;
|
|
}
|
|
|
|
afterEach((): void => {
|
|
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
|
|
});
|
|
|
|
describe('doctor runtime observation', (): void => {
|
|
it('runs a synthetic no-token positive control through mosaic cred even when the clone is missing', async (): Promise<void> => {
|
|
const runtime = await loadRuntime('MB-REQ-05 synthetic refusal control');
|
|
const root = join(tempRoot(), 'missing-brain');
|
|
const requests: CommandRequest[] = [];
|
|
const runner: CommandRunner = (request): CommandResult => {
|
|
requests.push(request);
|
|
return {
|
|
status: 10,
|
|
stdout: validateResult('refused', 'no-token-for-identity'),
|
|
stderr: 'refused reason=no-token-for-identity',
|
|
};
|
|
};
|
|
|
|
const report = runtime.collectBrainDoctorReport(
|
|
{
|
|
registrySource: registry(),
|
|
targetGitUrl: 'https://git.mosaicstack.dev/mosaicstack/stack.git',
|
|
brainNamespace: 'mosaicstack',
|
|
identity: 'synthetic-no-token',
|
|
root,
|
|
},
|
|
runner,
|
|
);
|
|
|
|
expect(requests).toHaveLength(1);
|
|
expect(requests[0]).toMatchObject({
|
|
program: 'mosaic',
|
|
args: [
|
|
'cred',
|
|
'validate',
|
|
'synthetic-no-token',
|
|
'--estate',
|
|
'homelab',
|
|
'--host',
|
|
'git.mosaicstack.dev',
|
|
'--repo',
|
|
'mosaicstack/mosaic-brain',
|
|
'--require',
|
|
'write',
|
|
'--json',
|
|
],
|
|
});
|
|
expect(requests.some(requestHasSecretShape)).toBe(false);
|
|
expect(report.refusalControl).toEqual({
|
|
observed: true,
|
|
reasonCode: 'no-token-for-identity',
|
|
});
|
|
expect(report.findings.map((finding) => finding.code)).toEqual(
|
|
expect.arrayContaining(['brain-clone-missing', 'brain-write-access-refused']),
|
|
);
|
|
});
|
|
|
|
it('treats process/object terminal-class disagreement as indeterminate', async (): Promise<void> => {
|
|
const runtime = await loadRuntime('MB-REQ-08 process/object disagreement');
|
|
const root = join(tempRoot(), 'missing-brain');
|
|
|
|
const report = runtime.collectBrainDoctorReport(
|
|
{
|
|
registrySource: registry(),
|
|
targetGitUrl: 'https://git.mosaicstack.dev/mosaicstack/stack.git',
|
|
brainNamespace: 'mosaicstack',
|
|
identity: 'synthetic-no-token',
|
|
root,
|
|
},
|
|
(): CommandResult => ({
|
|
status: 0,
|
|
stdout: validateResult('refused', 'no-token-for-identity'),
|
|
stderr: '',
|
|
}),
|
|
);
|
|
|
|
expect(report.access).toMatchObject({
|
|
outcome: 'indeterminate',
|
|
exitCode: 30,
|
|
reasonCode: 'unexpected-provider-shape',
|
|
});
|
|
expect(report.refusalControl.observed).toBe(false);
|
|
});
|
|
|
|
it('rejects a self-consistent broker object for a different declared subject', async (): Promise<void> => {
|
|
const runtime = await loadRuntime('MB-REQ-04 caller subject binding');
|
|
const root = join(tempRoot(), 'missing-brain');
|
|
|
|
const report = runtime.collectBrainDoctorReport(
|
|
{
|
|
registrySource: registry(),
|
|
targetGitUrl: 'https://git.mosaicstack.dev/mosaicstack/stack.git',
|
|
brainNamespace: 'mosaicstack',
|
|
identity: 'seat-a',
|
|
root,
|
|
},
|
|
(): CommandResult => ({
|
|
status: 0,
|
|
stdout: validateResult('ok', 'validation-verified'),
|
|
stderr: '',
|
|
}),
|
|
);
|
|
|
|
expect(report.access).toMatchObject({
|
|
outcome: 'indeterminate',
|
|
exitCode: 30,
|
|
reasonCode: 'unexpected-provider-shape',
|
|
});
|
|
});
|
|
|
|
it('reads wrong remote and dirty state from git while preserving credential identity binding', async (): Promise<void> => {
|
|
const runtime = await loadRuntime('MB-REQ-08 real git observation seam');
|
|
const root = join(tempRoot(), 'brain');
|
|
mkdirSync(join(root, '.git'), { recursive: true });
|
|
const requests: CommandRequest[] = [];
|
|
const runner: CommandRunner = (request): CommandResult => {
|
|
requests.push(request);
|
|
if (request.program === 'mosaic') {
|
|
const source = validateResult('ok', 'validation-verified').replaceAll(
|
|
'synthetic-no-token',
|
|
'seat-a',
|
|
);
|
|
return { status: 0, stdout: source, stderr: '' };
|
|
}
|
|
const command = request.args.join(' ');
|
|
if (command.includes('rev-parse --is-inside-work-tree')) {
|
|
return { status: 0, stdout: 'true\n', stderr: '' };
|
|
}
|
|
if (command.includes('remote get-url origin')) {
|
|
return {
|
|
status: 0,
|
|
stdout: 'https://git.uscllc.com/usc/mosaic-brain.git\n',
|
|
stderr: '',
|
|
};
|
|
}
|
|
if (command.includes('branch --show-current')) {
|
|
return { status: 0, stdout: 'main\n', stderr: '' };
|
|
}
|
|
if (command.includes('status --porcelain')) {
|
|
return { status: 0, stdout: '?? uncommitted.md\n', stderr: '' };
|
|
}
|
|
return { status: 99, stdout: '', stderr: 'unexpected command' };
|
|
};
|
|
|
|
const report = runtime.collectBrainDoctorReport(
|
|
{
|
|
registrySource: registry(),
|
|
targetGitUrl: 'https://git.mosaicstack.dev/mosaicstack/stack.git',
|
|
brainNamespace: 'mosaicstack',
|
|
identity: 'seat-a',
|
|
root,
|
|
},
|
|
runner,
|
|
);
|
|
|
|
expect(report.findings.map((finding) => finding.code)).toEqual(
|
|
expect.arrayContaining(['brain-remote-mismatch', 'brain-uncommitted-state']),
|
|
);
|
|
expect(requests.filter((request) => request.program === 'git')).toHaveLength(4);
|
|
for (const request of requests) {
|
|
expect(request.env['MOSAIC_GIT_IDENTITY']).toBe('seat-a');
|
|
expect(request.env['GIT_TERMINAL_PROMPT']).toBe('0');
|
|
}
|
|
});
|
|
|
|
it('repairs write refusal through mosaic cred, revalidates, then clones and verifies the resulting object', async (): Promise<void> => {
|
|
const runtime = await loadRuntime('MB-REQ-08 broker-only doctor repair');
|
|
const root = join(tempRoot(), 'brain');
|
|
const requests: CommandRequest[] = [];
|
|
let validationCount = 0;
|
|
const runner: CommandRunner = (request): CommandResult => {
|
|
requests.push(request);
|
|
if (request.program === 'mosaic' && request.args[1] === 'validate') {
|
|
validationCount += 1;
|
|
if (validationCount === 1) {
|
|
return {
|
|
status: 10,
|
|
stdout: validateResult('refused', 'no-token-for-identity').replaceAll(
|
|
'synthetic-no-token',
|
|
'seat-a',
|
|
),
|
|
stderr: 'refused reason=no-token-for-identity',
|
|
};
|
|
}
|
|
const source = validateResult('ok', 'validation-verified').replaceAll(
|
|
'synthetic-no-token',
|
|
'seat-a',
|
|
);
|
|
return { status: 0, stdout: source, stderr: '' };
|
|
}
|
|
if (request.program === 'mosaic' && request.args[1] === 'grant') {
|
|
return { status: 0, stdout: '{"outcome":"ok"}\n', stderr: '' };
|
|
}
|
|
if (request.program === 'git' && request.args[0] === 'clone') {
|
|
mkdirSync(join(root, '.git'), { recursive: true });
|
|
return { status: 0, stdout: '', stderr: '' };
|
|
}
|
|
const command = request.args.join(' ');
|
|
if (command.includes('rev-parse --is-inside-work-tree')) {
|
|
return { status: 0, stdout: 'true\n', stderr: '' };
|
|
}
|
|
if (command.includes('remote get-url origin')) {
|
|
return {
|
|
status: 0,
|
|
stdout: 'https://git.mosaicstack.dev/mosaicstack/mosaic-brain.git\n',
|
|
stderr: '',
|
|
};
|
|
}
|
|
if (command.includes('branch --show-current')) {
|
|
return { status: 0, stdout: 'main\n', stderr: '' };
|
|
}
|
|
if (command.includes('status --porcelain')) {
|
|
return { status: 0, stdout: '', stderr: '' };
|
|
}
|
|
return { status: 99, stdout: '', stderr: 'unexpected command' };
|
|
};
|
|
|
|
const report = runtime.repairBrainDoctor(
|
|
{
|
|
registrySource: registry(),
|
|
targetGitUrl: 'https://git.mosaicstack.dev/mosaicstack/stack.git',
|
|
brainNamespace: 'mosaicstack',
|
|
identity: 'seat-a',
|
|
root,
|
|
},
|
|
runner,
|
|
);
|
|
|
|
expect(report.findings).toEqual([]);
|
|
const sequence = requests.map(
|
|
(request) => `${request.program}:${request.args[1] ?? request.args[0]}`,
|
|
);
|
|
expect(sequence.slice(0, 4)).toEqual([
|
|
'mosaic:validate',
|
|
'mosaic:grant',
|
|
'mosaic:validate',
|
|
'git:--branch',
|
|
]);
|
|
expect(requests.some(requestHasSecretShape)).toBe(false);
|
|
});
|
|
|
|
it('does not clone when broker revalidation remains refused after a grant attempt', async (): Promise<void> => {
|
|
const runtime = await loadRuntime('MB-REQ-08 failed grant remains visible');
|
|
const root = join(tempRoot(), 'brain');
|
|
const requests: CommandRequest[] = [];
|
|
|
|
const report = runtime.repairBrainDoctor(
|
|
{
|
|
registrySource: registry(),
|
|
targetGitUrl: 'https://git.mosaicstack.dev/mosaicstack/stack.git',
|
|
brainNamespace: 'mosaicstack',
|
|
identity: 'synthetic-no-token',
|
|
root,
|
|
},
|
|
(request): CommandResult => {
|
|
requests.push(request);
|
|
if (request.program === 'mosaic' && request.args[1] === 'grant') {
|
|
return { status: 10, stdout: '{"outcome":"refused"}\n', stderr: 'refused' };
|
|
}
|
|
return {
|
|
status: 10,
|
|
stdout: validateResult('refused', 'no-token-for-identity'),
|
|
stderr: 'refused reason=no-token-for-identity',
|
|
};
|
|
},
|
|
);
|
|
|
|
expect(report.findings.map((finding) => finding.code)).toEqual(
|
|
expect.arrayContaining(['brain-clone-missing', 'brain-write-access-refused']),
|
|
);
|
|
expect(requests.some((request) => request.program === 'git')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('R5 production refusal control', (): void => {
|
|
it('requires matching refusal on both Git transport and broker API axes', async (): Promise<void> => {
|
|
const runtime = await loadRuntime('MB-REQ-05 production both-axis refusal');
|
|
const requests: CommandRequest[] = [];
|
|
|
|
const result = runtime.collectBrainRefusalControl(
|
|
{
|
|
registrySource: registry(),
|
|
targetGitUrl: 'https://git.mosaicstack.dev/mosaicstack/stack.git',
|
|
brainNamespace: 'mosaicstack',
|
|
refusalIdentity: 'synthetic-no-token',
|
|
},
|
|
(request): CommandResult => {
|
|
requests.push(request);
|
|
if (request.program === 'mosaic') {
|
|
return {
|
|
status: 10,
|
|
stdout: validateResult('refused', 'no-token-for-identity'),
|
|
stderr: 'refused reason=no-token-for-identity',
|
|
};
|
|
}
|
|
return {
|
|
status: 128,
|
|
stdout: '',
|
|
stderr: 'credential helper refused reason=no-token-for-identity',
|
|
};
|
|
},
|
|
);
|
|
|
|
expect(result).toEqual({
|
|
observed: true,
|
|
reasonCode: 'no-token-for-identity',
|
|
gitReasonCode: 'no-token-for-identity',
|
|
apiReasonCode: 'no-token-for-identity',
|
|
});
|
|
expect(requests.map((request) => request.program)).toEqual(['mosaic', 'git']);
|
|
expect(requests[1]?.args).toEqual([
|
|
'ls-remote',
|
|
'https://git.mosaicstack.dev/mosaicstack/mosaic-brain.git',
|
|
'HEAD',
|
|
]);
|
|
expect(
|
|
requests.every((request) => request.env['MOSAIC_GIT_IDENTITY'] === 'synthetic-no-token'),
|
|
).toBe(true);
|
|
expect(requests.some(requestHasSecretShape)).toBe(false);
|
|
});
|
|
|
|
it('fails when the API refuses but Git transport accepts the out-of-estate identity', async (): Promise<void> => {
|
|
const runtime = await loadRuntime('MB-REQ-05 production axis disagreement');
|
|
|
|
const result = runtime.collectBrainRefusalControl(
|
|
{
|
|
registrySource: registry(),
|
|
targetGitUrl: 'https://git.mosaicstack.dev/mosaicstack/stack.git',
|
|
brainNamespace: 'mosaicstack',
|
|
refusalIdentity: 'synthetic-no-token',
|
|
},
|
|
(request): CommandResult =>
|
|
request.program === 'mosaic'
|
|
? {
|
|
status: 10,
|
|
stdout: validateResult('refused', 'no-token-for-identity'),
|
|
stderr: 'refused reason=no-token-for-identity',
|
|
}
|
|
: { status: 0, stdout: 'refs are visible', stderr: '' },
|
|
);
|
|
|
|
expect(result).toMatchObject({
|
|
observed: false,
|
|
reasonCode: 'permission-evidence-disagrees',
|
|
gitReasonCode: 'transport-accepted',
|
|
apiReasonCode: 'no-token-for-identity',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('push-on-write publication', (): void => {
|
|
it('publishes approved in-memory blobs despite path substitution and excludes unrelated staged secrets', async (): Promise<void> => {
|
|
const runtime = await loadRuntime('MB-REQ-06 isolated publication index');
|
|
const root = tempRoot();
|
|
const remote = join(root, 'remote.git');
|
|
const checkout = join(root, 'brain');
|
|
execFileSync('git', ['init', '--bare', '--initial-branch=main', remote]);
|
|
execFileSync('git', ['init', '--initial-branch=main', checkout]);
|
|
writeFileSync(join(checkout, 'README.md'), 'brain\n');
|
|
execFileSync('git', ['-C', checkout, 'add', 'README.md']);
|
|
execFileSync('git', [
|
|
'-C',
|
|
checkout,
|
|
'-c',
|
|
'user.name=fixture',
|
|
'-c',
|
|
'[email protected]',
|
|
'commit',
|
|
'-m',
|
|
'seed',
|
|
]);
|
|
execFileSync('git', ['-C', checkout, 'remote', 'add', 'origin', remote]);
|
|
execFileSync('git', ['-C', checkout, 'push', '-u', 'origin', 'main']);
|
|
writeFileSync(join(checkout, '.env'), 'TEST_ONLY_SECRET=fixture-value\n');
|
|
execFileSync('git', ['-C', checkout, 'add', '-f', '.env']);
|
|
const approved = join(checkout, 'finding.md');
|
|
writeFileSync(approved, 'TEST_ONLY_SECRET=path-substitution\n');
|
|
|
|
const evidence = runtime.publishBrainPaths(
|
|
{
|
|
root: checkout,
|
|
identity: 'seat-a',
|
|
entries: [{ path: approved, content: new TextEncoder().encode('approved finding\n') }],
|
|
message: 'append approved finding',
|
|
},
|
|
runtime.systemCommandRunner,
|
|
);
|
|
|
|
const committed = execFileSync('git', [
|
|
'-C',
|
|
checkout,
|
|
'diff-tree',
|
|
'--no-commit-id',
|
|
'--name-only',
|
|
'-r',
|
|
evidence.commit,
|
|
]).toString();
|
|
const staged = execFileSync('git', [
|
|
'-C',
|
|
checkout,
|
|
'diff',
|
|
'--cached',
|
|
'--name-only',
|
|
]).toString();
|
|
const committedContent = execFileSync('git', [
|
|
'-C',
|
|
checkout,
|
|
'show',
|
|
`${evidence.commit}:finding.md`,
|
|
]).toString();
|
|
expect(committed.trim().split('\n')).toEqual(['finding.md']);
|
|
expect(committedContent).toBe('approved finding\n');
|
|
expect(staged.trim().split('\n')).toContain('.env');
|
|
expect(evidence.reachable).toBe(true);
|
|
});
|
|
|
|
it('commits with command-scoped identity, pushes immediately, and proves reachability from origin/main', async (): Promise<void> => {
|
|
const runtime = await loadRuntime('MB-REQ-07 publish-on-write reachability');
|
|
const root = tempRoot();
|
|
const requests: CommandRequest[] = [];
|
|
const commit = 'a'.repeat(40);
|
|
const remoteHead = 'b'.repeat(40);
|
|
const runner: CommandRunner = (request): CommandResult => {
|
|
requests.push(request);
|
|
const command = request.args.join(' ');
|
|
if (command.includes('hash-object')) {
|
|
return { status: 0, stdout: `${'f'.repeat(40)}\n`, stderr: '' };
|
|
}
|
|
if (command.includes('rev-parse') && request.args.at(-1)?.includes(':')) {
|
|
return { status: 0, stdout: `${'f'.repeat(40)}\n`, stderr: '' };
|
|
}
|
|
if (command.includes('rev-parse HEAD'))
|
|
return { status: 0, stdout: `${commit}\n`, stderr: '' };
|
|
if (command.includes('rev-parse origin/main')) {
|
|
return { status: 0, stdout: `${remoteHead}\n`, stderr: '' };
|
|
}
|
|
if (command.includes('diff --cached --quiet')) {
|
|
return { status: 1, stdout: '', stderr: '' };
|
|
}
|
|
if (command.includes('diff-tree')) {
|
|
return { status: 0, stdout: '.gitignore\0lanes/lane-a/.gitkeep\0', stderr: '' };
|
|
}
|
|
if (command.includes('show -s')) {
|
|
return {
|
|
status: 0,
|
|
stdout: 'seat-a\[email protected]\0seat-a\[email protected]\n',
|
|
stderr: '',
|
|
};
|
|
}
|
|
return { status: 0, stdout: '', stderr: '' };
|
|
};
|
|
|
|
const evidence = runtime.publishBrainPaths(
|
|
{
|
|
root,
|
|
identity: 'seat-a',
|
|
entries: [
|
|
{ path: join(root, '.gitignore'), content: new TextEncoder().encode('*.token\n') },
|
|
{ path: join(root, 'lanes', 'lane-a', '.gitkeep'), content: new Uint8Array() },
|
|
],
|
|
message: 'migrate lane-a state',
|
|
},
|
|
runner,
|
|
);
|
|
|
|
expect(evidence).toEqual({ commit, remoteHead, reachable: true });
|
|
const rendered = requests.map((request) => `${request.program} ${request.args.join(' ')}`);
|
|
expect(rendered).toEqual(
|
|
expect.arrayContaining([
|
|
expect.stringMatching(/git -C .* hash-object -w --stdin/),
|
|
expect.stringMatching(/git -C .* update-index --add --cacheinfo 100644 .* \.gitignore/),
|
|
expect.stringMatching(
|
|
/git -C .* update-index --add --cacheinfo 100644 .* lanes\/lane-a\/\.gitkeep/,
|
|
),
|
|
expect.stringMatching(
|
|
/git -C .* -c user\.name=seat-a -c user\.email=seat-a@fleet\.mosaicstack\.dev commit/,
|
|
),
|
|
expect.stringMatching(/git -C .* push origin HEAD:main/),
|
|
expect.stringMatching(/git -C .* fetch origin main/),
|
|
expect.stringMatching(/git -C .* merge-base --is-ancestor/),
|
|
]),
|
|
);
|
|
const pushIndex = rendered.findIndex((command) => command.includes(' push origin HEAD:main'));
|
|
const fetchIndex = rendered.findIndex((command) => command.includes(' fetch origin main'));
|
|
expect(pushIndex).toBeGreaterThan(-1);
|
|
expect(fetchIndex).toBeGreaterThan(pushIndex);
|
|
expect(rendered.join('\n')).not.toMatch(/timer|cron|interval/);
|
|
expect(requests.some(requestHasSecretShape)).toBe(false);
|
|
});
|
|
|
|
it('rebases and retries a rejected concurrent append-only push instead of choosing last-writer-wins', async (): Promise<void> => {
|
|
const runtime = await loadRuntime('MB-REQ-07 append-only multi-host conflict');
|
|
const root = tempRoot();
|
|
const requests: CommandRequest[] = [];
|
|
const firstCommit = 'a'.repeat(40);
|
|
const rebasedCommit = 'c'.repeat(40);
|
|
const remoteHead = 'd'.repeat(40);
|
|
let pushes = 0;
|
|
let rebased = false;
|
|
|
|
const evidence = runtime.publishBrainPaths(
|
|
{
|
|
root,
|
|
identity: 'seat-a',
|
|
entries: [
|
|
{
|
|
path: join(root, 'lanes', 'lane-a', 'findings', 'host-a.md'),
|
|
content: new TextEncoder().encode('finding\n'),
|
|
},
|
|
],
|
|
message: 'append host-a finding',
|
|
},
|
|
(request): CommandResult => {
|
|
requests.push(request);
|
|
const command = request.args.join(' ');
|
|
if (command.includes('hash-object')) {
|
|
return { status: 0, stdout: `${'f'.repeat(40)}\n`, stderr: '' };
|
|
}
|
|
if (command.includes('rev-parse') && request.args.at(-1)?.includes(':')) {
|
|
return { status: 0, stdout: `${'f'.repeat(40)}\n`, stderr: '' };
|
|
}
|
|
if (command.includes('push origin HEAD:main')) {
|
|
pushes += 1;
|
|
return pushes === 1
|
|
? { status: 1, stdout: '', stderr: 'non-fast-forward' }
|
|
: { status: 0, stdout: '', stderr: '' };
|
|
}
|
|
if (command.includes('rebase origin/main')) {
|
|
rebased = true;
|
|
return { status: 0, stdout: '', stderr: '' };
|
|
}
|
|
if (command.includes('rev-parse HEAD')) {
|
|
return {
|
|
status: 0,
|
|
stdout: `${rebased ? rebasedCommit : firstCommit}\n`,
|
|
stderr: '',
|
|
};
|
|
}
|
|
if (command.includes('rev-parse origin/main')) {
|
|
return { status: 0, stdout: `${remoteHead}\n`, stderr: '' };
|
|
}
|
|
if (command.includes('diff --cached --quiet')) {
|
|
return { status: 1, stdout: '', stderr: '' };
|
|
}
|
|
if (command.includes('diff-tree')) {
|
|
return {
|
|
status: 0,
|
|
stdout: 'lanes/lane-a/findings/host-a.md\0',
|
|
stderr: '',
|
|
};
|
|
}
|
|
if (command.includes('show -s')) {
|
|
return {
|
|
status: 0,
|
|
stdout: 'seat-a\[email protected]\0seat-a\[email protected]\n',
|
|
stderr: '',
|
|
};
|
|
}
|
|
return { status: 0, stdout: '', stderr: '' };
|
|
},
|
|
);
|
|
|
|
const rendered = requests.map((request) => request.args.join(' '));
|
|
const firstPush = rendered.findIndex((command) => command.includes('push origin HEAD:main'));
|
|
const rebase = rendered.findIndex((command) => command.includes('rebase origin/main'));
|
|
const secondPush = rendered
|
|
.map((command): boolean => command.includes('push origin HEAD:main'))
|
|
.lastIndexOf(true);
|
|
expect(firstPush).toBeGreaterThan(-1);
|
|
expect(rebase).toBeGreaterThan(firstPush);
|
|
expect(secondPush).toBeGreaterThan(rebase);
|
|
expect(pushes).toBe(2);
|
|
expect(evidence).toEqual({ commit: rebasedCommit, remoteHead, reachable: true });
|
|
});
|
|
|
|
it('re-proves reachability without inventing a commit when content was already published', async (): Promise<void> => {
|
|
const runtime = await loadRuntime('MB-REQ-07 idempotent publication read-back');
|
|
const root = tempRoot();
|
|
const commit = 'e'.repeat(40);
|
|
const requests: CommandRequest[] = [];
|
|
|
|
const evidence = runtime.publishBrainPaths(
|
|
{
|
|
root,
|
|
identity: 'seat-a',
|
|
entries: [
|
|
{
|
|
path: join(root, 'lanes', 'lane-a', 'already-present.md'),
|
|
content: new TextEncoder().encode('existing\n'),
|
|
},
|
|
],
|
|
message: 'append existing finding',
|
|
},
|
|
(request): CommandResult => {
|
|
requests.push(request);
|
|
const command = request.args.join(' ');
|
|
if (command.includes('hash-object')) {
|
|
return { status: 0, stdout: `${'f'.repeat(40)}\n`, stderr: '' };
|
|
}
|
|
if (command.includes(' commit ')) {
|
|
return { status: 1, stdout: '', stderr: 'nothing to commit' };
|
|
}
|
|
if (command.includes('diff --cached --quiet')) {
|
|
return { status: 0, stdout: '', stderr: '' };
|
|
}
|
|
if (command.includes('rev-parse HEAD') || command.includes('rev-parse origin/main')) {
|
|
return { status: 0, stdout: `${commit}\n`, stderr: '' };
|
|
}
|
|
return { status: 0, stdout: '', stderr: '' };
|
|
},
|
|
);
|
|
|
|
expect(evidence).toEqual({ commit, remoteHead: commit, reachable: true });
|
|
expect(requests.some((request) => request.args.includes('push'))).toBe(false);
|
|
});
|
|
|
|
it('does not manufacture reachability when merge-base rejects the new commit', async (): Promise<void> => {
|
|
const runtime = await loadRuntime('MB-REQ-07 publication reachability negative control');
|
|
const root = tempRoot();
|
|
const commit = 'a'.repeat(40);
|
|
const remoteHead = 'b'.repeat(40);
|
|
|
|
const evidence = runtime.publishBrainPaths(
|
|
{
|
|
root,
|
|
identity: 'seat-a',
|
|
entries: [
|
|
{ path: join(root, '.gitignore'), content: new TextEncoder().encode('*.token\n') },
|
|
],
|
|
message: 'seed brain',
|
|
},
|
|
(request): CommandResult => {
|
|
const command = request.args.join(' ');
|
|
if (command.includes('hash-object')) {
|
|
return { status: 0, stdout: `${'f'.repeat(40)}\n`, stderr: '' };
|
|
}
|
|
if (command.includes('rev-parse') && request.args.at(-1)?.includes(':')) {
|
|
return { status: 0, stdout: `${'f'.repeat(40)}\n`, stderr: '' };
|
|
}
|
|
if (command.includes('rev-parse HEAD')) {
|
|
return { status: 0, stdout: `${commit}\n`, stderr: '' };
|
|
}
|
|
if (command.includes('rev-parse origin/main')) {
|
|
return { status: 0, stdout: `${remoteHead}\n`, stderr: '' };
|
|
}
|
|
if (command.includes('diff --cached --quiet')) {
|
|
return { status: 1, stdout: '', stderr: '' };
|
|
}
|
|
if (command.includes('diff-tree')) {
|
|
return { status: 0, stdout: '.gitignore\0', stderr: '' };
|
|
}
|
|
if (command.includes('show -s')) {
|
|
return {
|
|
status: 0,
|
|
stdout: 'seat-a\[email protected]\0seat-a\[email protected]\n',
|
|
stderr: '',
|
|
};
|
|
}
|
|
if (command.includes('merge-base --is-ancestor')) {
|
|
return { status: 1, stdout: '', stderr: '' };
|
|
}
|
|
return { status: 0, stdout: '', stderr: '' };
|
|
},
|
|
);
|
|
|
|
expect(evidence).toEqual({ commit, remoteHead, reachable: false });
|
|
});
|
|
});
|