kanban-dev-webapp/src/lib/git/repo-url.test.ts
Dave cab3b98372 chore(plan): pick up Plan Puzzle v1 in-progress work and Forgejo repo-url helper
The branch plan-puzzle-v1-tests had accumulated changes from a
sibling thread (Plan Puzzle v1 backend + frontend) that hadn't
been committed yet, plus a new src/lib/git/repo-url helper used by
the Forgejo ingest path. Pick them all up so the working tree is
clean and the branch reflects everything since its fork.

Files (Plan Puzzle):
  src/app/actions/client-proposal.ts
  src/app/actions/owner-plan.ts
  src/app/actions/reschedule.ts
  src/app/actions/reschedule-helpers.ts (+ test)
  src/components/admin/project-form.tsx
  src/components/roadmap/roadmap-builder.tsx
  src/components/roadmap/timeline-view.tsx
  src/lib/cards/write-with-audit.ts (+ test)
  src/lib/roadmap/timeline.ts (+ test)

New helper:
  src/lib/git/repo-url.ts (+ test)

Verified: npm run build clean, 466/466 unit tests pass.
2026-09-21 14:40:04 +02:00

90 lines
2.6 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { parseRepoRef } from "./repo-url";
describe("parseRepoRef", () => {
it("parses a Forgejo HTTPS URL with .git suffix", () => {
expect(
parseRepoRef(
"http://forgejo-b82b4lpyyzyg40thdsjdd38i.163.245.214.68.sslip.io/David/mega-plant.git"
)
).toEqual({ owner: "David", name: "mega-plant" });
});
it("parses a GitHub URL without .git suffix", () => {
expect(parseRepoRef("https://github.com/octocat/Hello-World")).toEqual({
owner: "octocat",
name: "Hello-World",
});
});
it("parses an SSH scp-style URL", () => {
expect(parseRepoRef("git@forgejo.example.com:David/mega-plant.git")).toEqual(
{ owner: "David", name: "mega-plant" }
);
});
it("parses an ssh:// URL", () => {
expect(parseRepoRef("ssh://git@host/owner/repo.git")).toEqual({
owner: "owner",
name: "repo",
});
});
it("parses an http URL with a port", () => {
expect(parseRepoRef("http://localhost:3000/David/mega-plant.git")).toEqual({
owner: "David",
name: "mega-plant",
});
});
it("strips a trailing slash", () => {
expect(parseRepoRef("https://gitea.example.com/owner/repo/")).toEqual({
owner: "owner",
name: "repo",
});
});
it("treats owner/name shorthand as a path", () => {
expect(parseRepoRef("David/mega-plant")).toEqual({
owner: "David",
name: "mega-plant",
});
});
it("preserves case from the URL", () => {
expect(
parseRepoRef("https://forgejo.example.com/David/Mega-Plant.git")
).toEqual({ owner: "David", name: "Mega-Plant" });
});
it("drops extra path segments (commits, branches, …)", () => {
expect(
parseRepoRef("https://gitea.example.com/owner/repo/commits/main")
).toEqual({ owner: "owner", name: "repo" });
});
it("ignores extra whitespace around the input", () => {
expect(parseRepoRef(" https://gitea.example.com/owner/repo.git ")).toEqual(
{ owner: "owner", name: "repo" }
);
});
it("returns null for empty or whitespace input", () => {
expect(parseRepoRef("")).toBeNull();
expect(parseRepoRef(" ")).toBeNull();
});
it("returns null when there is only one path segment", () => {
expect(parseRepoRef("https://gitea.example.com/owner")).toBeNull();
expect(parseRepoRef("owner")).toBeNull();
});
it("returns null when the URL has no path", () => {
expect(parseRepoRef("https://gitea.example.com/")).toBeNull();
});
it("returns null for garbage", () => {
expect(parseRepoRef("not a url at all !!!")).toBeNull();
});
});