102 lines
3.6 KiB
JavaScript
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);
|
|
});
|