feat(housekeeping): unify complete rebuild in gated preview
CI / check (pull_request) Failing after 10s
CI / deploy (pull_request) Skipped
CI / release (pull_request) Skipped

This commit is contained in:
Simo committed 2026-08-31 20:37:51 +02:00
1 parent 4ad007d241
commit 43470ebf82
402 files changed
+78313 -1663

No files matched your search

@@ -1,95 +1,154 @@
import { describe, expect, it } from "vitest";
import { PERMS } from "@/lib/permission-slugs";
import { anyCapability, type HousekeepingDomainManifest } from "../contracts";
import {
anyCapability,
type CanonicalHousekeepingHref,
type HousekeepingDomainManifest,
} from "../contracts";
import { createHousekeepingRegistry } from "../registry";
import { matchHousekeepingRoute } from "./match-route";
const peopleManifest = {
id: "people",
labelKey: "pages.housekeeping.domains.people.title",
descriptionKey: "pages.housekeeping.domains.people.description",
iconId: "users",
previewHref: "/ase-next/people",
capability: anyCapability(PERMS.USERS_VIEW),
landingRouteId: "people.users",
routes: [
{
id: "people.users",
labelKey: "pages.housekeeping.people.users.title",
href: "/ase-next/people/users",
capability: anyCapability(PERMS.USERS_VIEW),
},
{
id: "people.user-settings",
labelKey: "pages.housekeeping.people.userSettings.title",
href: "/ase-next/people/users/settings",
capability: anyCapability(PERMS.USERS_VIEW),
},
{
id: "people.user-detail",
labelKey: "pages.housekeeping.people.userDetail.title",
href: "/ase-next/people/users/:id",
capability: anyCapability(PERMS.USERS_VIEW),
},
],
searchProviders: [],
inboxSources: [],
widgets: [],
} satisfies HousekeepingDomainManifest;
const requirement = anyCapability(PERMS.USERS_VIEW);
const registry = createHousekeepingRegistry([peopleManifest]);
function peopleManifest(
routes: HousekeepingDomainManifest["routes"],
): HousekeepingDomainManifest {
return {
id: "people",
labelKey: "pages.housekeeping.domains.people.title",
descriptionKey: "pages.housekeeping.domains.people.description",
iconId: "users",
canonicalHref: "/ase-next/people",
capability: requirement,
routes,
searchProviders: [],
inboxSources: [],
widgets: [],
};
}
function route(
id: string,
href: CanonicalHousekeepingHref,
): HousekeepingDomainManifest["routes"][number] {
return {
id,
labelKey: `pages.housekeeping.routes.${id}`,
href,
capability: requirement,
};
}
const registry = createHousekeepingRegistry([
peopleManifest([
route("people.user-detail", "/ase-next/people/users/:id"),
route("people.user-create", "/ase-next/people/users/new"),
route("people.users", "/ase-next/people/users"),
route("people.ticket-note", "/ase-next/people/tickets/:ticketId/notes/:noteId"),
route("people.literal-tool", "/ase-next/people/tools/a+b[1]"),
]),
]);
describe("matchHousekeepingRoute", () => {
it("matches concrete and dynamic preview routes", () => {
it("matches a static route without parameters", () => {
expect(matchHousekeepingRoute(registry, "/ase-next/people/users")).toEqual({
routeId: "people.users",
domain: "people",
params: {},
canonicalHref: "/ase-next/people/users",
});
});
it("prefers a static route over a dynamic route with the same depth", () => {
expect(matchHousekeepingRoute(registry, "/ase-next/people/users/new")).toEqual({
routeId: "people.user-create",
domain: "people",
params: {},
canonicalHref: "/ase-next/people/users/new",
});
});
it("matches a dynamic segment and decodes its parameter", () => {
expect(
matchHousekeepingRoute(registry, "/ase-next/people/users/42"),
matchHousekeepingRoute(registry, "/ase-next/people/users/Ada%20Lovelace"),
).toEqual({
routeId: "people.user-detail",
domain: "people",
params: { id: "42" },
canonicalHref: "/ase-next/people/users/42",
params: { id: "Ada Lovelace" },
canonicalHref: "/ase-next/people/users/Ada%20Lovelace",
});
});
it("prefers a literal route over a dynamic route", () => {
it("matches nested dynamic segments independently", () => {
expect(
matchHousekeepingRoute(registry, "/ase-next/people/users/settings"),
).toMatchObject({
routeId: "people.user-settings",
params: {},
matchHousekeepingRoute(
registry,
"/ase-next/people/tickets/42/notes/follow-up",
),
).toEqual({
routeId: "people.ticket-note",
domain: "people",
params: { ticketId: "42", noteId: "follow-up" },
canonicalHref: "/ase-next/people/tickets/42/notes/follow-up",
});
});
it("decodes a parameter exactly once", () => {
it("treats regular-expression metacharacters as literal segment text", () => {
expect(
matchHousekeepingRoute(registry, "/ase-next/people/users/%34%32"),
).toMatchObject({
routeId: "people.user-detail",
params: { id: "42" },
canonicalHref: "/ase-next/people/users/42",
});
matchHousekeepingRoute(registry, "/ase-next/people/tools/a+b[1]"),
)?.toMatchObject({ routeId: "people.literal-tool" });
expect(
matchHousekeepingRoute(registry, "/ase-next/people/tools/ab1"),
).toBeNull();
});
it.each([
"/ase-next/people",
{
routes: [
route("people.kind-settings", "/ase-next/people/:kind/settings"),
route("people.user-detail", "/ase-next/people/users/:id"),
],
},
{
routes: [
route("people.user-detail", "/ase-next/people/users/:id"),
route("people.kind-settings", "/ase-next/people/:kind/settings"),
],
},
])(
"prefers the earliest static segment regardless of registration order",
({ routes }) => {
const orderedRegistry = createHousekeepingRegistry([
peopleManifest(routes),
]);
expect(
matchHousekeepingRoute(orderedRegistry, "/ase-next/people/users/settings"),
)?.toMatchObject({
routeId: "people.user-detail",
params: { id: "settings" },
});
},
);
it.each([
"/ase-next/people/unknown",
"/ase-next/economy/users",
"/ase-next/system/users/Ada",
])("returns null for unknown or cross-domain path %s", (path) => {
expect(matchHousekeepingRoute(registry, path)).toBeNull();
});
it.each([
"ase/people/users",
"/ase-next//people/users",
"/ase-next/people/users/",
"/ase-next/people/users?rank=7",
"/ase-next/people/users#active",
"/ase-next/people\\users",
"/ase-next/people//users",
"/ase-next/people/./users",
"/ase-next/people/users?tab=active",
"/ase-next/people/users/%",
"/ase-next/people/users/%2Fsystem",
"/ase-next/people/users/..",
"/ase-next/people/users/%2F",
"/ase-next/people/users/%252F",
"/ase-next/people/users/%00",
])("rejects an unregistered canonical path: %s", (pathname) => {
expect(matchHousekeepingRoute(registry, pathname)).toBeNull();
"/ase-next\\people\\users",
])("fails closed for malformed canonical path %s", (path) => {
expect(matchHousekeepingRoute(registry, path)).toBeNull();
});
});