test: add comprehensive unit tests for all API services and guards

Add 11 new test files covering roles guard, brand-access guard, users,
brands, plans, payments, vehicles, categories, parts, referrals, and
admin services. Total test count increases from ~52 to 164, all passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sase Dev
2026-02-12 04:24:55 +00:00
parent ffa9781eb9
commit 8fb7bbbaca
11 changed files with 2270 additions and 0 deletions

View File

@@ -0,0 +1,156 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { ForbiddenException } from "@nestjs/common";
import { BrandAccessGuard } from "./brand-access.guard";
function createMockDb(overrides: Record<string, unknown> = {}) {
function chainable(terminalValue: unknown) {
const chain: Record<string, unknown> = {};
const methods = ["select", "from", "where", "orderBy", "limit", "offset", "innerJoin", "insert", "values", "update", "set", "returning"];
for (const m of methods) chain[m] = vi.fn().mockReturnValue(chain);
chain.limit = vi.fn().mockReturnValue(terminalValue);
return chain;
}
return {
select: vi.fn().mockImplementation(() => chainable(overrides._selectRows ?? [])),
...overrides,
};
}
function createMockContext(options: { user?: any; params?: any; body?: any }) {
const request: Record<string, unknown> = {
params: options.params ?? {},
body: options.body ?? {},
};
if (options.user) request.user = options.user;
return {
switchToHttp: vi.fn().mockReturnValue({
getRequest: vi.fn().mockReturnValue(request),
}),
};
}
describe("BrandAccessGuard", () => {
it("should throw ForbiddenException when no user", async () => {
const db = createMockDb();
const guard = new BrandAccessGuard(db as any);
const context = createMockContext({});
await expect(guard.canActivate(context as any)).rejects.toThrow(ForbiddenException);
});
it("should allow admin users (bypass)", async () => {
const db = createMockDb();
const guard = new BrandAccessGuard(db as any);
const context = createMockContext({
user: { id: "u1", role: "admin" },
params: { brandId: "brand-1" },
});
const result = await guard.canActivate(context as any);
expect(result).toBe(true);
});
it("should allow when no brandId in request", async () => {
const db = createMockDb();
const guard = new BrandAccessGuard(db as any);
const context = createMockContext({
user: { id: "u1", role: "user" },
});
const result = await guard.canActivate(context as any);
expect(result).toBe(true);
});
it("should throw ForbiddenException when no active subscription", async () => {
const db = createMockDb({ _selectRows: [] });
const guard = new BrandAccessGuard(db as any);
const context = createMockContext({
user: { id: "u1", role: "user" },
params: { brandId: "brand-1" },
});
await expect(guard.canActivate(context as any)).rejects.toThrow(ForbiddenException);
});
it("should throw ForbiddenException when active sub but no brand access", async () => {
let callCount = 0;
const db = {
select: vi.fn().mockImplementation(() => {
callCount++;
const chain: Record<string, any> = {
from: vi.fn().mockReturnThis(),
where: vi.fn().mockReturnThis(),
limit: vi.fn().mockImplementation(() => {
if (callCount === 1) return [{ id: "sub-1", userId: "u1", status: "active" }];
return []; // no brand access
}),
};
return chain;
}),
};
const guard = new BrandAccessGuard(db as any);
const context = createMockContext({
user: { id: "u1", role: "user" },
params: { brandId: "brand-1" },
});
await expect(guard.canActivate(context as any)).rejects.toThrow(ForbiddenException);
});
it("should allow when active sub + brand access", async () => {
let callCount = 0;
const db = {
select: vi.fn().mockImplementation(() => {
callCount++;
const chain: Record<string, any> = {
from: vi.fn().mockReturnThis(),
where: vi.fn().mockReturnThis(),
limit: vi.fn().mockImplementation(() => {
if (callCount === 1) return [{ id: "sub-1", userId: "u1", status: "active" }];
return [{ id: "ba-1", userId: "u1", brandId: "brand-1" }];
}),
};
return chain;
}),
};
const guard = new BrandAccessGuard(db as any);
const context = createMockContext({
user: { id: "u1", role: "user" },
params: { brandId: "brand-1" },
});
const result = await guard.canActivate(context as any);
expect(result).toBe(true);
});
it("should read brandId from body when not in params", async () => {
let callCount = 0;
const db = {
select: vi.fn().mockImplementation(() => {
callCount++;
const chain: Record<string, any> = {
from: vi.fn().mockReturnThis(),
where: vi.fn().mockReturnThis(),
limit: vi.fn().mockImplementation(() => {
if (callCount === 1) return [{ id: "sub-1", userId: "u1", status: "active" }];
return [{ id: "ba-1", userId: "u1", brandId: "brand-body" }];
}),
};
return chain;
}),
};
const guard = new BrandAccessGuard(db as any);
const context = createMockContext({
user: { id: "u1", role: "user" },
body: { brandId: "brand-body" },
});
const result = await guard.canActivate(context as any);
expect(result).toBe(true);
});
});

View File

@@ -0,0 +1,70 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { ForbiddenException } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
import { RolesGuard } from "./roles.guard";
function createMockExecutionContext(user?: { role: string }) {
const request: Record<string, unknown> = {};
if (user) request.user = user;
return {
getHandler: vi.fn(),
getClass: vi.fn(),
switchToHttp: vi.fn().mockReturnValue({
getRequest: vi.fn().mockReturnValue(request),
}),
};
}
describe("RolesGuard", () => {
let guard: RolesGuard;
let reflector: Reflector;
beforeEach(() => {
vi.clearAllMocks();
reflector = { getAllAndOverride: vi.fn() } as unknown as Reflector;
guard = new RolesGuard(reflector);
});
it("should allow access when no roles are required", () => {
vi.mocked(reflector.getAllAndOverride).mockReturnValue(undefined);
const context = createMockExecutionContext({ role: "user" });
expect(guard.canActivate(context as any)).toBe(true);
});
it("should allow access when roles array is empty", () => {
vi.mocked(reflector.getAllAndOverride).mockReturnValue([]);
const context = createMockExecutionContext({ role: "user" });
expect(guard.canActivate(context as any)).toBe(true);
});
it("should allow access when user has matching role", () => {
vi.mocked(reflector.getAllAndOverride).mockReturnValue(["admin"]);
const context = createMockExecutionContext({ role: "admin" });
expect(guard.canActivate(context as any)).toBe(true);
});
it("should throw ForbiddenException when user has non-matching role", () => {
vi.mocked(reflector.getAllAndOverride).mockReturnValue(["admin"]);
const context = createMockExecutionContext({ role: "user" });
expect(() => guard.canActivate(context as any)).toThrow(ForbiddenException);
});
it("should throw ForbiddenException when no user on request", () => {
vi.mocked(reflector.getAllAndOverride).mockReturnValue(["admin"]);
const context = createMockExecutionContext();
expect(() => guard.canActivate(context as any)).toThrow(ForbiddenException);
});
it("should allow when user matches one of multiple required roles", () => {
vi.mocked(reflector.getAllAndOverride).mockReturnValue(["admin", "moderator"]);
const context = createMockExecutionContext({ role: "moderator" });
expect(guard.canActivate(context as any)).toBe(true);
});
});