From f2f4769460597c31f2adce18a034fe8f904dee55 Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Wed, 8 Jul 2026 01:25:21 +0900 Subject: [PATCH] Add route lint harness --- package.json | 1 + scripts/route_lint.mjs | 60 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 scripts/route_lint.mjs diff --git a/package.json b/package.json index a89d563..df6b6e7 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "test:e2e:headed": "playwright test --headed", "test:e2e:public-smoke": "playwright test --project=\"Public Smoke\" tests/e2e/public-smoke.spec.ts", "test:e2e:admin-smoke": "playwright test --project=\"Admin Smoke\" tests/e2e/admin-smoke.spec.ts", + "test:route-lint": "node scripts/route_lint.mjs", "test:e2e:ci": "playwright test --project=\"Desktop Chrome\"", "test:local": "python scripts/run_local_tests.py" }, diff --git a/scripts/route_lint.mjs b/scripts/route_lint.mjs new file mode 100644 index 0000000..127f381 --- /dev/null +++ b/scripts/route_lint.mjs @@ -0,0 +1,60 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +const root = process.cwd(); +const files = [ + 'src/TaxBaik.Web/Program.cs', + 'src/TaxBaik.Web.Client/Components/Admin/App.razor', + 'src/TaxBaik.Web.Client/Components/Admin/Routes.razor', + 'src/TaxBaik.Web.Client/Components/Admin/RedirectToLogin.razor', + 'tests/e2e/public-smoke.spec.ts', + 'tests/e2e/admin-smoke.spec.ts', + 'tests/e2e/route-isolation.spec.ts' +]; + +const required = [ + { + file: 'src/TaxBaik.Web/Program.cs', + pattern: 'MapGroup("/taxbaik/admin")', + message: '관리자 Razor Components는 /taxbaik/admin 그룹에만 매핑되어야 합니다.' + }, + { + file: 'tests/e2e/route-isolation.spec.ts', + pattern: 'public home does not boot the admin app', + message: '공용/관리자 분리 하네스가 필요합니다.' + }, + { + file: 'tests/e2e/route-isolation.spec.ts', + pattern: 'admin login boots the admin app under the admin base path', + message: '관리자 base path 하네스가 필요합니다.' + } +]; + +let failed = false; + +for (const entry of required) { + const fullPath = path.join(root, entry.file); + const text = fs.readFileSync(fullPath, 'utf8'); + if (!text.includes(entry.pattern)) { + console.error(`[route-lint] FAIL ${entry.file}: missing "${entry.pattern}"`); + console.error(` ${entry.message}`); + failed = true; + } +} + +const adminFiles = files + .filter(file => file.includes('/Admin/') || file.includes('admin-smoke') || file.includes('route-isolation')); + +for (const file of adminFiles) { + const fullPath = path.join(root, file); + const text = fs.readFileSync(fullPath, 'utf8'); + if (file === 'src/TaxBaik.Web/Program.cs') continue; + + if (text.includes('ToBaseRelativePath(Navigation.Uri)')) { + console.error(`[route-lint] FAIL ${file}: unsafe ToBaseRelativePath(Navigation.Uri) usage detected.`); + failed = true; + } +} + +if (failed) process.exit(1); +console.log('[route-lint] OK');