mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-13 08:15:14 +02:00
92 lines
3.7 KiB
TypeScript
92 lines
3.7 KiB
TypeScript
import { mkdir, mkdtemp, readFile, writeFile } from 'node:fs/promises';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
import { GitService } from '../../core/index.js';
|
|
import { FileIngestTraceWriter } from '../ingest-trace.js';
|
|
import { integrateWorkUnitPatch } from './patch-integrator.js';
|
|
|
|
async function makeRepo() {
|
|
const homeDir = await mkdtemp(join(tmpdir(), 'ktx-integrate-'));
|
|
const configDir = join(homeDir, 'config');
|
|
const git = new GitService({
|
|
storage: { configDir, homeDir },
|
|
git: {
|
|
userName: 'System User',
|
|
userEmail: 'system@example.com',
|
|
bootstrapMessage: 'init',
|
|
bootstrapAuthor: 'system',
|
|
bootstrapAuthorEmail: 'system@example.com',
|
|
},
|
|
});
|
|
await git.onModuleInit();
|
|
await mkdir(join(configDir, 'wiki/global'), { recursive: true });
|
|
await writeFile(join(configDir, 'wiki/global/a.md'), 'old\n');
|
|
await git.commitFiles(['wiki/global/a.md'], 'base', 'System User', 'system@example.com');
|
|
return { homeDir, configDir, git, baseSha: await git.revParseHead() };
|
|
}
|
|
|
|
describe('integrateWorkUnitPatch', () => {
|
|
it('applies a clean patch, runs semantic gates, and commits accepted changes', async () => {
|
|
const { homeDir, configDir, git, baseSha } = await makeRepo();
|
|
const childDir = join(homeDir, 'child');
|
|
await git.addWorktree(childDir, 'child', baseSha);
|
|
const childGit = git.forWorktree(childDir);
|
|
await writeFile(join(childDir, 'wiki/global/a.md'), 'new\n');
|
|
await childGit.commitFiles(['wiki/global/a.md'], 'edit', 'System User', 'system@example.com');
|
|
const patchPath = join(homeDir, 'patches/wu.patch');
|
|
await childGit.writeBinaryNoRenamePatch(baseSha, 'HEAD', patchPath);
|
|
const trace = new FileIngestTraceWriter({
|
|
tracePath: join(homeDir, '.ktx/ingest-traces/job-1/trace.jsonl'),
|
|
jobId: 'job-1',
|
|
connectionId: 'c1',
|
|
sourceKey: 'fake',
|
|
level: 'trace',
|
|
});
|
|
|
|
const result = await integrateWorkUnitPatch({
|
|
unitKey: 'wu-1',
|
|
patchPath,
|
|
integrationGit: git,
|
|
trace,
|
|
author: { name: 'KTX Test', email: 'system@ktx.local' },
|
|
validateAppliedTree: vi.fn().mockResolvedValue(undefined),
|
|
slDisallowed: false,
|
|
});
|
|
|
|
expect(result.status).toBe('accepted');
|
|
await expect(readFile(join(configDir, 'wiki/global/a.md'), 'utf-8')).resolves.toBe('new\n');
|
|
await expect(readFile(trace.tracePath, 'utf-8')).resolves.toContain('patch_apply_finished');
|
|
});
|
|
|
|
it('rolls back and classifies semantic conflicts', async () => {
|
|
const { homeDir, configDir, git, baseSha } = await makeRepo();
|
|
const childDir = join(homeDir, 'child-semantic');
|
|
await git.addWorktree(childDir, 'child-semantic', baseSha);
|
|
const childGit = git.forWorktree(childDir);
|
|
await writeFile(join(childDir, 'wiki/global/a.md'), 'bad\n');
|
|
await childGit.commitFiles(['wiki/global/a.md'], 'bad edit', 'System User', 'system@example.com');
|
|
const patchPath = join(homeDir, 'patches/bad.patch');
|
|
await childGit.writeBinaryNoRenamePatch(baseSha, 'HEAD', patchPath);
|
|
const trace = new FileIngestTraceWriter({
|
|
tracePath: join(homeDir, '.ktx/ingest-traces/job-2/trace.jsonl'),
|
|
jobId: 'job-2',
|
|
connectionId: 'c1',
|
|
sourceKey: 'fake',
|
|
level: 'trace',
|
|
});
|
|
|
|
const result = await integrateWorkUnitPatch({
|
|
unitKey: 'wu-bad',
|
|
patchPath,
|
|
integrationGit: git,
|
|
trace,
|
|
author: { name: 'KTX Test', email: 'system@ktx.local' },
|
|
validateAppliedTree: vi.fn().mockRejectedValue(new Error('final artifact gates failed')),
|
|
slDisallowed: false,
|
|
});
|
|
|
|
expect(result.status).toBe('semantic_conflict');
|
|
await expect(readFile(join(configDir, 'wiki/global/a.md'), 'utf-8')).resolves.toBe('old\n');
|
|
});
|
|
});
|