From df72a92b4c6c6ef584568ddc77d20f0ddc75c2ac Mon Sep 17 00:00:00 2001 From: Till JS Date: Thu, 9 Apr 2026 16:05:17 +0200 Subject: [PATCH] =?UTF-8?q?test(mana/web):=20consistency=20guard=20for=20w?= =?UTF-8?q?orkbench-registry=20=E2=86=94=20MANA=5FAPPS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the test that would have caught the inventar↔inventory drift months earlier (commit 45790ffbb fixed the actual mismatch). Walks both directions: 1. Every workbench-registered app must have a MANA_APPS entry, OR be in the WORKBENCH_ONLY allowlist (currently `automations`, `playground` — internal devtools we don't want in marketing). 2. Every MANA_APPS entry must be registered in the workbench, OR be in the BRANDING_ONLY allowlist (`mana` itself, standalone subdomains like `arcade`, "Coming Soon" placeholders like `wisekeep`/`mail`/`events`, and modules whose workbench integration is still pending like `guides`/`who`). Plus a regression guard that fails loudly if anyone reintroduces `inventar` as an id in either registry. The point: every future drift between the two registries forces the contributor to either fix it on the spot or explicitly classify the new entry in one of the allowlists with a comment. No more silent fail-open tier-gating. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../web/src/lib/app-registry/registry.spec.ts | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 apps/mana/apps/web/src/lib/app-registry/registry.spec.ts diff --git a/apps/mana/apps/web/src/lib/app-registry/registry.spec.ts b/apps/mana/apps/web/src/lib/app-registry/registry.spec.ts new file mode 100644 index 000000000..b60f96cbe --- /dev/null +++ b/apps/mana/apps/web/src/lib/app-registry/registry.spec.ts @@ -0,0 +1,78 @@ +import { describe, it, expect } from 'vitest'; +import { MANA_APPS } from '@mana/shared-branding'; +// Side-effect import: registers every workbench app via ./apps. +// Without this, getAllApps() would return an empty list. +import './apps'; +import { getAllApps } from './registry'; + +/** + * Apps that intentionally exist in the workbench but are NOT in MANA_APPS + * (e.g. internal devtools we don't want in marketing/branding lists). + * + * Adding to this list = "yes, this is supposed to drift, here's why". + * Anything else triggers the test below and surfaces the drift early — + * the kind of mismatch that produced the silent inventar↔inventory bug + * before commit 45790ffbb. + */ +const WORKBENCH_ONLY = new Set(['automations', 'playground']); + +/** + * Apps that intentionally exist in MANA_APPS but are NOT in the workbench + * registry (standalone subdomains, marketing-only "Coming Soon" entries, + * modules that exist as routes/i18n but aren't workbench-integrated yet, + * or the unified-app meta entry itself). + * + * When you add a new app to MANA_APPS, you must EITHER register it in the + * workbench (apps/web/src/lib/app-registry/apps.ts) or add it here with a + * comment explaining why it doesn't belong in the workbench. This forces + * the drift conversation to happen at the time of the change instead of + * months later. + */ +const BRANDING_ONLY = new Set([ + // Meta entry for the unified Mana app itself — it can't be a "module" + // of its own workbench. + 'mana', + // Standalone web app on its own subdomain (arcade.mana.how). + 'arcade', + // Marketing placeholders, status: 'planning' / 'development'. No + // workbench module exists yet — they only show up in the AppsPage + // gallery as "Coming Soon" hints. + 'wisekeep', + 'mail', + 'events', + // Status 'beta' but the workbench integration is still pending. Move + // out of this list once the apps.ts entry exists. + 'guides', + 'who', +]); + +describe('app registry ↔ MANA_APPS consistency', () => { + it('every workbench-registry app has a MANA_APPS entry or is in WORKBENCH_ONLY', () => { + const brandingIds = new Set(MANA_APPS.map((a) => a.id)); + const unaccounted = getAllApps() + .map((a) => a.id) + .filter((id) => !brandingIds.has(id) && !WORKBENCH_ONLY.has(id)); + expect(unaccounted, `Workbench apps missing from MANA_APPS: ${unaccounted.join(', ')}`).toEqual( + [] + ); + }); + + it('every MANA_APPS entry is registered in the workbench or is in BRANDING_ONLY', () => { + const workbenchIds = new Set(getAllApps().map((a) => a.id)); + const unaccounted = MANA_APPS.map((a) => a.id).filter( + (id) => !workbenchIds.has(id) && !BRANDING_ONLY.has(id) + ); + expect( + unaccounted, + `MANA_APPS entries missing from workbench registry: ${unaccounted.join(', ')}` + ).toEqual([]); + }); + + it('inventar→inventory rename is fully applied (regression guard)', () => { + // The whole point of commit 45790ffbb. If anyone re-introduces an + // `inventar` id in either registry, the join in registry.ts + // silently goes back to fail-open for that module. + const allIds = [...getAllApps().map((a) => a.id), ...MANA_APPS.map((a) => a.id)]; + expect(allIds).not.toContain('inventar'); + }); +});