feat(#3): Add comprehensive tests and improve Prisma seed script
- Create comprehensive test suite for PrismaService (10 tests) - Fix AppController tests with proper PrismaService mocking - Wrap seed operations in transaction for atomicity - Replace N+1 pattern with batch operations (createMany) - Add concurrency warning to seed script - All tests passing (14/14) - Build successful - Test coverage >85% Fixes #3 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,20 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { AppService } from "./app.service";
|
||||
import { AppController } from "./app.controller";
|
||||
import { PrismaService } from "./prisma/prisma.service";
|
||||
|
||||
describe("AppController", () => {
|
||||
const appService = new AppService();
|
||||
const controller = new AppController(appService);
|
||||
|
||||
// Mock PrismaService
|
||||
const mockPrismaService = {
|
||||
isHealthy: vi.fn(),
|
||||
getConnectionInfo: vi.fn(),
|
||||
$connect: vi.fn(),
|
||||
$disconnect: vi.fn(),
|
||||
} as unknown as PrismaService;
|
||||
|
||||
const controller = new AppController(appService, mockPrismaService);
|
||||
|
||||
describe("getHello", () => {
|
||||
it('should return "Mosaic Stack API"', () => {
|
||||
@@ -13,11 +23,33 @@ describe("AppController", () => {
|
||||
});
|
||||
|
||||
describe("getHealth", () => {
|
||||
it("should return health status", () => {
|
||||
const result = controller.getHealth();
|
||||
it("should return health status", async () => {
|
||||
// Setup mocks
|
||||
vi.mocked(mockPrismaService.isHealthy).mockResolvedValue(true);
|
||||
vi.mocked(mockPrismaService.getConnectionInfo).mockResolvedValue({
|
||||
connected: true,
|
||||
database: "mosaic",
|
||||
version: "PostgreSQL 17",
|
||||
});
|
||||
|
||||
const result = await controller.getHealth();
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.status).toBe("healthy");
|
||||
expect(result.data.timestamp).toBeDefined();
|
||||
expect(result.data.checks.database.status).toBe("healthy");
|
||||
});
|
||||
|
||||
it("should return degraded status when database is unhealthy", async () => {
|
||||
// Setup mocks for unhealthy state
|
||||
vi.mocked(mockPrismaService.isHealthy).mockResolvedValue(false);
|
||||
vi.mocked(mockPrismaService.getConnectionInfo).mockResolvedValue({
|
||||
connected: false,
|
||||
});
|
||||
|
||||
const result = await controller.getHealth();
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.status).toBe("degraded");
|
||||
expect(result.data.checks.database.status).toBe("unhealthy");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
130
apps/api/src/prisma/prisma.service.spec.ts
Normal file
130
apps/api/src/prisma/prisma.service.spec.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
||||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { PrismaService } from "./prisma.service";
|
||||
|
||||
describe("PrismaService", () => {
|
||||
let service: PrismaService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [PrismaService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<PrismaService>(PrismaService);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await service.$disconnect();
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
|
||||
describe("onModuleInit", () => {
|
||||
it("should connect to the database", async () => {
|
||||
const connectSpy = vi
|
||||
.spyOn(service, "$connect")
|
||||
.mockResolvedValue(undefined);
|
||||
|
||||
await service.onModuleInit();
|
||||
|
||||
expect(connectSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should throw error if connection fails", async () => {
|
||||
const error = new Error("Connection failed");
|
||||
vi.spyOn(service, "$connect").mockRejectedValue(error);
|
||||
|
||||
await expect(service.onModuleInit()).rejects.toThrow(error);
|
||||
});
|
||||
});
|
||||
|
||||
describe("onModuleDestroy", () => {
|
||||
it("should disconnect from the database", async () => {
|
||||
const disconnectSpy = vi
|
||||
.spyOn(service, "$disconnect")
|
||||
.mockResolvedValue(undefined);
|
||||
|
||||
await service.onModuleDestroy();
|
||||
|
||||
expect(disconnectSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("isHealthy", () => {
|
||||
it("should return true when database is accessible", async () => {
|
||||
vi.spyOn(service, "$queryRaw").mockResolvedValue([{ result: 1 }]);
|
||||
|
||||
const result = await service.isHealthy();
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false when database is not accessible", async () => {
|
||||
vi
|
||||
.spyOn(service, "$queryRaw")
|
||||
.mockRejectedValue(new Error("Database error"));
|
||||
|
||||
const result = await service.isHealthy();
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getConnectionInfo", () => {
|
||||
it("should return connection info when connected", async () => {
|
||||
const mockResult = [
|
||||
{
|
||||
current_database: "test_db",
|
||||
version: "PostgreSQL 17.0 on x86_64-linux",
|
||||
},
|
||||
];
|
||||
vi.spyOn(service, "$queryRaw").mockResolvedValue(mockResult);
|
||||
|
||||
const result = await service.getConnectionInfo();
|
||||
|
||||
expect(result).toEqual({
|
||||
connected: true,
|
||||
database: "test_db",
|
||||
version: "PostgreSQL",
|
||||
});
|
||||
});
|
||||
|
||||
it("should return connected false when result is empty", async () => {
|
||||
vi.spyOn(service, "$queryRaw").mockResolvedValue([]);
|
||||
|
||||
const result = await service.getConnectionInfo();
|
||||
|
||||
expect(result).toEqual({ connected: false });
|
||||
});
|
||||
|
||||
it("should return connected false when query fails", async () => {
|
||||
vi
|
||||
.spyOn(service, "$queryRaw")
|
||||
.mockRejectedValue(new Error("Query failed"));
|
||||
|
||||
const result = await service.getConnectionInfo();
|
||||
|
||||
expect(result).toEqual({ connected: false });
|
||||
});
|
||||
|
||||
it("should handle version without split", async () => {
|
||||
const mockResult = [
|
||||
{
|
||||
current_database: "test_db",
|
||||
version: "PostgreSQL",
|
||||
},
|
||||
];
|
||||
vi.spyOn(service, "$queryRaw").mockResolvedValue(mockResult);
|
||||
|
||||
const result = await service.getConnectionInfo();
|
||||
|
||||
expect(result).toEqual({
|
||||
connected: true,
|
||||
database: "test_db",
|
||||
version: "PostgreSQL",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user