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:
162
apps/api/src/parts/parts.service.spec.ts
Normal file
162
apps/api/src/parts/parts.service.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user