Files
ForgeFlow/tests/git-validator.test.mjs
T

102 lines
3.6 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import { mkdtemp, rm, writeFile, readFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { createRequire } from "node:module";
const exec = promisify(execFile);
const require = createRequire(import.meta.url);
const { GitService } = require("../src/main/git-service.cjs");
const {
GitValidatorService,
isSensitiveTrackedPath,
sameRemote,
} = require("../src/main/git-validator-service.cjs");
async function git(args, cwd) {
return exec("git", args, { cwd, encoding: "utf8" });
}
test("Git Validator scores repository hygiene and offers bounded safe repairs", async (t) => {
const root = await mkdtemp(path.join(os.tmpdir(), "forgeflow-validator-"));
t.after(() => rm(root, { recursive: true, force: true }));
await git(["init", "-b", "main"], root);
await git(["config", "user.name", "ForgeFlow Test"], root);
await git(["config", "user.email", "forgeflow@example.invalid"], root);
await git(
["remote", "add", "origin", "https://gitea.example.test/jens/app.git"],
root,
);
await writeFile(path.join(root, "README.md"), "# App\n", "utf8");
await writeFile(path.join(root, ".gitignore"), ".env\n", "utf8");
await git(["add", "."], root);
await git(["commit", "-m", "Initial"], root);
const validator = new GitValidatorService({
git: new GitService(),
gitea: {
getBranchProtection: async () => ({
protected: false,
enableForcePush: false,
}),
},
});
const repository = {
fullName: "jens/app",
name: "app",
owner: { login: "jens" },
defaultBranch: "main",
localPath: root,
cloneUrl: "https://gitea.example.test/jens/app.git",
sshUrl: "git@gitea.example.test:jens/app.git",
};
const report = await validator.scan(repository);
assert.ok(report.score > 60);
assert.equal(
report.checks.find((check) => check.id === "origin").status,
"pass",
);
assert.equal(
report.checks.find((check) => check.id === "default-branch-protection")
.fixAction,
"protect-default-branch",
);
const safety = report.checks.find((check) => check.id === "local-safety");
assert.equal(safety.safe, true);
await validator.repair(repository, safety);
const rescanned = await validator.scan(repository);
assert.equal(
rescanned.checks.find((check) => check.id === "local-safety").status,
"pass",
);
});
test("Git Validator creates a reviewable gitignore without committing it", async (t) => {
const root = await mkdtemp(path.join(os.tmpdir(), "forgeflow-ignore-"));
t.after(() => rm(root, { recursive: true, force: true }));
await git(["init", "-b", "main"], root);
const validator = new GitValidatorService({ git: new GitService() });
const repository = { localPath: root };
await validator.repair(repository, { fixAction: "add-gitignore" });
const content = await readFile(path.join(root, ".gitignore"), "utf8");
assert.match(content, /\.env/);
const status = await git(["status", "--short"], root);
assert.match(status.stdout, /\?\? \.gitignore/);
});
test("Git Validator recognizes remote aliases and secret-shaped tracked paths", () => {
assert.equal(
sameRemote(
"git@gitea.example.test:jens/app.git",
"https://gitea.example.test/jens/app",
),
true,
);
assert.equal(isSensitiveTrackedPath(".env.production"), true);
assert.equal(isSensitiveTrackedPath("config/private.pem"), true);
assert.equal(isSensitiveTrackedPath(".env.example"), false);
});