import type { EnvironmentId, ProjectId } from "@t3tools/contracts"; import { describe, expect, it } from "vite-plus/test"; import { assignProjectsToEnvironments, resolvePickableEnvironments, type AssignableProject, } from "./pullRequestProjectAssignment.logic"; const project = ( id: string, environmentId: string, canonicalKey?: string, ): AssignableProject & { workspaceRoot: string } => ({ id: id as ProjectId, environmentId: environmentId as EnvironmentId, repositoryIdentity: canonicalKey === undefined ? null : { canonicalKey }, workspaceRoot: `/srv/${environmentId}/${id}`, }); const envs = (...ids: ReadonlyArray) => ids as ReadonlyArray; const plain = (assignment: Map) => Object.fromEntries([...assignment].map(([id, projectIds]) => [id, projectIds])); describe("one server per repository", () => { it("lets the first server list a repository both hold", () => { const assignment = assignProjectsToEnvironments( [ project("a1", "env-1", "github.com/acme/app"), project("b1", "env-1", "github.com/acme/tools"), project("a2", "env-2", "github.com/acme/app"), project("c2", "env-2", "github.com/acme/site"), ], envs("env-1", "env-2"), "env-1" as EnvironmentId, ); expect(plain(assignment)).toEqual({ "env-1": ["a1", "b1"], "env-2": ["c2"] }); }); it("prefers the named server over the first one", () => { const assignment = assignProjectsToEnvironments( [ project("a1", "env-1", "github.com/acme/app"), project("a2", "env-2", "github.com/acme/app"), ], envs("env-1", "env-2"), "env-2" as EnvironmentId, ); expect(plain(assignment)).toEqual({ "env-2": ["a2"] }); }); it("drops a server with nothing of its own", () => { const assignment = assignProjectsToEnvironments( [ project("a1", "env-1", "github.com/acme/app"), project("a2", "env-2", "github.com/acme/app"), ], envs("env-1", "env-2"), "env-1" as EnvironmentId, ); expect(assignment.has("env-2" as EnvironmentId)).toBe(false); }); it("keeps every copy of a project that has no identity to compare", () => { const assignment = assignProjectsToEnvironments( [project("p1", "env-1"), project("p2", "env-2")], envs("env-1", "env-2"), "env-1" as EnvironmentId, ); expect(plain(assignment)).toEqual({ "env-1": ["p1"], "env-2": ["p2"] }); }); it("keeps a repository listed by the one server that holds it", () => { const assignment = assignProjectsToEnvironments( [ project("a1", "env-1", "github.com/acme/app"), project("b2", "env-2", "gitlab.com/acme/app"), ], envs("env-1", "env-2"), "env-1" as EnvironmentId, ); expect(plain(assignment)).toEqual({ "env-1": ["a1"], "env-2": ["b2"] }); }); it("keeps a server's own worktrees of the repository it lists", () => { const assignment = assignProjectsToEnvironments( [ project("a1", "env-1", "github.com/acme/app"), project("a1-wt", "env-1", "GitHub.com/acme/app"), project("a2", "env-2", "github.com/acme/app"), ], envs("env-1", "env-2"), "env-1" as EnvironmentId, ); expect(plain(assignment)).toEqual({ "env-1": ["a1", "a1-wt"] }); }); it("reads two servers' copies of one repository as one however the remote is cased", () => { const assignment = assignProjectsToEnvironments( [ project("a1", "env-1", "git.example.com/Team/App"), project("a2", "env-2", "GIT.example.com/team/app"), ], envs("env-1", "env-2"), "env-1" as EnvironmentId, ); expect(plain(assignment)).toEqual({ "env-1": ["a1"] }); }); it("ignores a project on a server that is not being read", () => { const assignment = assignProjectsToEnvironments( [ project("a1", "env-1", "github.com/acme/app"), project("a2", "env-2", "github.com/acme/app"), ], envs("env-2"), "env-2" as EnvironmentId, ); expect(plain(assignment)).toEqual({ "env-2": ["a2"] }); }); it("answers the same whatever order the projects arrive in", () => { const projects = [ project("a2", "env-2", "github.com/acme/app"), project("a1", "env-1", "github.com/acme/app"), ]; const forward = assignProjectsToEnvironments(projects, envs("env-1", "env-2"), null); const backward = assignProjectsToEnvironments( projects.toReversed(), envs("env-1", "env-2"), null, ); expect(plain(forward)).toEqual({ "env-1": ["a1"] }); expect(plain(backward)).toEqual(plain(forward)); }); }); const connected = (...ids: ReadonlyArray) => ids.map((id) => ({ environmentId: id as EnvironmentId, label: `Server ${id}` })); const on = (environmentId: string, projectId: string) => ({ environmentId: environmentId as EnvironmentId, projectId: projectId as ProjectId, }); describe("where a pull request can be acted on", () => { it("offers every server holding the repository, the panel's own first", () => { const pickable = resolvePickableEnvironments( on("env-2", "a2"), [ project("a1", "env-1", "github.com/acme/app"), project("a2", "env-2", "github.com/acme/app"), project("c3", "env-3", "github.com/acme/site"), ], connected("env-1", "env-2", "env-3"), ); expect(pickable).toEqual([ { environmentId: "env-2", projectId: "a2", workspaceRoot: "/srv/env-2/a2", label: "Server env-2", }, { environmentId: "env-1", projectId: "a1", workspaceRoot: "/srv/env-1/a1", label: "Server env-1", }, ]); }); it("matches copies however the remote is cased", () => { const pickable = resolvePickableEnvironments( on("env-1", "a1"), [ project("a1", "env-1", "github.com/acme/app"), project("a2", "env-2", "GitHub.com/ACME/app"), ], connected("env-1", "env-2"), ); expect(pickable.map((entry) => entry.environmentId)).toEqual(["env-1", "env-2"]); }); it("offers nothing where one server holds the repository", () => { expect( resolvePickableEnvironments( on("env-1", "a1"), [ project("a1", "env-1", "github.com/acme/app"), project("c2", "env-2", "github.com/acme/site"), ], connected("env-1", "env-2"), ), ).toEqual([]); }); it("offers nothing for a project with no identity to compare", () => { expect( resolvePickableEnvironments( on("env-1", "p1"), [project("p1", "env-1"), project("p2", "env-2")], connected("env-1", "env-2"), ), ).toEqual([]); }); it("offers nothing while the projects are still arriving", () => { expect(resolvePickableEnvironments(on("env-1", "a1"), [], connected("env-1", "env-2"))).toEqual( [], ); }); it("leaves out a server that is not connected", () => { expect( resolvePickableEnvironments( on("env-1", "a1"), [ project("a1", "env-1", "github.com/acme/app"), project("a2", "env-2", "github.com/acme/app"), ], connected("env-1"), ), ).toEqual([]); }); it("names one copy per server, so two worktrees are one choice", () => { const pickable = resolvePickableEnvironments( on("env-1", "a1"), [ project("a1", "env-1", "github.com/acme/app"), project("a1-wt", "env-1", "github.com/acme/app"), project("a2", "env-2", "github.com/acme/app"), project("a2-wt", "env-2", "github.com/acme/app"), ], connected("env-1", "env-2"), ); expect(pickable.map((entry) => entry.projectId)).toEqual(["a1", "a2"]); }); it("keeps the panel's own worktree rather than the server's first copy", () => { const pickable = resolvePickableEnvironments( on("env-1", "a1-wt"), [ project("a1", "env-1", "github.com/acme/app"), project("a1-wt", "env-1", "github.com/acme/app"), project("a2", "env-2", "github.com/acme/app"), ], connected("env-1", "env-2"), ); expect(pickable[0]).toEqual({ environmentId: "env-1", projectId: "a1-wt", workspaceRoot: "/srv/env-1/a1-wt", label: "Server env-1", }); }); });