gendesign/frontend/src/lib/__tests__/isPathAllowed.test.ts
Light1YT c08e4d7811
All checks were successful
Deploy Trade-In / changes (push) Successful in 6s
Deploy / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-backend (push) Successful in 26s
Deploy / build-backend (push) Successful in 28s
Deploy / build-worker (push) Successful in 28s
Deploy / build-frontend (push) Successful in 27s
Deploy Trade-In / deploy (push) Successful in 36s
Deploy / deploy (push) Successful in 55s
feat(rbac): pilot scope → только /trade-in/** (#587)
Decision 2026-05-26: pilot не видит landing/analytics/site-finder/concept, только Trade-In.
2026-05-26 07:40:44 +00:00

84 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Parity-tests с backend `tests/test_rbac.py::test_is_path_allowed_*`.
*
* Если кто-то меняет глоб-семантику backend'а — должен синхронно поменять JS
* порт здесь, иначе UI начнёт врать про доступы (показывать ссылки, которые
* backend заблокирует, или наоборот скрывать разрешённые).
*
* Pairs мирорят `auth/roles.yaml` (обновлено 2026-05-26 — pilot scope сужен
* до /trade-in/** only):
* admin: paths=["/**"] deny=[]
* pilot: paths=["/trade-in/**", "/trade-in/api/v1/**"]
* deny=["/admin/**", "/api/v1/admin/**", "/trade-in/api/v1/admin/**"]
*/
import { isPathAllowed } from "../isPathAllowed";
const ADMIN_PATHS = ["/**"] as const;
const ADMIN_DENY = [] as const;
const PILOT_PATHS = ["/trade-in/**", "/trade-in/api/v1/**"] as const;
const PILOT_DENY = [
"/admin/**",
"/api/v1/admin/**",
"/trade-in/api/v1/admin/**",
] as const;
describe("isPathAllowed — admin everywhere", () => {
const cases: string[] = [
"/",
"/admin",
"/admin/scrape/runs",
"/api/v1/admin/scrape/status",
"/trade-in/api/v1/admin/scrape",
"/concept/123",
"/analytics/dashboard",
];
it.each(cases)("allows %s", (path) => {
expect(isPathAllowed(ADMIN_PATHS, ADMIN_DENY, path)).toBe(true);
});
});
describe("isPathAllowed — pilot allowed paths (только trade-in)", () => {
const allowed: string[] = [
"/trade-in",
"/trade-in/",
"/trade-in/123",
"/trade-in/api/v1/search",
"/trade-in/api/v1/me",
];
it.each(allowed)("allows %s", (path) => {
expect(isPathAllowed(PILOT_PATHS, PILOT_DENY, path)).toBe(true);
});
});
describe("isPathAllowed — pilot denied paths (landing + admin + остальное)", () => {
const denied: string[] = [
// Landing + non-tradein разделы — теперь denied (decision 2026-05-26)
"/",
"/analytics/dashboard",
"/site-finder/123",
"/concept/abc",
"/api/v1/parcels/123",
"/api/v1/analytics/dashboard",
// Admin paths (explicit deny + not in allowed)
"/admin",
"/admin/jobs",
"/admin/scrape/runs/42",
"/api/v1/admin/scrape/status",
"/api/v1/admin/jobs",
"/trade-in/api/v1/admin/scrape",
];
it.each(denied)("denies %s", (path) => {
expect(isPathAllowed(PILOT_PATHS, PILOT_DENY, path)).toBe(false);
});
});
describe("isPathAllowed — edge cases", () => {
it("denies path not matching any rule", () => {
expect(isPathAllowed(PILOT_PATHS, PILOT_DENY, "/unknown")).toBe(false);
});
it("matches bare /trade-in via /trade-in/** glob (no trailing /)", () => {
// Backend glob `/foo/**` после подстановки `(?:/.*)?` матчит и bare `/foo`.
expect(isPathAllowed(PILOT_PATHS, PILOT_DENY, "/trade-in")).toBe(true);
});
});