Files
stack/apps/api/src/prisma/prisma.service.spec.ts
T
Jason WoltjeandClaude Opus 4.5 10b49c4afb fix(tests): Resolve pipeline #243 test failures
Fixed 27 test failures by addressing several categories of issues:

Security spec tests (coordinator-integration, stitcher):
- Changed async test assertions to synchronous since ApiKeyGuard.canActivate
  is synchronous and throws directly rather than returning rejected promises
- Use expect(() => fn()).toThrow() instead of await expect(fn()).rejects.toThrow()

Federation controller tests:
- Added CsrfGuard and WorkspaceGuard mock overrides to test module
- Set DEFAULT_WORKSPACE_ID environment variable for handleIncomingConnection tests
- Added proper afterEach cleanup for environment variable restoration

Federation service tests:
- Updated RSA key generation tests to use Vitest 4.x timeout syntax
  (second argument as options object, not third argument)

Prisma service tests:
- Replaced vi.spyOn for $transaction and setWorkspaceContext with direct
  method assignment to avoid spy restoration issues
- Added vi.clearAllMocks() in afterEach to properly reset between tests

Integration tests (job-events, fulltext-search):
- Added conditional skip when DATABASE_URL is not set to prevent failures
  in environments without database access

Remaining 7 failures are pre-existing fulltext-search integration tests
that require specific PostgreSQL triggers not present in test database.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
2026-02-06 12:15:21 -06:00

256 lines
7.9 KiB
TypeScript

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();
vi.clearAllMocks();
});
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",
});
});
});
describe("setWorkspaceContext", () => {
it("should set workspace context variables in transaction", async () => {
const userId = "user-123";
const workspaceId = "workspace-456";
const executeRawSpy = vi.spyOn(service, "$executeRaw").mockResolvedValue(0);
// Mock $transaction to execute the callback with a mock tx client
const mockTx = {
$executeRaw: vi.fn().mockResolvedValue(0),
};
vi.spyOn(service, "$transaction").mockImplementation(async (fn) => {
return fn(mockTx as never);
});
await service.$transaction(async (tx) => {
await service.setWorkspaceContext(userId, workspaceId, tx as never);
});
expect(mockTx.$executeRaw).toHaveBeenCalledTimes(2);
});
it("should work when called outside transaction using default client", async () => {
const userId = "user-123";
const workspaceId = "workspace-456";
const executeRawSpy = vi.spyOn(service, "$executeRaw").mockResolvedValue(0);
await service.setWorkspaceContext(userId, workspaceId);
expect(executeRawSpy).toHaveBeenCalledTimes(2);
});
});
describe("withWorkspaceContext", () => {
it("should execute function with workspace context set", async () => {
const userId = "user-123";
const workspaceId = "workspace-456";
// Mock $transaction to execute the callback with a mock tx client that has $executeRaw
const mockTx = {
$executeRaw: vi.fn().mockResolvedValue(0),
};
// Mock both methods at the same time to avoid spy issues
const originalSetContext = service.setWorkspaceContext.bind(service);
const setContextCalls: [string, string, unknown][] = [];
service.setWorkspaceContext = vi.fn().mockImplementation((uid, wid, tx) => {
setContextCalls.push([uid, wid, tx]);
return Promise.resolve();
}) as typeof service.setWorkspaceContext;
service.$transaction = vi.fn().mockImplementation(async (fn) => {
return fn(mockTx as never);
}) as typeof service.$transaction;
const result = await service.withWorkspaceContext(userId, workspaceId, async () => {
return "test-result";
});
expect(result).toBe("test-result");
expect(setContextCalls).toHaveLength(1);
expect(setContextCalls[0]).toEqual([userId, workspaceId, mockTx]);
});
it("should pass transaction client to callback", async () => {
const userId = "user-123";
const workspaceId = "workspace-456";
// Mock $transaction to execute the callback with a mock tx client
const mockTx = {
$executeRaw: vi.fn().mockResolvedValue(0),
};
service.setWorkspaceContext = vi
.fn()
.mockResolvedValue(undefined) as typeof service.setWorkspaceContext;
service.$transaction = vi.fn().mockImplementation(async (fn) => {
return fn(mockTx as never);
}) as typeof service.$transaction;
let receivedClient: unknown = null;
await service.withWorkspaceContext(userId, workspaceId, async (tx) => {
receivedClient = tx;
return null;
});
expect(receivedClient).toBeDefined();
expect(receivedClient).toHaveProperty("$executeRaw");
});
it("should handle errors from callback", async () => {
const userId = "user-123";
const workspaceId = "workspace-456";
// Mock $transaction to execute the callback with a mock tx client
const mockTx = {
$executeRaw: vi.fn().mockResolvedValue(0),
};
service.setWorkspaceContext = vi
.fn()
.mockResolvedValue(undefined) as typeof service.setWorkspaceContext;
service.$transaction = vi.fn().mockImplementation(async (fn) => {
return fn(mockTx as never);
}) as typeof service.$transaction;
const error = new Error("Callback error");
await expect(
service.withWorkspaceContext(userId, workspaceId, async () => {
throw error;
})
).rejects.toThrow(error);
});
});
describe("clearWorkspaceContext", () => {
it("should clear workspace context variables", async () => {
// Mock $transaction to execute the callback with a mock tx client
const mockTx = {
$executeRaw: vi.fn().mockResolvedValue(0),
};
vi.spyOn(service, "$transaction").mockImplementation(async (fn) => {
return fn(mockTx as never);
});
await service.$transaction(async (tx) => {
await service.clearWorkspaceContext(tx as never);
});
expect(mockTx.$executeRaw).toHaveBeenCalledTimes(2);
});
});
});