Files
tilbudgivern/tests/api.test.js.disabled
alexpolo1 26d3452d59 Add comprehensive tests for API verification, frontend integration, and PDF generation
- Implemented a new test suite for production API verification using curl, ensuring all critical endpoints respond correctly and return valid JSON.
- Added frontend integration tests to check for critical errors in the UI and verify key UI elements are present and functional.
- Created test data for quotes and project calculations to facilitate testing.
- Developed a script to check offers in the Ordrestyring system, including detailed task descriptions.
- Added tests for generating PDFs from quote data, ensuring the output is valid and contains expected information.
- Implemented a test script for the Røsevangen project, integrating with the Ordrestyring API and verifying database entries.
2025-11-07 08:02:07 +00:00

192 lines
6.0 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const request = require('supertest');
const app = require('../unified-server.js');
describe('Tilbudgivern API Tests', () => {
describe('Material Search API', () => {
test('GET /api/pricing/materials - should return materials', async () => {
const response = await request(app)
.get('/api/pricing/materials?search=tagsten')
.expect('Content-Type', /json/)
.expect(200);
expect(response.body).toHaveProperty('success', true);
expect(response.body).toHaveProperty('materials');
expect(Array.isArray(response.body.materials)).toBe(true);
});
test('Material search - case insensitive', async () => {
const response = await request(app)
.get('/api/pricing/materials?search=TAGSTEN')
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.materials.length).toBeGreaterThan(0);
});
test('Material search - exact SKU match', async () => {
const response = await request(app)
.get('/api/pricing/materials?search=370100')
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.materials.length).toBeGreaterThan(0);
});
test('Material search - SQL injection protection', async () => {
const maliciousQuery = "'; DROP TABLE bygma_products; --";
const response = await request(app)
.get(`/api/pricing/materials?search=${encodeURIComponent(maliciousQuery)}`)
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.materials).toBeDefined();
});
});
describe('Labor API', () => {
test('POST /api/customer-projects/1/labor - valid data', async () => {
const laborData = {
carpenterCount: 2,
hoursPerDay: 8,
totalDays: 3,
hourlyRate: 450
};
const response = await request(app)
.post('/api/customer-projects/1/labor')
.send(laborData)
.expect('Content-Type', /json/)
.expect(200);
expect(response.body).toHaveProperty('success', true);
expect(response.body).toHaveProperty('laborId');
});
test('POST /api/customer-projects/1/labor - zero workers should fail', async () => {
const laborData = {
carpenterCount: 0,
hoursPerDay: 8,
totalDays: 3,
hourlyRate: 450
};
const response = await request(app)
.post('/api/customer-projects/1/labor')
.send(laborData)
.expect('Content-Type', /json/)
.expect(400);
expect(response.body).toHaveProperty('success', false);
expect(response.body.error).toContain('mindst 1 tømrer');
});
});
describe('Geometry API', () => {
test('POST /api/customer-projects/1/geometry - pitched roof with vindskede', async () => {
const geometryData = {
roofWidth: 10,
roofLength: 12,
roofHeight: 3,
roofType: 'skraat_tag',
notes: 'Test geometry with vindskede calculation'
};
const response = await request(app)
.post('/api/customer-projects/1/geometry')
.send(geometryData)
.expect('Content-Type', /json/)
.expect(200);
expect(response.body).toHaveProperty('success', true);
expect(response.body).toHaveProperty('geometryId');
expect(response.body).toHaveProperty('area');
expect(response.body).toHaveProperty('vindskede_lbm');
expect(response.body.vindskede_lbm).toBeGreaterThan(0);
});
test('Vindskede calculation accuracy', async () => {
const geometryData = {
roofWidth: 10,
roofLength: 12,
roofHeight: 3,
roofType: 'skraat_tag'
};
const response = await request(app)
.post('/api/customer-projects/1/geometry')
.send(geometryData)
.expect(200);
// Expected: 2 * sqrt((10/2)² + 3²) = 2 * sqrt(25 + 9) = 2 * sqrt(34) ≈ 11.66
expect(response.body.vindskede_lbm).toBeCloseTo(11.66, 1);
});
test('Flat roof should have zero vindskede', async () => {
const geometryData = {
roofWidth: 10,
roofLength: 12,
roofHeight: 0,
roofType: 'fladt_tag'
};
const response = await request(app)
.post('/api/customer-projects/1/geometry')
.send(geometryData)
.expect(200);
expect(response.body.vindskede_lbm).toBe(0);
});
});
describe('Price Logic Validation', () => {
test('Package price calculation: costPrice × (1 + profit%) = listPrice', async () => {
// Test the price logic through package endpoint
const response = await request(app)
.get('/api/packages')
.expect(200);
if (response.body.packages && response.body.packages.length > 0) {
const pkg = response.body.packages[0];
const expectedListPrice = pkg.cost_price * (1 + pkg.profit_margin / 100);
expect(pkg.list_price).toBeCloseTo(expectedListPrice, 2);
}
});
});
describe('Health Check', () => {
test('GET /health - should return OK', async () => {
const response = await request(app)
.get('/health')
.expect(200);
expect(response.body).toHaveProperty('status', 'OK');
expect(response.body).toHaveProperty('timestamp');
});
});
describe('Error Handling', () => {
test('Invalid project ID should return 404', async () => {
const response = await request(app)
.post('/api/customer-projects/99999/labor')
.send({
carpenterCount: 2,
hoursPerDay: 8,
totalDays: 3,
hourlyRate: 450
})
.expect(400);
expect(response.body.success).toBe(false);
});
test('Missing required fields should return 400', async () => {
const response = await request(app)
.post('/api/customer-projects/1/geometry')
.send({})
.expect(400);
expect(response.body.success).toBe(false);
});
});
});