db953b1e28
- Implement tests for client validation functions including tax ID, VAT ID, IBAN, BIC, website, and company form validation. - Create tests for revenue and expense categories ensuring all expected categories and labels are present. - Add tests for invoice number generation with various scenarios including prefix handling and sequence padding. - Introduce tests for default categories and their integration, ensuring no overlaps and consistent naming conventions. - Implement Zod schema validation tests for currency, tax rates, IBAN, tax ID, VAT ID, invoices, companies, and customers. - Add utility tests for tax calculations, including item amounts and invoice totals, ensuring correct handling of tax rates and formatting. - Set up Vitest configuration and global test setup for consistent testing environment.
199 lines
5.4 KiB
TypeScript
199 lines
5.4 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
|
|
/**
|
|
* Simple Integration Tests
|
|
*
|
|
* These tests verify that the API logic works correctly
|
|
* without requiring a full database connection.
|
|
*/
|
|
|
|
describe("API Integration (Simple)", () => {
|
|
describe("Company API Logic", () => {
|
|
it("should validate company creation data", async () => {
|
|
const { companySchema } = await import("@/lib/schemas");
|
|
|
|
const validCompany = {
|
|
name: "Test GmbH",
|
|
address: "Hauptstraße 1",
|
|
zip: "12345",
|
|
city: "Berlin",
|
|
country: "DE",
|
|
invoicePrefix: "RE",
|
|
kleinunternehmer: false,
|
|
taxId: null,
|
|
vatId: null,
|
|
bankIban: null,
|
|
bankBic: "",
|
|
};
|
|
|
|
const result = companySchema.safeParse(validCompany);
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it("should reject invalid company data", async () => {
|
|
const { companySchema } = await import("@/lib/schemas");
|
|
|
|
const invalidCompany = {
|
|
name: "", // Invalid: empty name
|
|
address: "Test St. 1",
|
|
zip: "12345",
|
|
city: "Berlin",
|
|
};
|
|
|
|
const result = companySchema.safeParse(invalidCompany);
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("Invoice API Logic", () => {
|
|
it("should validate invoice creation data", async () => {
|
|
const { invoiceSchema } = await import("@/lib/schemas");
|
|
|
|
const validInvoice = {
|
|
companyId: "company-123",
|
|
customerId: "customer-456",
|
|
issueDate: "2026-05-08",
|
|
dueDate: "2026-06-08",
|
|
kleinunternehmer: false,
|
|
items: [
|
|
{
|
|
position: 1,
|
|
description: "Web Development",
|
|
quantity: 10,
|
|
unit: "Stunden",
|
|
unitPrice: 100,
|
|
taxRate: 19,
|
|
netAmount: 1000,
|
|
taxAmount: 190,
|
|
grossAmount: 1190,
|
|
},
|
|
],
|
|
netTotal: 1000,
|
|
taxTotal: 190,
|
|
grossTotal: 1190,
|
|
};
|
|
|
|
const result = invoiceSchema.safeParse(validInvoice);
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it("should reject invoice with no items", async () => {
|
|
const { invoiceSchema } = await import("@/lib/schemas");
|
|
|
|
const invalidInvoice = {
|
|
companyId: "company-123",
|
|
customerId: "customer-456",
|
|
issueDate: "2026-05-08",
|
|
dueDate: "2026-06-08",
|
|
items: [], // Invalid: no items
|
|
netTotal: 0,
|
|
taxTotal: 0,
|
|
grossTotal: 0,
|
|
};
|
|
|
|
const result = invoiceSchema.safeParse(invalidInvoice);
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("Customer API Logic", () => {
|
|
it("should validate customer data", async () => {
|
|
const { customerSchema } = await import("@/lib/schemas");
|
|
|
|
const validCustomer = {
|
|
companyId: "company-123",
|
|
name: "Max Mustermann",
|
|
address: "Musterstraße 1",
|
|
zip: "12345",
|
|
city: "Berlin",
|
|
country: "DE",
|
|
};
|
|
|
|
const result = customerSchema.safeParse(validCustomer);
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it("should require companyId", async () => {
|
|
const { customerSchema } = await import("@/lib/schemas");
|
|
|
|
const invalidCustomer = {
|
|
name: "Max Mustermann",
|
|
address: "Musterstraße 1",
|
|
zip: "12345",
|
|
city: "Berlin",
|
|
};
|
|
|
|
const result = customerSchema.safeParse(invalidCustomer);
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("Tax Calculation Integration", () => {
|
|
it("should calculate invoice totals from items", async () => {
|
|
const { calcItemAmounts, calcInvoiceTotals } = await import("@/lib/tax");
|
|
|
|
const items = [
|
|
{
|
|
quantity: 10,
|
|
unitPrice: 100,
|
|
taxRate: 19,
|
|
},
|
|
{
|
|
quantity: 5,
|
|
unitPrice: 200,
|
|
taxRate: 7,
|
|
},
|
|
].map((item, idx) => ({
|
|
position: idx + 1,
|
|
description: `Service ${idx + 1}`,
|
|
...item,
|
|
...calcItemAmounts(item.quantity, item.unitPrice, item.taxRate),
|
|
}));
|
|
|
|
const totals = calcInvoiceTotals(items);
|
|
|
|
expect(totals.netTotal).toBe(2000); // 1000 + 1000
|
|
expect(totals.taxTotal).toBe(260); // 190 + 70 (5*200*7% = 70)
|
|
expect(totals.grossTotal).toBe(2260); // 1190 + 1070
|
|
});
|
|
|
|
it("should handle Kleinunternehmer calculation", async () => {
|
|
const { calcItemAmountsKleinunternehmer, calcInvoiceTotals } = await import("@/lib/tax");
|
|
|
|
const items = [
|
|
{
|
|
quantity: 10,
|
|
unitPrice: 100,
|
|
},
|
|
].map((item, idx) => ({
|
|
position: idx + 1,
|
|
description: "Service",
|
|
...item,
|
|
...calcItemAmountsKleinunternehmer(item.quantity, item.unitPrice),
|
|
taxRate: 0,
|
|
}));
|
|
|
|
const totals = calcInvoiceTotals(items);
|
|
|
|
expect(totals.netTotal).toBe(1000);
|
|
expect(totals.taxTotal).toBe(0);
|
|
expect(totals.grossTotal).toBe(1000);
|
|
});
|
|
});
|
|
|
|
describe("Authentication Logic", () => {
|
|
it("should identify unauthorized requests", () => {
|
|
const user = null;
|
|
const isAuthenticated = user !== null;
|
|
expect(isAuthenticated).toBe(false);
|
|
});
|
|
|
|
it("should identify authorized requests", () => {
|
|
const user = { id: "user-123", role: "ADMIN" };
|
|
const isAuthenticated = user !== null;
|
|
expect(isAuthenticated).toBe(true);
|
|
expect(user.role).toBe("ADMIN");
|
|
});
|
|
});
|
|
});
|