import { describe, it, expect, beforeAll, afterAll, beforeEach } from "vitest"; import { testPrisma, setupTestDatabase, cleanupTestDatabase, createTestUser, createTestCompany, createTestCustomer } from "./setup"; import bcrypt from "bcryptjs"; /** * Integration Tests for API Routes * * These tests require a test database. * They will skip gracefully if the database is not available. */ describe("API Integration Tests (Database Required)", () => { let testUser: any; let testCompany: any; let testCustomer: any; let dbAvailable = false; // Setup before all tests beforeAll(async () => { dbAvailable = await setupTestDatabase(); if (!dbAvailable) { console.warn("Skipping database integration tests - no test database available"); return; } // Create test user testUser = await createTestUser("test@example.com", "testuser"); // Create test company testCompany = await createTestCompany(testUser.id, "Integration Test GmbH"); // Create test customer testCustomer = await createTestCustomer(testCompany.id, "Integration Test Customer"); }); // Cleanup after all tests afterAll(async () => { if (!dbAvailable) return; await cleanupTestDatabase(); await testPrisma.$disconnect(); }); // Clean data between tests (but keep user/company/customer) beforeEach(async () => { if (!dbAvailable) return; // Delete invoices and related items await testPrisma.invoiceItem.deleteMany({ where: { invoice: { companyId: testCompany.id } }, }); await testPrisma.invoice.deleteMany({ where: { companyId: testCompany.id }, }); }); // Helper to skip tests if no database const dbTest = (name: string, fn: () => Promise) => { it(name, async () => { if (!dbAvailable) { console.warn(`Skipping "${name}" - no test database`); return; } await fn(); }); }; describe("Companies API", () => { dbTest("should list companies for a user", async () => { const companies = await testPrisma.company.findMany({ where: { userId: testUser.id }, }); expect(companies).toBeDefined(); expect(companies.length).toBeGreaterThan(0); expect(companies[0].name).toBe("Integration Test GmbH"); }); dbTest("should create a new company", async () => { const newCompanyData = { name: "New Test Company", address: "New Street 1", zip: "99999", city: "Munich", country: "DE", }; const company = await testPrisma.company.create({ data: { ...newCompanyData, userId: testUser.id, }, }); expect(company).toBeDefined(); expect(company.name).toBe("New Test Company"); expect(company.userId).toBe(testUser.id); }); }); describe("Invoices API", () => { dbTest("should create a new invoice", async () => { const { calcItemAmounts, calcInvoiceTotals } = await import("@/lib/tax"); const invoiceData = { companyId: testCompany.id, customerId: testCustomer.id, issueDate: new Date(), dueDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), kleinunternehmer: false, items: [ { position: 1, description: "Test Service", quantity: 10, unit: "Stunden", unitPrice: 100, taxRate: 19, netAmount: 1000, taxAmount: 190, grossAmount: 1190, }, ], }; // Recalculate server-side (as the API does) const recalculatedItems = invoiceData.items.map(item => ({ ...item, ...calcItemAmounts(item.quantity, item.unitPrice, item.taxRate), })); const totals = calcInvoiceTotals(recalculatedItems); const invoice = await testPrisma.invoice.create({ data: { companyId: invoiceData.companyId, customerId: invoiceData.customerId, issueDate: invoiceData.issueDate, dueDate: invoiceData.dueDate, status: "DRAFT", kleinunternehmer: invoiceData.kleinunternehmer, netTotal: totals.netTotal, taxTotal: totals.taxTotal, grossTotal: totals.grossTotal, items: { create: recalculatedItems.map((item, idx) => ({ position: idx + 1, description: item.description, quantity: item.quantity, unit: item.unit, unitPrice: item.unitPrice, taxRate: item.taxRate, netAmount: item.netAmount, taxAmount: item.taxAmount, grossAmount: item.grossAmount, })), }, }, include: { items: true, customer: true }, }); expect(invoice).toBeDefined(); expect(invoice.status).toBe("DRAFT"); expect(invoice.items).toHaveLength(1); expect(invoice.grossTotal).toBe(1190); }); }); describe("Buchungen API", () => { dbTest("should create a new Buchung", async () => { const buchungData = { companyId: testCompany.id, date: new Date(), account: "KASSE" as const, type: "EINLAGE" as const, amount: 1000, description: "Initial investment", isBusinessRecord: false, }; const buchung = await testPrisma.buchung.create({ data: buchungData, }); expect(buchung).toBeDefined(); expect(buchung.account).toBe("KASSE"); expect(buchung.type).toBe("EINLAGE"); expect(buchung.amount.toNumber()).toBe(1000); }); }); });