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,162 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { NotFoundException } from "@nestjs/common";
import { PartsService } from "./parts.service";
function createService(db: any) {
const pl24Service = {
getParts: vi.fn().mockResolvedValue([]),
};
const service = new PartsService(db as any, pl24Service as any);
return { service, db, pl24Service };
}
/** Build a simple chainable mock where limit is the terminal */
function simpleChain(terminalValue: unknown) {
const chain: Record<string, any> = {};
chain.from = vi.fn().mockReturnValue(chain);
chain.where = vi.fn().mockReturnValue(chain);
chain.orderBy = vi.fn().mockReturnValue(chain);
chain.innerJoin = vi.fn().mockReturnValue(chain);
chain.leftJoin = vi.fn().mockReturnValue(chain);
chain.limit = vi.fn().mockReturnValue(terminalValue);
return chain;
}
describe("PartsService", () => {
beforeEach(() => {
vi.clearAllMocks();
});
describe("getByCategory", () => {
it("should return parts from DB when cached", async () => {
const dbParts = [{ id: "p1", name: "Oil Filter", oemCode: "OEM-123" }];
// getByCategory: select().from(parts).where(...) — where is terminal here (no limit)
const db = {
select: vi.fn().mockReturnValue({
from: vi.fn().mockReturnThis(),
where: vi.fn().mockReturnValue(dbParts),
}),
};
const { service } = createService(db);
const result = await service.getByCategory("cat-1");
expect(result).toEqual(dbParts);
});
it("should fetch from PL24 when DB is empty", async () => {
const category = { id: "cat-1", vehicleId: "v1", externalId: "g1" };
const vehicle = { id: "v1", rawData: { vehicleId: "pl24-v1" }, brandName: "BMW" };
const pl24Parts = [{ name: "Oil Filter", oemCodes: ["OEM-1"], description: "Filter", quantity: 1, position: null, hotspotIndex: null }];
const insertedParts = [{ id: "p1", name: "Oil Filter", oemCode: "OEM-1" }];
let selectCall = 0;
const db = {
select: vi.fn().mockImplementation(() => {
selectCall++;
const captured = selectCall;
const chain: Record<string, any> = {};
chain.from = vi.fn().mockReturnValue(chain);
chain.where = vi.fn().mockImplementation(() => {
if (captured === 1) return []; // no parts in DB (where is terminal)
return chain;
});
chain.limit = vi.fn().mockImplementation(() => {
if (captured === 2) return [category];
if (captured === 3) return [vehicle];
return [];
});
return chain;
}),
insert: vi.fn().mockReturnValue({
values: vi.fn().mockReturnThis(),
returning: vi.fn().mockReturnValue(insertedParts),
}),
};
const { service, pl24Service } = createService(db);
pl24Service.getParts.mockResolvedValue(pl24Parts);
const result = await service.getByCategory("cat-1");
expect(pl24Service.getParts).toHaveBeenCalledWith("pl24-v1", "g1", "BMW");
expect(result).toEqual(insertedParts);
});
it("should throw NotFoundException when category not found", async () => {
let selectCall = 0;
const db = {
select: vi.fn().mockImplementation(() => {
selectCall++;
const captured = selectCall;
const chain: Record<string, any> = {};
chain.from = vi.fn().mockReturnValue(chain);
chain.where = vi.fn().mockImplementation(() => {
if (captured === 1) return []; // no parts
return chain;
});
chain.limit = vi.fn().mockReturnValue([]); // no category
return chain;
}),
};
const { service } = createService(db);
await expect(service.getByCategory("nonexistent")).rejects.toThrow(NotFoundException);
});
it("should throw NotFoundException when vehicle not found", async () => {
const category = { id: "cat-1", vehicleId: "v1", externalId: "g1" };
let selectCall = 0;
const db = {
select: vi.fn().mockImplementation(() => {
selectCall++;
const captured = selectCall;
const chain: Record<string, any> = {};
chain.from = vi.fn().mockReturnValue(chain);
chain.where = vi.fn().mockImplementation(() => {
if (captured === 1) return []; // no parts
return chain;
});
chain.limit = vi.fn().mockImplementation(() => {
if (captured === 2) return [category];
return []; // vehicle not found
});
return chain;
}),
};
const { service } = createService(db);
await expect(service.getByCategory("cat-1")).rejects.toThrow(NotFoundException);
});
});
describe("searchByOem", () => {
it("should return matching parts with vehicle info", async () => {
const results = [{ part: { id: "p1", oemCode: "OEM-123" }, vehicle: { vin: "VIN1" } }];
// searchByOem: select().from().innerJoin().where().limit(50)
const db = { select: vi.fn().mockReturnValue(simpleChain(results)) };
const { service } = createService(db);
const result = await service.searchByOem("OEM-123");
expect(result).toEqual(results);
});
});
describe("getById", () => {
it("should return part when found", async () => {
const part = { id: "p1", name: "Oil Filter" };
// getById: select().from().where().limit(1)
const db = { select: vi.fn().mockReturnValue(simpleChain([part])) };
const { service } = createService(db);
const result = await service.getById("p1");
expect(result).toEqual(part);
});
it("should throw NotFoundException when not found", async () => {
const db = { select: vi.fn().mockReturnValue(simpleChain([])) };
const { service } = createService(db);
await expect(service.getById("nonexistent")).rejects.toThrow(NotFoundException);
});
});
});