255 lines · 9.4 KB · TypeScript
RawDownload
import * as NodeServices from "@effect/platform-node/NodeServices";import { assert, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Path from "effect/Path"; import { ServerConfig } from "../config.ts";import * as GitVcsDriver from "../vcs/GitVcsDriver.ts";import * as VcsProcess from "../vcs/VcsProcess.ts";import { detectPrTemplate } from "./PrTemplateDetection.ts";import { symlinksSupported } from "@t3tools/shared/testing/symlinks"; const SINGLE_TEMPLATE_PATHS = [  ".github/pull_request_template.md",  ".github/PULL_REQUEST_TEMPLATE.md",  "pull_request_template.md",  "PULL_REQUEST_TEMPLATE.md",  "docs/pull_request_template.md",  "docs/PULL_REQUEST_TEMPLATE.md",] as const; const TEMPLATE_DIRECTORIES = [  ".github/PULL_REQUEST_TEMPLATE",  "PULL_REQUEST_TEMPLATE",  "docs/PULL_REQUEST_TEMPLATE",] as const; const PrTemplateDetectionTestLayer = GitVcsDriver.layer.pipe(  Layer.provide(    ServerConfig.layerTest(process.cwd(), {      prefix: "t3-pr-template-test-",    }),  ),  Layer.provideMerge(VcsProcess.layer),  Layer.provideMerge(NodeServices.layer),); const runGit = (cwd: string, args: ReadonlyArray<string>) =>  Effect.gen(function* () {    const git = yield* GitVcsDriver.GitVcsDriver;    return yield* git.execute({      operation: "PrTemplateDetection.test.runGit",      cwd,      args,    });  }); const runWithTempDirectory = <A, E, R>(  test: (cwd: string) => Effect.Effect<A, E, R | FileSystem.FileSystem | Path.Path>,) =>  Effect.scoped(    Effect.gen(function* () {      const fileSystem = yield* FileSystem.FileSystem;      const cwd = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-pr-template-" });      yield* runGit(cwd, ["init", "--initial-branch=main"]);      yield* runGit(cwd, ["config", "user.email", "test@example.com"]);      yield* runGit(cwd, ["config", "user.name", "Test User"]);      return yield* test(cwd);    }),  ).pipe(Effect.provide(PrTemplateDetectionTestLayer)); const writeTemplate = (cwd: string, relativePath: string, contents: string) =>  Effect.gen(function* () {    const fileSystem = yield* FileSystem.FileSystem;    const path = yield* Path.Path;    const templatePath = path.join(cwd, relativePath);    yield* fileSystem.makeDirectory(path.dirname(templatePath), { recursive: true });    yield* fileSystem.writeFileString(templatePath, contents);    return templatePath;  }); const commitTemplates = (cwd: string) =>  Effect.gen(function* () {    yield* runGit(cwd, ["add", "-A"]);    yield* runGit(cwd, ["commit", "--allow-empty", "-m", "Add pull request templates"]);  }); const detectTemplate = (cwd: string, treeish = "HEAD") =>  Effect.gen(function* () {    const git = yield* GitVcsDriver.GitVcsDriver;    return yield* detectPrTemplate(cwd, treeish, git.execute);  }); it.effect.each(SINGLE_TEMPLATE_PATHS)("recognizes $0", (relativePath) =>  runWithTempDirectory((cwd) =>    Effect.gen(function* () {      yield* writeTemplate(cwd, relativePath, `template from ${relativePath}`);      yield* commitTemplates(cwd);       const template = yield* detectTemplate(cwd);      assert.strictEqual(Option.getOrUndefined(template), `template from ${relativePath}`);    }),  ),); it.effect("reads templates from the requested base tree", () =>  runWithTempDirectory((cwd) =>    Effect.gen(function* () {      yield* writeTemplate(cwd, "README.md", "initial\n");      yield* commitTemplates(cwd);      yield* runGit(cwd, ["branch", "feature"]);      yield* writeTemplate(cwd, ".github/pull_request_template.md", "base template");      yield* commitTemplates(cwd);      yield* runGit(cwd, ["checkout", "feature"]);       assert.isTrue(Option.isNone(yield* detectTemplate(cwd)));      assert.strictEqual(        Option.getOrUndefined(yield* detectTemplate(cwd, "main")),        "base template",      );    }),  ),); it.effect("uses the first non-empty template in the configured path order", () =>  runWithTempDirectory((cwd) =>    Effect.gen(function* () {      yield* writeTemplate(cwd, ".github/pull_request_template.md", " \n");      yield* writeTemplate(cwd, ".github/PULL_REQUEST_TEMPLATE.md", "  ## Preferred template  \n");      yield* writeTemplate(cwd, "pull_request_template.md", "## Later template");      yield* commitTemplates(cwd);       const template = yield* detectTemplate(cwd);      assert.strictEqual(Option.getOrUndefined(template), "## Preferred template");    }),  ),); it.effect.each(TEMPLATE_DIRECTORIES)("recognizes the $0 directory", (relativeDirectory) =>  runWithTempDirectory((cwd) =>    Effect.gen(function* () {      yield* writeTemplate(cwd, `${relativeDirectory}/template.MD`, "directory template");      yield* commitTemplates(cwd);       const template = yield* detectTemplate(cwd);      assert.strictEqual(Option.getOrUndefined(template), "directory template");    }),  ),); it.effect.skipIf(!symlinksSupported)(  "skips unusable directory entries and uses the one valid template",  () =>    runWithTempDirectory((cwd) =>      Effect.gen(function* () {        const fileSystem = yield* FileSystem.FileSystem;        const path = yield* Path.Path;        const templateDirectory = path.join(cwd, ".github", "PULL_REQUEST_TEMPLATE");        yield* fileSystem.makeDirectory(path.join(templateDirectory, "b-directory.md"), {          recursive: true,        });        yield* fileSystem.writeFileString(path.join(templateDirectory, "a-empty.md"), " \n");        yield* fileSystem.symlink(          path.join(templateDirectory, "missing.md"),          path.join(templateDirectory, "c-broken.md"),        );        yield* fileSystem.writeFileString(path.join(templateDirectory, "z-valid.md"), "valid");        yield* commitTemplates(cwd);         const template = yield* detectTemplate(cwd);        assert.strictEqual(Option.getOrUndefined(template), "valid");      }),    ),); it.effect("does not guess between multiple directory templates", () =>  runWithTempDirectory((cwd) =>    Effect.gen(function* () {      yield* writeTemplate(cwd, ".github/PULL_REQUEST_TEMPLATE/a.md", "first");      yield* writeTemplate(cwd, ".github/PULL_REQUEST_TEMPLATE/b.md", "second");      yield* writeTemplate(cwd, "PULL_REQUEST_TEMPLATE/fallback.md", "fallback");      yield* commitTemplates(cwd);       const template = yield* detectTemplate(cwd);      assert.isTrue(Option.isNone(template));    }),  ),); it.effect.skipIf(!symlinksSupported)(  "rejects a committed template symlink escaping the repository",  () =>    runWithTempDirectory((cwd) =>      Effect.gen(function* () {        const fileSystem = yield* FileSystem.FileSystem;        const path = yield* Path.Path;        const outsideDirectory = yield* fileSystem.makeTempDirectoryScoped({          prefix: "t3-pr-template-outside-",        });        const outsideTemplate = path.join(outsideDirectory, "secret.md");        yield* fileSystem.writeFileString(outsideTemplate, "LOCAL_SECRET_SENTINEL");        const escapedTemplatePath = path.join(cwd, ".github", "pull_request_template.md");        yield* fileSystem.makeDirectory(path.dirname(escapedTemplatePath), { recursive: true });        yield* fileSystem.symlink(outsideTemplate, escapedTemplatePath);        yield* writeTemplate(cwd, "pull_request_template.md", "safe template");        yield* commitTemplates(cwd);         const template = yield* detectTemplate(cwd);        assert.strictEqual(Option.getOrUndefined(template), "safe template");        assert.notInclude(          Option.getOrElse(template, () => ""),          "LOCAL_SECRET_SENTINEL",        );      }),    ),); it.effect.skipIf(!symlinksSupported)(  "reads the committed template when a worktree parent is replaced",  () =>    runWithTempDirectory((cwd) =>      Effect.gen(function* () {        const fileSystem = yield* FileSystem.FileSystem;        const path = yield* Path.Path;        const outsideDirectory = yield* fileSystem.makeTempDirectoryScoped({          prefix: "t3-pr-template-outside-",        });        const templatePath = yield* writeTemplate(          cwd,          ".github/pull_request_template.md",          "committed template",        );        yield* commitTemplates(cwd);        yield* writeTemplate(outsideDirectory, "pull_request_template.md", "LOCAL_SECRET_SENTINEL");         const templateDirectory = path.dirname(templatePath);        yield* fileSystem.rename(templateDirectory, path.join(cwd, ".github-original"));        yield* fileSystem.symlink(outsideDirectory, templateDirectory);         const template = yield* detectTemplate(cwd);        assert.strictEqual(Option.getOrUndefined(template), "committed template");        assert.notInclude(          Option.getOrElse(template, () => ""),          "LOCAL_SECRET_SENTINEL",        );      }),    ),); it.effect("bounds template reads and marks truncated content", () =>  runWithTempDirectory((cwd) =>    Effect.gen(function* () {      const prefix = "a".repeat(8_000);      yield* writeTemplate(cwd, ".github/pull_request_template.md", `${prefix}SECRET_SENTINEL`);      yield* commitTemplates(cwd);       const template = Option.getOrThrow(yield* detectTemplate(cwd));      assert.strictEqual(template, `${prefix}\n\n[truncated]`);      assert.lengthOf(template.match(/\[truncated\]/g) ?? [], 1);      assert.notInclude(template, "SECRET_SENTINEL");    }),  ),);