Revert "Merge pull request 'Complete Housekeeping migration and /ase cutover' (#52) from codex/housekeeping-complete into main"
This reverts commit488b6e57c4, reversing changes made tob506b4499a.
This commit is contained in:
@@ -1,29 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { PERMS } from "@/lib/permission-slugs";
|
||||
import { CONTENT_INBOX_SOURCES } from "../domains/content/inbox";
|
||||
import { CONTENT_ROUTES } from "../domains/content/routes";
|
||||
import { CONTENT_SEARCH_PROVIDERS } from "../domains/content/search";
|
||||
import { CONTENT_WIDGETS } from "../domains/content/widgets";
|
||||
import { ECONOMY_INBOX_SOURCES } from "../domains/economy/inbox";
|
||||
import { ECONOMY_ROUTES } from "../domains/economy/routes";
|
||||
import { ECONOMY_SEARCH_PROVIDERS } from "../domains/economy/search";
|
||||
import { ECONOMY_WIDGETS } from "../domains/economy/widgets";
|
||||
import { HOTEL_INBOX_SOURCES } from "../domains/hotel/inbox";
|
||||
import { HOTEL_ROUTES } from "../domains/hotel/routes";
|
||||
import { HOTEL_SEARCH_PROVIDERS } from "../domains/hotel/search";
|
||||
import { HOTEL_WIDGETS } from "../domains/hotel/widgets";
|
||||
import { OPERATIONS_INBOX_SOURCES } from "../domains/operations/inbox";
|
||||
import { OPERATIONS_ROUTES } from "../domains/operations/routes";
|
||||
import { OPERATIONS_SEARCH_PROVIDERS } from "../domains/operations/search";
|
||||
import { OPERATIONS_WIDGETS } from "../domains/operations/widgets";
|
||||
import { PEOPLE_INBOX_SOURCES } from "../domains/people/inbox";
|
||||
import { PEOPLE_ROUTES } from "../domains/people/routes";
|
||||
import { PEOPLE_SEARCH_PROVIDERS } from "../domains/people/search";
|
||||
import { PEOPLE_WIDGETS } from "../domains/people/widgets";
|
||||
import { SYSTEM_INBOX_SOURCES } from "../domains/system/inbox";
|
||||
import { SYSTEM_ROUTES } from "../domains/system/routes";
|
||||
import { SYSTEM_SEARCH_PROVIDERS } from "../domains/system/search";
|
||||
import { SYSTEM_WIDGETS } from "../domains/system/widgets";
|
||||
import { HOUSEKEEPING_MANIFESTS } from "../manifests";
|
||||
import { HOUSEKEEPING_MIGRATION_MATRIX } from "../migration/matrix";
|
||||
import {
|
||||
@@ -32,21 +8,14 @@ import {
|
||||
} from "../migration/types";
|
||||
import {
|
||||
anyCapability,
|
||||
type CanonicalHousekeepingHref,
|
||||
type CapabilityRequirement,
|
||||
type HousekeepingCapabilityContext,
|
||||
type HousekeepingDomainManifest,
|
||||
type HousekeepingInboxSource,
|
||||
type HousekeepingSearchProvider,
|
||||
type HousekeepingWidgetDefinition,
|
||||
ok,
|
||||
} from "./contracts";
|
||||
import { defaultHousekeepingPreferences } from "./preferences/schema";
|
||||
import {
|
||||
createHousekeepingRegistry,
|
||||
loadHousekeepingWidgets,
|
||||
WIDGET_POLICY,
|
||||
} from "./registry";
|
||||
import { createHousekeepingRegistry } from "./registry";
|
||||
|
||||
const manifest = (
|
||||
id: HousekeepingDomainId,
|
||||
@@ -56,7 +25,7 @@ const manifest = (
|
||||
labelKey: `pages.housekeeping.domains.${id}.title`,
|
||||
descriptionKey: `pages.housekeeping.domains.${id}.description`,
|
||||
iconId: "settings",
|
||||
canonicalHref: id === "operations" ? "/ase" : `/ase/${id}`,
|
||||
previewHref: `/admin-next/${id}`,
|
||||
capability: anyCapability(capabilitySlug),
|
||||
routes: [],
|
||||
searchProviders: [],
|
||||
@@ -104,7 +73,7 @@ const expectedManifests = [
|
||||
{
|
||||
id: "operations",
|
||||
iconId: "inbox",
|
||||
canonicalHref: "/ase",
|
||||
previewHref: "/admin-next/operations",
|
||||
labelKey: "pages.housekeeping.domains.operations.title",
|
||||
descriptionKey: "pages.housekeeping.domains.operations.description",
|
||||
slugs: [PERMS.ADMIN_DASHBOARD],
|
||||
@@ -112,7 +81,7 @@ const expectedManifests = [
|
||||
{
|
||||
id: "people",
|
||||
iconId: "users",
|
||||
canonicalHref: "/ase/people",
|
||||
previewHref: "/admin-next/people",
|
||||
labelKey: "pages.housekeeping.domains.people.title",
|
||||
descriptionKey: "pages.housekeeping.domains.people.description",
|
||||
slugs: [
|
||||
@@ -143,7 +112,7 @@ const expectedManifests = [
|
||||
{
|
||||
id: "content",
|
||||
iconId: "file-text",
|
||||
canonicalHref: "/ase/content",
|
||||
previewHref: "/admin-next/content",
|
||||
labelKey: "pages.housekeeping.domains.content.title",
|
||||
descriptionKey: "pages.housekeeping.domains.content.description",
|
||||
slugs: [
|
||||
@@ -166,7 +135,7 @@ const expectedManifests = [
|
||||
{
|
||||
id: "economy",
|
||||
iconId: "gem",
|
||||
canonicalHref: "/ase/economy",
|
||||
previewHref: "/admin-next/economy",
|
||||
labelKey: "pages.housekeeping.domains.economy.title",
|
||||
descriptionKey: "pages.housekeeping.domains.economy.description",
|
||||
slugs: [
|
||||
@@ -179,7 +148,7 @@ const expectedManifests = [
|
||||
{
|
||||
id: "hotel",
|
||||
iconId: "hotel",
|
||||
canonicalHref: "/ase/hotel",
|
||||
previewHref: "/admin-next/hotel",
|
||||
labelKey: "pages.housekeeping.domains.hotel.title",
|
||||
descriptionKey: "pages.housekeeping.domains.hotel.description",
|
||||
slugs: [
|
||||
@@ -196,7 +165,7 @@ const expectedManifests = [
|
||||
{
|
||||
id: "system",
|
||||
iconId: "settings",
|
||||
canonicalHref: "/ase/system",
|
||||
previewHref: "/admin-next/system",
|
||||
labelKey: "pages.housekeeping.domains.system.title",
|
||||
descriptionKey: "pages.housekeeping.domains.system.description",
|
||||
slugs: [
|
||||
@@ -239,9 +208,9 @@ describe("housekeeping registry", () => {
|
||||
it("rejects invalid preview and empty translation keys", () => {
|
||||
expect(() =>
|
||||
createHousekeepingRegistry([
|
||||
{ ...manifest("people"), canonicalHref: "/ase" },
|
||||
{ ...manifest("people"), previewHref: "/admin-next/operations" },
|
||||
]),
|
||||
).toThrow("invalid canonical href");
|
||||
).toThrow("invalid preview href");
|
||||
expect(() =>
|
||||
createHousekeepingRegistry([{ ...manifest("people"), labelKey: " " }]),
|
||||
).toThrow("empty label key");
|
||||
@@ -263,13 +232,13 @@ describe("housekeeping registry", () => {
|
||||
{
|
||||
id: "users",
|
||||
labelKey: "pages.housekeeping.domains.people.title",
|
||||
href: "/ase/people/users" as CanonicalHousekeepingHref,
|
||||
href: "/admin-next/people/users",
|
||||
capability: anyCapability(PERMS.USERS_VIEW),
|
||||
},
|
||||
{
|
||||
id: "users",
|
||||
labelKey: "pages.housekeeping.domains.people.title",
|
||||
href: "/ase/people/staff",
|
||||
href: "/admin-next/people/staff",
|
||||
capability: anyCapability(PERMS.USERS_VIEW),
|
||||
},
|
||||
],
|
||||
@@ -284,13 +253,13 @@ describe("housekeeping registry", () => {
|
||||
{
|
||||
id: "users",
|
||||
labelKey: "pages.housekeeping.domains.people.title",
|
||||
href: "/ase/people/users" as CanonicalHousekeepingHref,
|
||||
href: "/admin-next/people/users",
|
||||
capability: anyCapability(PERMS.USERS_VIEW),
|
||||
},
|
||||
{
|
||||
id: "staff",
|
||||
labelKey: "pages.housekeeping.domains.people.title",
|
||||
href: "/ase/people/users" as CanonicalHousekeepingHref,
|
||||
href: "/admin-next/people/users",
|
||||
capability: anyCapability(PERMS.USERS_VIEW),
|
||||
},
|
||||
],
|
||||
@@ -299,37 +268,11 @@ describe("housekeeping registry", () => {
|
||||
).toThrow("duplicate route href");
|
||||
});
|
||||
|
||||
it("rejects duplicate dynamic route shapes regardless of parameter name", () => {
|
||||
const base = manifest("people");
|
||||
|
||||
expect(() =>
|
||||
createHousekeepingRegistry([
|
||||
{
|
||||
...base,
|
||||
routes: [
|
||||
{
|
||||
id: "people.user-detail",
|
||||
labelKey: "pages.housekeeping.routes.people.user-detail",
|
||||
href: "/ase/people/users/:id",
|
||||
capability: anyCapability(PERMS.USERS_VIEW),
|
||||
},
|
||||
{
|
||||
id: "people.user-profile",
|
||||
labelKey: "pages.housekeeping.routes.people.user-profile",
|
||||
href: "/ase/people/users/:username",
|
||||
capability: anyCapability(PERMS.USERS_VIEW),
|
||||
},
|
||||
],
|
||||
},
|
||||
]),
|
||||
).toThrow("duplicate dynamic route shape");
|
||||
});
|
||||
|
||||
it("rejects empty route fields and unknown capability slugs", () => {
|
||||
const route = {
|
||||
id: "users",
|
||||
labelKey: "pages.housekeeping.domains.people.title",
|
||||
href: "/ase/people/users" as CanonicalHousekeepingHref,
|
||||
href: "/admin-next/people/users",
|
||||
capability: anyCapability(PERMS.USERS_VIEW),
|
||||
};
|
||||
|
||||
@@ -340,10 +283,7 @@ describe("housekeeping registry", () => {
|
||||
).toThrow("empty route id");
|
||||
expect(() =>
|
||||
createHousekeepingRegistry([
|
||||
{
|
||||
...manifest("people"),
|
||||
routes: [{ ...route, href: " " as CanonicalHousekeepingHref }],
|
||||
},
|
||||
{ ...manifest("people"), routes: [{ ...route, href: " " }] },
|
||||
]),
|
||||
).toThrow("empty route href");
|
||||
expect(() =>
|
||||
@@ -379,70 +319,14 @@ describe("housekeeping registry", () => {
|
||||
expect(actual).toMatchObject({
|
||||
id: expected.id,
|
||||
iconId: expected.iconId,
|
||||
canonicalHref: expected.canonicalHref,
|
||||
previewHref: expected.previewHref,
|
||||
labelKey: expected.labelKey,
|
||||
descriptionKey: expected.descriptionKey,
|
||||
});
|
||||
expect(actual.routes).toEqual(
|
||||
expected.id === "operations"
|
||||
? OPERATIONS_ROUTES
|
||||
: expected.id === "people"
|
||||
? PEOPLE_ROUTES
|
||||
: expected.id === "content"
|
||||
? CONTENT_ROUTES
|
||||
: expected.id === "economy"
|
||||
? ECONOMY_ROUTES
|
||||
: expected.id === "hotel"
|
||||
? HOTEL_ROUTES
|
||||
: expected.id === "system"
|
||||
? SYSTEM_ROUTES
|
||||
: [],
|
||||
);
|
||||
expect(actual.searchProviders).toEqual(
|
||||
expected.id === "operations"
|
||||
? OPERATIONS_SEARCH_PROVIDERS
|
||||
: expected.id === "people"
|
||||
? PEOPLE_SEARCH_PROVIDERS
|
||||
: expected.id === "content"
|
||||
? CONTENT_SEARCH_PROVIDERS
|
||||
: expected.id === "economy"
|
||||
? ECONOMY_SEARCH_PROVIDERS
|
||||
: expected.id === "hotel"
|
||||
? HOTEL_SEARCH_PROVIDERS
|
||||
: expected.id === "system"
|
||||
? SYSTEM_SEARCH_PROVIDERS
|
||||
: [],
|
||||
);
|
||||
expect(actual.inboxSources).toEqual(
|
||||
expected.id === "operations"
|
||||
? OPERATIONS_INBOX_SOURCES
|
||||
: expected.id === "people"
|
||||
? PEOPLE_INBOX_SOURCES
|
||||
: expected.id === "content"
|
||||
? CONTENT_INBOX_SOURCES
|
||||
: expected.id === "economy"
|
||||
? ECONOMY_INBOX_SOURCES
|
||||
: expected.id === "hotel"
|
||||
? HOTEL_INBOX_SOURCES
|
||||
: expected.id === "system"
|
||||
? SYSTEM_INBOX_SOURCES
|
||||
: [],
|
||||
);
|
||||
expect(actual.widgets).toEqual(
|
||||
expected.id === "operations"
|
||||
? OPERATIONS_WIDGETS
|
||||
: expected.id === "people"
|
||||
? PEOPLE_WIDGETS
|
||||
: expected.id === "content"
|
||||
? CONTENT_WIDGETS
|
||||
: expected.id === "economy"
|
||||
? ECONOMY_WIDGETS
|
||||
: expected.id === "hotel"
|
||||
? HOTEL_WIDGETS
|
||||
: expected.id === "system"
|
||||
? SYSTEM_WIDGETS
|
||||
: [],
|
||||
);
|
||||
expect(actual.routes).toEqual([]);
|
||||
expect(actual.searchProviders).toEqual([]);
|
||||
expect(actual.inboxSources).toEqual([]);
|
||||
expect(actual.widgets).toEqual([]);
|
||||
expect(actual.capability).toEqual({ mode: "any", slugs: expected.slugs });
|
||||
}
|
||||
});
|
||||
@@ -467,7 +351,7 @@ describe("housekeeping registry", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects duplicates within each provider kind across domain manifests", () => {
|
||||
it("rejects duplicate provider and widget ids across domain manifests", () => {
|
||||
expect(() =>
|
||||
createHousekeepingRegistry([
|
||||
{ ...manifest("people"), searchProviders: [searchProvider("shared")] },
|
||||
@@ -503,7 +387,7 @@ describe("housekeeping registry", () => {
|
||||
widgets: [widget("shared", "content")],
|
||||
},
|
||||
]),
|
||||
).not.toThrow();
|
||||
).toThrow("duplicate widget id: shared");
|
||||
});
|
||||
|
||||
it("rejects provider and widget ownership outside their manifest", () => {
|
||||
@@ -583,88 +467,6 @@ describe("housekeeping registry", () => {
|
||||
).toThrow("invalid widget kind: people.queue");
|
||||
});
|
||||
|
||||
it("loads only authorized mandatory and enabled optional widgets with isolated timeouts", async () => {
|
||||
const context: HousekeepingCapabilityContext = {
|
||||
actor: { id: 42, username: "operator", rank: 7 },
|
||||
isSuperAdmin: false,
|
||||
has: (slug) => slug === PERMS.USERS_VIEW,
|
||||
hasAny: (...slugs) => slugs.includes(PERMS.USERS_VIEW),
|
||||
hasAll: (...slugs) => slugs.every((slug) => slug === PERMS.USERS_VIEW),
|
||||
};
|
||||
let deniedCalls = 0;
|
||||
const base = {
|
||||
...manifest("people"),
|
||||
capability: anyCapability(PERMS.USERS_VIEW),
|
||||
};
|
||||
const registry = createHousekeepingRegistry([
|
||||
{
|
||||
...base,
|
||||
widgets: [
|
||||
{
|
||||
...widget("people.required"),
|
||||
kind: "mandatory",
|
||||
load: async () => ok("required", "required"),
|
||||
},
|
||||
{
|
||||
...widget("people.optional"),
|
||||
load: async () => ok("optional", "optional"),
|
||||
},
|
||||
{
|
||||
...widget(
|
||||
"people.denied",
|
||||
"people",
|
||||
anyCapability(PERMS.BANS_VIEW),
|
||||
),
|
||||
load: async () => {
|
||||
deniedCalls += 1;
|
||||
return ok("denied", "denied");
|
||||
},
|
||||
},
|
||||
{
|
||||
...widget("people.slow"),
|
||||
load: async (_context, signal) =>
|
||||
new Promise((resolve) => {
|
||||
signal.addEventListener("abort", () =>
|
||||
resolve(ok("late", "late")),
|
||||
);
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
const preferences = {
|
||||
...defaultHousekeepingPreferences(),
|
||||
widgetOrder: [
|
||||
"stale.widget",
|
||||
"people.optional",
|
||||
"people.required",
|
||||
"people.slow",
|
||||
],
|
||||
enabledOptionalWidgetIds: [
|
||||
"people.optional",
|
||||
"people.denied",
|
||||
"people.slow",
|
||||
],
|
||||
};
|
||||
|
||||
const result = await loadHousekeepingWidgets(
|
||||
registry,
|
||||
preferences,
|
||||
context,
|
||||
5,
|
||||
);
|
||||
|
||||
expect(WIDGET_POLICY.timeoutMs).toBe(2_000);
|
||||
expect(result.widgets).toEqual([
|
||||
{ id: "people.optional", data: "optional" },
|
||||
{ id: "people.required", data: "required" },
|
||||
]);
|
||||
expect(result.errors).toEqual([
|
||||
{ widgetId: "people.slow", code: "TIMEOUT" },
|
||||
]);
|
||||
expect(deniedCalls).toBe(0);
|
||||
});
|
||||
|
||||
it("rejects empty domain and route capability requirements", () => {
|
||||
expect(() =>
|
||||
createHousekeepingRegistry([
|
||||
@@ -679,7 +481,7 @@ describe("housekeeping registry", () => {
|
||||
{
|
||||
id: "users",
|
||||
labelKey: "pages.housekeeping.domains.people.title",
|
||||
href: "/ase/people/users" as CanonicalHousekeepingHref,
|
||||
href: "/admin-next/people/users",
|
||||
capability: { mode: "any", slugs: [] },
|
||||
},
|
||||
],
|
||||
@@ -692,7 +494,7 @@ describe("housekeeping registry", () => {
|
||||
const route = {
|
||||
id: "shared",
|
||||
labelKey: "pages.housekeeping.domains.people.title",
|
||||
href: "/ase/shared" as CanonicalHousekeepingHref,
|
||||
href: "/admin-next/shared",
|
||||
capability: anyCapability(PERMS.USERS_VIEW),
|
||||
};
|
||||
|
||||
@@ -701,12 +503,7 @@ describe("housekeeping registry", () => {
|
||||
{ ...manifest("people"), routes: [route] },
|
||||
{
|
||||
...manifest("content"),
|
||||
routes: [
|
||||
{
|
||||
...route,
|
||||
href: "/ase/content/shared" as CanonicalHousekeepingHref,
|
||||
},
|
||||
],
|
||||
routes: [{ ...route, href: "/admin-next/content/shared" }],
|
||||
},
|
||||
]),
|
||||
).toThrow("duplicate route id");
|
||||
@@ -721,25 +518,3 @@ describe("housekeeping registry", () => {
|
||||
).toThrow("duplicate route href");
|
||||
});
|
||||
});
|
||||
|
||||
it("accepts the canonical operations root and rejects legacy admin hrefs", () => {
|
||||
const canonicalOperations = manifest("operations") as unknown as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
delete canonicalOperations.canonicalHref;
|
||||
canonicalOperations.canonicalHref = "/ase";
|
||||
|
||||
expect(() =>
|
||||
createHousekeepingRegistry([
|
||||
canonicalOperations as unknown as HousekeepingDomainManifest,
|
||||
]),
|
||||
).not.toThrow();
|
||||
|
||||
canonicalOperations.canonicalHref = "/admin";
|
||||
expect(() =>
|
||||
createHousekeepingRegistry([
|
||||
canonicalOperations as unknown as HousekeepingDomainManifest,
|
||||
]),
|
||||
).toThrow("invalid canonical href");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user