85 lines
3.1 KiB
TypeScript
85 lines
3.1 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { execSync, spawnSync } from "node:child_process";
|
|
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { EventEmitter } from "node:events";
|
|
import type { Task, TaskStore } from "@fusion/core";
|
|
import { TaskExecutor } from "../executor.js";
|
|
|
|
const hasGit = spawnSync("git", ["--version"], { stdio: "pipe" }).status === 0;
|
|
const describeIfGit = hasGit ? describe : describe.skip;
|
|
|
|
function git(repo: string, command: string): string {
|
|
return execSync(command, { cwd: repo, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
}
|
|
|
|
function createStore(): TaskStore & EventEmitter {
|
|
const emitter = new EventEmitter();
|
|
return Object.assign(emitter, {
|
|
updateTask: vi.fn().mockResolvedValue(undefined),
|
|
on: emitter.on.bind(emitter),
|
|
}) as unknown as TaskStore & EventEmitter;
|
|
}
|
|
|
|
function makeTask(baseCommitSha?: string): Task {
|
|
return {
|
|
id: "FN-TEST-4383",
|
|
title: "Test",
|
|
description: "Test",
|
|
column: "in-progress",
|
|
dependencies: [],
|
|
steps: [],
|
|
currentStep: 0,
|
|
log: [],
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
...(baseCommitSha ? { baseCommitSha } : {}),
|
|
} as Task;
|
|
}
|
|
|
|
describeIfGit("captureBaseCommitSha (real git)", () => {
|
|
const repos: string[] = [];
|
|
|
|
afterEach(() => {
|
|
for (const repo of repos.splice(0)) rmSync(repo, { recursive: true, force: true });
|
|
});
|
|
|
|
it("FN-4309/FN-4383: preserves baseCommitSha across sessions and keeps commit distance", async () => {
|
|
const repo = mkdtempSync(path.join(os.tmpdir(), "fn-4383-"));
|
|
repos.push(repo);
|
|
git(repo, "git init -b main");
|
|
git(repo, 'git config user.email "test@example.com"');
|
|
git(repo, 'git config user.name "Test"');
|
|
|
|
writeFileSync(path.join(repo, "file.txt"), "init\n", "utf-8");
|
|
git(repo, "git add file.txt && git commit -m 'init'");
|
|
|
|
git(repo, "git checkout -b fusion/fn-test-4383");
|
|
for (let i = 1; i <= 17; i += 1) {
|
|
writeFileSync(path.join(repo, `branch-${i}.txt`), `branch ${i}\n`, "utf-8");
|
|
git(repo, `git add branch-${i}.txt && git commit -m 'branch ${i}'`);
|
|
}
|
|
|
|
const store = createStore();
|
|
const executor = new TaskExecutor(store, repo);
|
|
const audit = { git: vi.fn().mockResolvedValue(undefined) };
|
|
|
|
await (executor as any).captureBaseCommitSha(makeTask(), repo, audit, { isResume: false });
|
|
const firstBase = (store.updateTask as any).mock.calls[0][1].baseCommitSha as string;
|
|
expect(firstBase).toBeTruthy();
|
|
|
|
writeFileSync(path.join(repo, "branch-18.txt"), "branch 18\n", "utf-8");
|
|
git(repo, "git add branch-18.txt && git commit -m 'branch 18'");
|
|
|
|
// Resume of the same task: baseCommitSha must be preserved so diff math
|
|
// stays stable across sessions (FN-4309/FN-4383).
|
|
await (executor as any).captureBaseCommitSha(makeTask(firstBase), repo, audit, { isResume: true });
|
|
|
|
expect((store.updateTask as any).mock.calls).toHaveLength(1);
|
|
|
|
const distance = Number(git(repo, `git rev-list --count ${firstBase}..HEAD`));
|
|
expect(distance).toBeGreaterThan(0);
|
|
}, 30_000);
|
|
});
|