import { Filament } from '../src/types/filament'; import { Pool } from 'pg'; describe('Data Structure Consistency Tests', () => { const connectionString = "postgresql://filamenteka_admin:onrBjiAjHKQXBAJSVWU2t2kQ7HDil9re@filamenteka.ci7fsdlbzmag.eu-central-1.rds.amazonaws.com:5432/filamenteka"; // Admin panel expected structure (source of truth) const ADMIN_STRUCTURE = { fields: [ 'id', 'tip', 'finish', 'boja', 'bojaHex', // Frontend uses camelCase 'refill', 'vakum', 'otvoreno', 'kolicina', 'cena', 'createdAt', 'updatedAt' ], requiredFields: ['tip', 'finish', 'boja'], excludedFields: ['brand'], // Should NOT exist fieldTypes: { tip: 'string', finish: 'string', boja: 'string', bojaHex: 'string', refill: 'string', vakum: 'string', otvoreno: 'string', kolicina: 'string', cena: 'string' } }; // Database expected structure const DB_STRUCTURE = { columns: [ 'id', 'tip', 'finish', 'boja', 'boja_hex', // Database uses snake_case 'refill', 'vakum', 'otvoreno', 'kolicina', 'cena', 'created_at', 'updated_at' ], excludedColumns: ['brand'], columnTypes: { id: 'uuid', tip: 'character varying', finish: 'character varying', boja: 'character varying', boja_hex: 'character varying', refill: 'character varying', vakum: 'character varying', otvoreno: 'character varying', kolicina: 'integer', cena: 'character varying', created_at: 'timestamp with time zone', updated_at: 'timestamp with time zone' } }; describe('Admin Panel Structure', () => { it('should have correct TypeScript interface', () => { // Check Filament interface matches admin structure const filamentKeys: (keyof Filament)[] = [ 'id', 'tip', 'finish', 'boja', 'bojaHex', 'boja_hex', 'refill', 'vakum', 'otvoreno', 'kolicina', 'cena', 'status', 'createdAt', 'updatedAt' ]; // Should not have brand expect(filamentKeys).not.toContain('brand'); // Should have all required fields ADMIN_STRUCTURE.requiredFields.forEach(field => { expect(filamentKeys).toContain(field); }); }); it('should have consistent form fields in admin dashboard', () => { const formFields = [ 'tip', 'finish', 'boja', 'bojaHex', 'refill', 'vakum', 'otvoreno', 'kolicina', 'cena' ]; // Check all form fields are in admin structure formFields.forEach(field => { expect(ADMIN_STRUCTURE.fields).toContain(field); }); // Should not have brand in form expect(formFields).not.toContain('brand'); }); }); describe('Database Schema Consistency', () => { let pool: Pool; beforeAll(() => { pool = new Pool({ connectionString, ssl: { rejectUnauthorized: false } }); }); afterAll(async () => { await pool.end(); }); it('should have correct columns in database', async () => { const result = await pool.query(` SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'filaments' ORDER BY ordinal_position `); const columns = result.rows.map(row => row.column_name); const columnTypes = result.rows.reduce((acc, row) => { acc[row.column_name] = row.data_type; return acc; }, {} as Record); // Check all expected columns exist DB_STRUCTURE.columns.forEach(col => { expect(columns).toContain(col); }); // Check no excluded columns exist DB_STRUCTURE.excludedColumns.forEach(col => { expect(columns).not.toContain(col); }); // Check column types match Object.entries(DB_STRUCTURE.columnTypes).forEach(([col, type]) => { expect(columnTypes[col]).toBe(type); }); }); it('should not have brand column', async () => { const result = await pool.query(` SELECT COUNT(*) as count FROM information_schema.columns WHERE table_name = 'filaments' AND column_name = 'brand' `); expect(parseInt(result.rows[0].count)).toBe(0); }); }); describe('Frontend Consistency', () => { it('should transform fields correctly between admin and database', () => { // Admin uses camelCase, DB uses snake_case const transformations = { 'bojaHex': 'boja_hex', 'createdAt': 'created_at', 'updatedAt': 'updated_at' }; Object.entries(transformations).forEach(([adminField, dbField]) => { expect(ADMIN_STRUCTURE.fields).toContain(adminField); expect(DB_STRUCTURE.columns).toContain(dbField); }); }); it('should handle quantity fields correctly', () => { // Admin form shows refill, vakuum, otvoreno as numbers // Database stores them as strings like "2", "1 vakuum", "Ne" const quantityFields = ['refill', 'vakum', 'otvoreno']; quantityFields.forEach(field => { expect(ADMIN_STRUCTURE.fieldTypes[field]).toBe('string'); expect(DB_STRUCTURE.columnTypes[field]).toBe('character varying'); }); }); }); describe('API Consistency', () => { it('should handle requests without brand field', () => { const validRequest = { tip: 'PLA', finish: 'Basic', boja: 'Black', boja_hex: '#000000', refill: '2', vakum: '1 vakuum', otvoreno: 'Ne', kolicina: '3', cena: '3999' }; // Should not have brand expect(validRequest).not.toHaveProperty('brand'); // Should have all required fields expect(validRequest).toHaveProperty('tip'); expect(validRequest).toHaveProperty('finish'); expect(validRequest).toHaveProperty('boja'); }); }); describe('Data Flow Consistency', () => { it('should maintain consistent data flow: Admin → API → Database', () => { // Admin form data const adminData = { tip: 'PLA', finish: 'Basic', boja: 'Black', bojaHex: '#000000', refill: '2', vakum: '1 vakuum', otvoreno: 'Ne', kolicina: '3', cena: '3999' }; // API transformation (in services/api.ts) const apiData = { ...adminData, boja_hex: adminData.bojaHex }; delete (apiData as any).bojaHex; // Database expected data expect(apiData).toHaveProperty('boja_hex'); expect(apiData).not.toHaveProperty('bojaHex'); expect(apiData).not.toHaveProperty('brand'); }); }); });