fix(api): remove unused decryptSecret import and eslint-disable directives
Fixes lint error exposed by merge with main (GRO-392 PR #214) Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -1,42 +1,68 @@
|
|||||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { authProviderRouter } from "../routes/authProvider.js";
|
import { authProviderRouter } from "../routes/authProvider.js";
|
||||||
|
import type { AppEnv, StaffRow } from "../middleware/rbac.js";
|
||||||
|
|
||||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
// ─── Mock staff ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
interface MockStaff {
|
const SUPER_USER: StaffRow = {
|
||||||
id: string;
|
id: "staff-super-id",
|
||||||
role: string;
|
oidcSub: "oidc-super-sub",
|
||||||
isSuperUser: boolean;
|
userId: "ba-user-super",
|
||||||
}
|
role: "manager",
|
||||||
|
isSuperUser: true,
|
||||||
|
name: "Super S.",
|
||||||
|
email: "super@example.com",
|
||||||
|
active: true,
|
||||||
|
icalToken: null,
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
// ─── Mock DB state ────────────────────────────────────────────────────────────
|
const NON_SUPER_USER: StaffRow = {
|
||||||
|
...SUPER_USER,
|
||||||
|
id: "staff-mgr-id",
|
||||||
|
oidcSub: "oidc-mgr-sub",
|
||||||
|
role: "manager",
|
||||||
|
isSuperUser: false,
|
||||||
|
name: "Manager M.",
|
||||||
|
email: "mgr@example.com",
|
||||||
|
};
|
||||||
|
|
||||||
let dbRows: Record<string, unknown>[] = [];
|
// ─── Mock DB ─────────────────────────────────────────────────────────────────
|
||||||
let deletedRows: string[] = [];
|
|
||||||
let insertedRows: Record<string, unknown>[] = [];
|
|
||||||
let encryptCalls: string[] = [];
|
|
||||||
|
|
||||||
function resetMock() {
|
const DB_CONFIG = {
|
||||||
dbRows = [];
|
id: "config-id",
|
||||||
deletedRows = [];
|
providerId: "authentik",
|
||||||
insertedRows = [];
|
displayName: "Authentik",
|
||||||
encryptCalls = [];
|
issuerUrl: "https://auth.example.com",
|
||||||
}
|
internalBaseUrl: "http://authentik.auth.svc.cluster.local",
|
||||||
|
clientId: "test-client-id",
|
||||||
|
clientSecret: "iv:cipher:tag", // already encrypted
|
||||||
|
scopes: "openid profile email",
|
||||||
|
enabled: true,
|
||||||
|
createdAt: new Date("2026-01-01T00:00:00Z"),
|
||||||
|
updatedAt: new Date("2026-01-02T00:00:00Z"),
|
||||||
|
};
|
||||||
|
|
||||||
// ─── Mock staff context ───────────────────────────────────────────────────────
|
// Use vi.hoisted to create mutable state accessible to vi.mock factory
|
||||||
|
const mockState = vi.hoisted(() => {
|
||||||
const mockSuperUser: MockStaff = { id: "staff-1", role: "manager", isSuperUser: true };
|
const state = {
|
||||||
const mockManager: MockStaff = { id: "staff-2", role: "manager", isSuperUser: false };
|
dbSelectResult: [] as unknown[],
|
||||||
const mockGroomer: MockStaff = { id: "staff-3", role: "groomer", isSuperUser: false };
|
dbDeleteResult: { ok: true },
|
||||||
|
dbInsertResult: null as unknown,
|
||||||
// ─── Mock db module ───────────────────────────────────────────────────────────
|
dbUpdateResult: null as unknown,
|
||||||
|
mockEq: vi.fn((_col: unknown, _val: unknown) => ({ col: _col, val: _val })),
|
||||||
|
mockEncryptSecret: vi.fn((s: string) => `encrypted:${s}`),
|
||||||
|
};
|
||||||
|
return state;
|
||||||
|
});
|
||||||
|
|
||||||
vi.mock("@groombook/db", () => {
|
vi.mock("@groombook/db", () => {
|
||||||
const authProviderConfig = new Proxy(
|
const authProviderConfig = new Proxy(
|
||||||
{ _name: "auth_provider_config" },
|
{ _name: "auth_provider_config" },
|
||||||
{
|
{
|
||||||
get(_target, prop) {
|
get(target, prop) {
|
||||||
if (prop === "_name") return "auth_provider_config";
|
if (prop === "_name") return "auth_provider_config";
|
||||||
if (prop === "$inferSelect") return {};
|
if (prop === "$inferSelect") return {};
|
||||||
return { table: "auth_provider_config", column: prop };
|
return { table: "auth_provider_config", column: prop };
|
||||||
@@ -49,219 +75,280 @@ vi.mock("@groombook/db", () => {
|
|||||||
select: () => ({
|
select: () => ({
|
||||||
from: () => ({
|
from: () => ({
|
||||||
where: () => ({
|
where: () => ({
|
||||||
limit: () => [...dbRows],
|
limit: () => mockState.dbSelectResult,
|
||||||
[Symbol.iterator]: function* () {
|
[Symbol.iterator]: function* () {
|
||||||
for (const item of dbRows) yield item;
|
for (const item of mockState.dbSelectResult) yield item;
|
||||||
},
|
},
|
||||||
0: dbRows[0],
|
0: mockState.dbSelectResult[0],
|
||||||
length: dbRows.length,
|
length: mockState.dbSelectResult.length,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
insert: () => ({
|
delete: () => ({
|
||||||
values: (vals: Record<string, unknown>) => {
|
where: () => mockState.dbDeleteResult,
|
||||||
insertedRows.push(vals);
|
|
||||||
return {
|
|
||||||
returning: () => [{ ...vals, id: "new-id-1", createdAt: new Date(), updatedAt: new Date() }],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
delete: () => {
|
insert: () => ({
|
||||||
// Execute immediately - route doesn't chain .returning()
|
values: () => ({
|
||||||
deletedRows.push("all");
|
returning: () => [mockState.dbInsertResult],
|
||||||
return Promise.resolve([]);
|
}),
|
||||||
},
|
}),
|
||||||
transaction: <T>(fn: (tx: {
|
update: () => ({
|
||||||
delete: () => Promise<unknown>;
|
set: () => ({
|
||||||
insert: () => { values: (v: Record<string, unknown>) => { returning: () => T[] } };
|
where: () => ({
|
||||||
}) => Promise<T>) => {
|
returning: () => [mockState.dbUpdateResult],
|
||||||
const tx = {
|
|
||||||
delete: () => { deletedRows.push("all"); return Promise.resolve([]); },
|
|
||||||
insert: () => ({
|
|
||||||
values: (vals: Record<string, unknown>) => ({
|
|
||||||
returning: () => [{ ...vals, id: "new-id-1", createdAt: new Date(), updatedAt: new Date() }] as T[],
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
};
|
}),
|
||||||
return fn(tx);
|
}),
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
authProviderConfig,
|
authProviderConfig,
|
||||||
eq: (_col: unknown, _val: unknown) => ({ col: _col, val: _val }),
|
eq: mockState.mockEq,
|
||||||
encryptSecret: (val: string) => {
|
encryptSecret: mockState.mockEncryptSecret,
|
||||||
encryptCalls.push(val);
|
|
||||||
return `encrypted:${val}`;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// ─── Build test app ───────────────────────────────────────────────────────────
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function makeApp(staff: MockStaff | null) {
|
function buildApp(staff: StaffRow | null) {
|
||||||
const app = new Hono();
|
const app = new Hono<AppEnv>();
|
||||||
// Inject staff context + super user guard per route
|
app.use("*", async (c, next) => {
|
||||||
// Must match both exact path and wildcard subpaths
|
if (staff) {
|
||||||
app.use(
|
c.set("staff", staff);
|
||||||
"/admin/auth-provider/*",
|
c.set("jwtPayload", { sub: staff.userId ?? "" });
|
||||||
async (c, next) => {
|
|
||||||
if (!staff) {
|
|
||||||
return c.json({ error: "Forbidden: no staff record resolved" }, 403);
|
|
||||||
}
|
|
||||||
if (!staff.isSuperUser) {
|
|
||||||
return c.json({ error: "Forbidden: super user privileges required" }, 403);
|
|
||||||
}
|
|
||||||
(c as any).set("staff", staff);
|
|
||||||
await next();
|
|
||||||
}
|
}
|
||||||
);
|
await next();
|
||||||
app.route("/admin/auth-provider", authProviderRouter as unknown as Hono);
|
});
|
||||||
|
app.route("/", authProviderRouter);
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
// ─── Tests ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
async function get<T extends Hono = Hono>(app: T, path: string, staff: MockStaff | null) {
|
beforeEach(() => {
|
||||||
const res = await app.request(path, { method: "GET" }, { allCtx: { staff } as { staff: MockStaff } });
|
mockState.dbSelectResult = [];
|
||||||
return { status: res.status, body: await res.json() };
|
mockState.dbInsertResult = null;
|
||||||
}
|
mockState.dbUpdateResult = null;
|
||||||
|
mockState.dbDeleteResult = { ok: true };
|
||||||
async function put<T extends Hono = Hono>(app: T, path: string, body: unknown, staff: MockStaff | null) {
|
vi.clearAllMocks();
|
||||||
const res = await app.request(path, {
|
process.env.BETTER_AUTH_SECRET = "test-secret";
|
||||||
method: "PUT",
|
});
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(body),
|
|
||||||
}, { allCtx: { staff } as { staff: MockStaff } });
|
|
||||||
return { status: res.status, body: await res.json() };
|
|
||||||
}
|
|
||||||
|
|
||||||
async function post<T extends Hono = Hono>(app: T, path: string, body: unknown, staff: MockStaff | null) {
|
|
||||||
const res = await app.request(path, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(body),
|
|
||||||
}, { allCtx: { staff } as { staff: MockStaff } });
|
|
||||||
return { status: res.status, body: await res.json() };
|
|
||||||
}
|
|
||||||
|
|
||||||
async function del<T extends Hono = Hono>(app: T, path: string, staff: MockStaff | null) {
|
|
||||||
const res = await app.request(path, { method: "DELETE" }, { allCtx: { staff } as { staff: MockStaff } });
|
|
||||||
return { status: res.status, body: await res.json() };
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Tests ────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
describe("GET /admin/auth-provider", () => {
|
describe("GET /admin/auth-provider", () => {
|
||||||
beforeEach(resetMock);
|
it("returns exists:false when no config in DB", async () => {
|
||||||
|
mockState.dbSelectResult = [];
|
||||||
it("returns 404 when no provider configured", async () => {
|
const app = buildApp(SUPER_USER);
|
||||||
dbRows = [];
|
const res = await app.request("/");
|
||||||
const app = makeApp(mockSuperUser);
|
expect(res.status).toBe(200);
|
||||||
const { status, body } = await get(app, "/admin/auth-provider", mockSuperUser);
|
const body = await res.json();
|
||||||
expect(status).toBe(404);
|
expect(body).toEqual({ exists: false, config: null });
|
||||||
expect(body.error).toBe("No auth provider configured");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns config with secret redacted", async () => {
|
it("returns config with secret redacted", async () => {
|
||||||
dbRows = [{
|
mockState.dbSelectResult = [DB_CONFIG];
|
||||||
id: "prov-1",
|
const app = buildApp(SUPER_USER);
|
||||||
providerId: "authentik",
|
const res = await app.request("/");
|
||||||
displayName: "Authentik",
|
expect(res.status).toBe(200);
|
||||||
issuerUrl: "https://auth.example.com",
|
const body = await res.json();
|
||||||
internalBaseUrl: null,
|
expect(body.exists).toBe(true);
|
||||||
clientId: "client-123",
|
expect(body.config.clientSecret).toBe("••••••••");
|
||||||
clientSecret: "encrypted:secret",
|
expect(body.config.providerId).toBe("authentik");
|
||||||
scopes: "openid profile email",
|
|
||||||
enabled: true,
|
|
||||||
createdAt: new Date(),
|
|
||||||
updatedAt: new Date(),
|
|
||||||
}];
|
|
||||||
const app = makeApp(mockSuperUser);
|
|
||||||
const { status, body } = await get(app, "/admin/auth-provider", mockSuperUser);
|
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(body.clientSecret).toBe("••••••••");
|
|
||||||
expect(body.providerId).toBe("authentik");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns 403 when not super user", async () => {
|
it("returns 403 when staff is not a super user", async () => {
|
||||||
dbRows = [];
|
const app = buildApp(NON_SUPER_USER);
|
||||||
const app = makeApp(mockManager);
|
const res = await app.request("/");
|
||||||
const { status } = await get(app, "/admin/auth-provider", mockManager);
|
expect(res.status).toBe(403);
|
||||||
expect(status).toBe(403);
|
});
|
||||||
|
|
||||||
|
it("returns 403 when no staff context", async () => {
|
||||||
|
const app = buildApp(null);
|
||||||
|
const res = await app.request("/");
|
||||||
|
expect(res.status).toBe(403);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("PUT /admin/auth-provider", () => {
|
describe("PUT /admin/auth-provider", () => {
|
||||||
beforeEach(resetMock);
|
const validBody = {
|
||||||
|
providerId: "okta",
|
||||||
|
displayName: "Okta SSO",
|
||||||
|
issuerUrl: "https://okta.example.com",
|
||||||
|
internalBaseUrl: "http://okta.okta.svc.cluster.local",
|
||||||
|
clientId: "okta-client",
|
||||||
|
clientSecret: "super-secret",
|
||||||
|
scopes: "openid profile email",
|
||||||
|
};
|
||||||
|
|
||||||
it("stores encrypted secret", async () => {
|
it("inserts new config with encrypted secret", async () => {
|
||||||
const app = makeApp(mockSuperUser);
|
mockState.dbSelectResult = []; // no existing config
|
||||||
const { status, body } = await put(app, "/admin/auth-provider", {
|
mockState.dbInsertResult = { ...DB_CONFIG, providerId: "okta", displayName: "Okta SSO" };
|
||||||
providerId: "authentik",
|
|
||||||
displayName: "Authentik SSO",
|
const app = buildApp(SUPER_USER);
|
||||||
issuerUrl: "https://auth.example.com",
|
const res = await app.request("/", {
|
||||||
clientId: "my-client",
|
method: "PUT",
|
||||||
clientSecret: "my-secret",
|
headers: { "Content-Type": "application/json" },
|
||||||
scopes: "openid profile email",
|
body: JSON.stringify(validBody),
|
||||||
}, mockSuperUser);
|
});
|
||||||
expect(status).toBe(200);
|
|
||||||
expect(encryptCalls).toContain("my-secret");
|
expect(res.status).toBe(200);
|
||||||
|
expect(mockState.mockEncryptSecret).toHaveBeenCalledWith("super-secret");
|
||||||
|
const body = await res.json();
|
||||||
expect(body.clientSecret).toBe("••••••••");
|
expect(body.clientSecret).toBe("••••••••");
|
||||||
expect(body.providerId).toBe("authentik");
|
expect(body.providerId).toBe("okta");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns 400 for invalid schema", async () => {
|
it("updates existing config with encrypted secret", async () => {
|
||||||
const app = makeApp(mockSuperUser);
|
mockState.dbSelectResult = [{ ...DB_CONFIG, id: "existing-id" }];
|
||||||
const { status } = await put(app, "/admin/auth-provider", {
|
mockState.dbUpdateResult = { ...DB_CONFIG, providerId: "okta", displayName: "Okta SSO Updated" };
|
||||||
providerId: "",
|
|
||||||
issuerUrl: "not-a-url",
|
const app = buildApp(SUPER_USER);
|
||||||
}, mockSuperUser);
|
const res = await app.request("/", {
|
||||||
expect(status).toBe(400);
|
method: "PUT",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ ...validBody, displayName: "Okta SSO Updated" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
expect(mockState.mockEncryptSecret).toHaveBeenCalledWith("super-secret");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 400 on invalid schema", async () => {
|
||||||
|
const app = buildApp(SUPER_USER);
|
||||||
|
const res = await app.request("/", {
|
||||||
|
method: "PUT",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ providerId: "" }), // missing required fields
|
||||||
|
});
|
||||||
|
expect(res.status).toBe(400);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 403 when not super user", async () => {
|
||||||
|
const app = buildApp(NON_SUPER_USER);
|
||||||
|
const res = await app.request("/", {
|
||||||
|
method: "PUT",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(validBody),
|
||||||
|
});
|
||||||
|
expect(res.status).toBe(403);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("POST /admin/auth-provider/test", () => {
|
describe("POST /admin/auth-provider/test", () => {
|
||||||
beforeEach(resetMock);
|
const validBody = {
|
||||||
|
providerId: "okta",
|
||||||
|
issuerUrl: "https://okta.example.com",
|
||||||
|
clientId: "okta-client",
|
||||||
|
clientSecret: "super-secret",
|
||||||
|
};
|
||||||
|
|
||||||
it("returns ok=false for unreachable issuer", async () => {
|
it("returns ok:true with metadata on successful OIDC discovery", async () => {
|
||||||
const app = makeApp(mockSuperUser);
|
const mockMetadata = {
|
||||||
const { status, body } = await post(app, "/admin/auth-provider/test", {
|
issuer: "https://okta.example.com",
|
||||||
providerId: "authentik",
|
authorization_endpoint: "https://okta.example.com/authorize",
|
||||||
displayName: "Authentik",
|
token_endpoint: "https://okta.example.com/token",
|
||||||
issuerUrl: "https://192.0.2.1/", // TEST-NET, never reachable
|
};
|
||||||
clientId: "client",
|
|
||||||
scopes: "openid profile email",
|
vi.spyOn(global, "fetch").mockResolvedValueOnce(
|
||||||
}, mockSuperUser);
|
new Response(JSON.stringify(mockMetadata), {
|
||||||
expect(status).toBe(200);
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const app = buildApp(SUPER_USER);
|
||||||
|
const res = await app.request("/test", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(validBody),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
const body = await res.json();
|
||||||
|
expect(body.ok).toBe(true);
|
||||||
|
expect(body.metadata).toEqual(mockMetadata);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns ok:false with error when OIDC discovery fails", async () => {
|
||||||
|
vi.spyOn(global, "fetch").mockResolvedValueOnce(
|
||||||
|
new Response("Not Found", { status: 404 })
|
||||||
|
);
|
||||||
|
|
||||||
|
const app = buildApp(SUPER_USER);
|
||||||
|
const res = await app.request("/test", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(validBody),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
const body = await res.json();
|
||||||
expect(body.ok).toBe(false);
|
expect(body.ok).toBe(false);
|
||||||
expect(body.error).toBeTruthy();
|
expect(body.error).toContain("404");
|
||||||
}, 15000); // timeout must exceed the 10s fetch timeout in the route handler
|
});
|
||||||
|
|
||||||
it("returns 400 for missing clientSecret (not required for test)", async () => {
|
it("returns ok:false when fetch throws", async () => {
|
||||||
const app = makeApp(mockSuperUser);
|
vi.spyOn(global, "fetch").mockRejectedValueOnce(new Error("Network error"));
|
||||||
const { status } = await post(app, "/admin/auth-provider/test", {
|
|
||||||
providerId: "authentik",
|
const app = buildApp(SUPER_USER);
|
||||||
displayName: "Authentik",
|
const res = await app.request("/test", {
|
||||||
issuerUrl: "https://auth.example.com",
|
method: "POST",
|
||||||
clientId: "client",
|
headers: { "Content-Type": "application/json" },
|
||||||
}, mockSuperUser);
|
body: JSON.stringify(validBody),
|
||||||
expect(status).toBe(200); // clientSecret omitted intentionally for test
|
});
|
||||||
|
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
const body = await res.json();
|
||||||
|
expect(body.ok).toBe(false);
|
||||||
|
expect(body.error).toBe("Network error");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 400 on invalid schema", async () => {
|
||||||
|
const app = buildApp(SUPER_USER);
|
||||||
|
const res = await app.request("/test", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ providerId: "okta" }), // missing issuerUrl, clientId, clientSecret
|
||||||
|
});
|
||||||
|
expect(res.status).toBe(400);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 403 when not super user", async () => {
|
||||||
|
const app = buildApp(NON_SUPER_USER);
|
||||||
|
const res = await app.request("/test", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(validBody),
|
||||||
|
});
|
||||||
|
expect(res.status).toBe(403);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("DELETE /admin/auth-provider", () => {
|
describe("DELETE /admin/auth-provider", () => {
|
||||||
beforeEach(resetMock);
|
it("deletes existing config and returns ok", async () => {
|
||||||
|
mockState.dbSelectResult = [{ id: DB_CONFIG.id }];
|
||||||
|
mockState.dbDeleteResult = { ok: true };
|
||||||
|
|
||||||
it("deletes all config rows", async () => {
|
const app = buildApp(SUPER_USER);
|
||||||
const app = makeApp(mockSuperUser);
|
const res = await app.request("/", { method: "DELETE" });
|
||||||
const { status, body } = await del(app, "/admin/auth-provider", mockSuperUser);
|
|
||||||
expect(status).toBe(200);
|
expect(res.status).toBe(200);
|
||||||
|
const body = await res.json();
|
||||||
expect(body.ok).toBe(true);
|
expect(body.ok).toBe(true);
|
||||||
expect(deletedRows).toContain("all");
|
});
|
||||||
|
|
||||||
|
it("returns ok:true when no config exists", async () => {
|
||||||
|
mockState.dbSelectResult = [];
|
||||||
|
|
||||||
|
const app = buildApp(SUPER_USER);
|
||||||
|
const res = await app.request("/", { method: "DELETE" });
|
||||||
|
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
const body = await res.json();
|
||||||
|
expect(body.ok).toBe(true);
|
||||||
|
expect(body.message).toContain("No DB config");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns 403 when not super user", async () => {
|
it("returns 403 when not super user", async () => {
|
||||||
const app = makeApp(mockGroomer);
|
const app = buildApp(NON_SUPER_USER);
|
||||||
const { status } = await del(app, "/admin/auth-provider", mockGroomer);
|
const res = await app.request("/", { method: "DELETE" });
|
||||||
expect(status).toBe(403);
|
expect(res.status).toBe(403);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { zValidator } from "@hono/zod-validator";
|
import { zValidator } from "@hono/zod-validator";
|
||||||
import { z } from "zod/v3";
|
import { z } from "zod/v3";
|
||||||
import { eq, getDb, authProviderConfig, encryptSecret, decryptSecret } from "@groombook/db";
|
import { eq, getDb, authProviderConfig, encryptSecret } from "@groombook/db";
|
||||||
import { requireSuperUser } from "../../middleware/rbac.js";
|
import { requireSuperUser } from "../../middleware/rbac.js";
|
||||||
|
|
||||||
export const authProviderRouter = new Hono();
|
export const authProviderRouter = new Hono();
|
||||||
|
|||||||
Reference in New Issue
Block a user