import { describe, it, expect } from 'vitest' import { validateDevice, validateUpdateDevice, validateUser, validateUpdateUser, validatePoi, validateUpdatePoi, } from '../../server/utils/validation.js' describe('validation', () => { describe('validateDevice', () => { it('validates valid device data', () => { const result = validateDevice({ name: 'Test Device', device_type: 'traffic', lat: 40.7128, lng: -74.0060, stream_url: 'https://example.com/stream', source_type: 'mjpeg', }) expect(result.valid).toBe(true) expect(result.data).toBeDefined() expect(result.data.device_type).toBe('traffic') }) it('rejects invalid coordinates', () => { const result = validateDevice({ name: 'Test', lat: 'invalid', lng: -74.0060, }) expect(result.valid).toBe(false) expect(result.errors).toContain('lat and lng required as finite numbers') }) it('rejects non-object input', () => { const result = validateDevice(null) expect(result.valid).toBe(false) expect(result.errors).toContain('body required') }) it('defaults device_type to feed', () => { const result = validateDevice({ name: 'Test', lat: 40.7128, lng: -74.0060, }) expect(result.data.device_type).toBe('feed') }) }) describe('validateUpdateDevice', () => { it('validates partial updates', () => { const result = validateUpdateDevice({ name: 'Updated', lat: 40.7128 }) expect(result.valid).toBe(true) expect(result.data.name).toBe('Updated') expect(result.data.lat).toBe(40.7128) }) it('allows empty updates', () => { const result = validateUpdateDevice({}) expect(result.valid).toBe(true) expect(Object.keys(result.data).length).toBe(0) }) it('rejects invalid device_type', () => { const result = validateUpdateDevice({ device_type: 'invalid' }) expect(result.valid).toBe(false) expect(result.errors).toContain('Invalid device_type') }) it('handles device_type undefined', () => { const result = validateUpdateDevice({ name: 'Test' }) expect(result.valid).toBe(true) expect(result.data.device_type).toBeUndefined() }) it('handles vendor null', () => { const result = validateUpdateDevice({ vendor: null }) expect(result.valid).toBe(true) expect(result.data.vendor).toBeNull() }) it('handles vendor empty string', () => { const result = validateUpdateDevice({ vendor: '' }) expect(result.valid).toBe(true) expect(result.data.vendor).toBeNull() }) it('handles vendor string', () => { const result = validateUpdateDevice({ vendor: 'Test Vendor' }) expect(result.valid).toBe(true) expect(result.data.vendor).toBe('Test Vendor') }) }) describe('validateUser', () => { it('validates valid user data', () => { const result = validateUser({ identifier: 'testuser', password: 'password123', role: 'admin', }) expect(result.valid).toBe(true) expect(result.data.identifier).toBe('testuser') }) it('rejects missing identifier', () => { const result = validateUser({ password: 'password123', role: 'admin', }) expect(result.valid).toBe(false) expect(result.errors).toContain('identifier required') }) it('rejects invalid role', () => { const result = validateUser({ identifier: 'testuser', password: 'password123', role: 'invalid', }) expect(result.valid).toBe(false) expect(result.errors).toContain('role must be admin, leader, or member') }) }) describe('validateUpdateUser', () => { it('validates partial updates', () => { const result = validateUpdateUser({ role: 'leader' }) expect(result.valid).toBe(true) expect(result.data.role).toBe('leader') }) it('rejects empty identifier', () => { const result = validateUpdateUser({ identifier: '' }) expect(result.valid).toBe(false) expect(result.errors).toContain('identifier cannot be empty') }) }) describe('validatePoi', () => { it('validates valid POI data', () => { const result = validatePoi({ lat: 40.7128, lng: -74.0060, label: 'Test POI', iconType: 'flag', }) expect(result.valid).toBe(true) expect(result.data.lat).toBe(40.7128) }) it('rejects invalid coordinates', () => { const result = validatePoi({ lat: 'invalid', lng: -74.0060, }) expect(result.valid).toBe(false) expect(result.errors).toContain('lat and lng required as finite numbers') }) }) describe('validateUpdatePoi', () => { it('validates partial updates', () => { const result = validateUpdatePoi({ label: 'Updated', lat: 40.7128 }) expect(result.valid).toBe(true) expect(result.data.label).toBe('Updated') expect(result.data.lat).toBe(40.7128) }) it('rejects invalid iconType', () => { const result = validateUpdatePoi({ iconType: 'invalid' }) expect(result.valid).toBe(false) expect(result.errors).toContain('Invalid iconType') }) it('allows empty updates', () => { const result = validateUpdatePoi({}) expect(result.valid).toBe(true) expect(Object.keys(result.data).length).toBe(0) }) it('rejects invalid lat', () => { const result = validateUpdatePoi({ lat: 'invalid' }) expect(result.valid).toBe(false) expect(result.errors).toContain('lat must be a finite number') }) it('rejects invalid lng', () => { const result = validateUpdatePoi({ lng: 'invalid' }) expect(result.valid).toBe(false) expect(result.errors).toContain('lng must be a finite number') }) }) describe('validateUpdateDevice', () => { it('handles vendor null', () => { const result = validateUpdateDevice({ vendor: null }) expect(result.valid).toBe(true) expect(result.data.vendor).toBeNull() }) it('handles vendor empty string', () => { const result = validateUpdateDevice({ vendor: '' }) expect(result.valid).toBe(true) expect(result.data.vendor).toBeNull() }) it('handles config object', () => { const result = validateUpdateDevice({ config: { key: 'value' } }) expect(result.valid).toBe(true) expect(result.data.config).toBe('{"key":"value"}') }) it('handles config null', () => { const result = validateUpdateDevice({ config: null }) expect(result.valid).toBe(true) expect(result.data.config).toBeNull() }) it('handles config string', () => { const result = validateUpdateDevice({ config: '{"key":"value"}' }) expect(result.valid).toBe(true) expect(result.data.config).toBe('{"key":"value"}') }) }) describe('validateUpdateUser', () => { it('handles empty password', () => { const result = validateUpdateUser({ password: '' }) expect(result.valid).toBe(true) expect(result.data.password).toBeUndefined() }) it('handles undefined password', () => { const result = validateUpdateUser({ password: undefined }) expect(result.valid).toBe(true) expect(result.data.password).toBeUndefined() }) it('validates password when provided', () => { const result = validateUpdateUser({ password: 'newpassword' }) expect(result.valid).toBe(true) expect(result.data.password).toBe('newpassword') }) }) describe('validateDevice', () => { it('handles missing stream_url', () => { const result = validateDevice({ name: 'Test', lat: 40.7128, lng: -74.0060, }) expect(result.valid).toBe(true) expect(result.data.stream_url).toBe('') }) it('handles invalid source_type', () => { const result = validateDevice({ name: 'Test', lat: 40.7128, lng: -74.0060, source_type: 'invalid', }) expect(result.valid).toBe(true) expect(result.data.source_type).toBe('mjpeg') }) }) describe('validatePoi', () => { it('defaults iconType to pin', () => { const result = validatePoi({ lat: 40.7128, lng: -74.0060, }) expect(result.valid).toBe(true) expect(result.data.icon_type).toBe('pin') }) it('handles invalid iconType', () => { const result = validatePoi({ lat: 40.7128, lng: -74.0060, iconType: 'invalid', }) expect(result.valid).toBe(true) expect(result.data.icon_type).toBe('pin') }) it('validates valid POI with all fields', () => { const result = validatePoi({ lat: 40.7128, lng: -74.0060, label: 'Test POI', iconType: 'flag', }) expect(result.valid).toBe(true) expect(result.data.lat).toBe(40.7128) expect(result.data.lng).toBe(-74.0060) expect(result.data.label).toBe('Test POI') expect(result.data.icon_type).toBe('flag') }) }) describe('validateUpdateDevice', () => { it('handles all field types', () => { const result = validateUpdateDevice({ name: 'Test', device_type: 'traffic', vendor: 'Vendor', lat: 40.7128, lng: -74.0060, stream_url: 'https://example.com', source_type: 'hls', config: { key: 'value' }, }) expect(result.valid).toBe(true) expect(result.data.name).toBe('Test') expect(result.data.device_type).toBe('traffic') expect(result.data.vendor).toBe('Vendor') expect(result.data.lat).toBe(40.7128) expect(result.data.lng).toBe(-74.0060) expect(result.data.stream_url).toBe('https://example.com') expect(result.data.source_type).toBe('hls') expect(result.data.config).toBe('{"key":"value"}') }) }) describe('validateUpdatePoi', () => { it('handles all field types', () => { const result = validateUpdatePoi({ label: 'Updated', iconType: 'waypoint', lat: 41.7128, lng: -75.0060, }) expect(result.valid).toBe(true) expect(result.data.label).toBe('Updated') expect(result.data.icon_type).toBe('waypoint') expect(result.data.lat).toBe(41.7128) expect(result.data.lng).toBe(-75.0060) }) it('handles partial updates', () => { const result = validateUpdatePoi({ label: 'Test' }) expect(result.valid).toBe(true) expect(result.data.label).toBe('Test') }) }) describe('validateDevice edge cases', () => { it('handles vendor undefined', () => { const result = validateDevice({ name: 'Test', lat: 40.7128, lng: -74.0060, }) expect(result.valid).toBe(true) expect(result.data.vendor).toBeNull() }) it('handles config as object', () => { const result = validateDevice({ name: 'Test', lat: 40.7128, lng: -74.0060, config: { key: 'value' }, }) expect(result.valid).toBe(true) expect(result.data.config).toBe('{"key":"value"}') }) it('handles config as string', () => { const result = validateDevice({ name: 'Test', lat: 40.7128, lng: -74.0060, config: '{"key":"value"}', }) expect(result.valid).toBe(true) expect(result.data.config).toBe('{"key":"value"}') }) it('handles config null', () => { const result = validateDevice({ name: 'Test', lat: 40.7128, lng: -74.0060, config: null, }) expect(result.valid).toBe(true) expect(result.data.config).toBe(null) }) }) describe('validateUpdateDevice edge cases', () => { it('handles config null in updates', () => { const result = validateUpdateDevice({ config: null }) expect(result.valid).toBe(true) expect(result.data.config).toBeNull() }) it('handles config undefined in updates', () => { const result = validateUpdateDevice({ config: undefined }) expect(result.valid).toBe(true) expect(result.data.config).toBeUndefined() }) it('handles source_type undefined in updates', () => { const result = validateUpdateDevice({ name: 'Test' }) expect(result.valid).toBe(true) expect(result.data.source_type).toBeUndefined() }) it('handles lat undefined in updates', () => { const result = validateUpdateDevice({ name: 'Test' }) expect(result.valid).toBe(true) expect(result.data.lat).toBeUndefined() }) it('handles lng undefined in updates', () => { const result = validateUpdateDevice({ name: 'Test' }) expect(result.valid).toBe(true) expect(result.data.lng).toBeUndefined() }) it('handles stream_url undefined in updates', () => { const result = validateUpdateDevice({ name: 'Test' }) expect(result.valid).toBe(true) expect(result.data.stream_url).toBeUndefined() }) it('handles config undefined in updates', () => { const result = validateUpdateDevice({ name: 'Test' }) expect(result.valid).toBe(true) expect(result.data.config).toBeUndefined() }) }) describe('validateUpdateUser edge cases', () => { it('handles role undefined', () => { const result = validateUpdateUser({ identifier: 'test' }) expect(result.valid).toBe(true) expect(result.data.role).toBeUndefined() }) it('handles identifier undefined', () => { const result = validateUpdateUser({ role: 'admin' }) expect(result.valid).toBe(true) expect(result.data.identifier).toBeUndefined() }) it('handles password undefined', () => { const result = validateUpdateUser({ role: 'admin' }) expect(result.valid).toBe(true) expect(result.data.password).toBeUndefined() }) }) describe('validateUpdatePoi edge cases', () => { it('handles label undefined', () => { const result = validateUpdatePoi({ lat: 40.7128 }) expect(result.valid).toBe(true) expect(result.data.label).toBeUndefined() }) it('handles iconType undefined', () => { const result = validateUpdatePoi({ label: 'Test' }) expect(result.valid).toBe(true) expect(result.data.icon_type).toBeUndefined() }) it('handles lat undefined', () => { const result = validateUpdatePoi({ label: 'Test' }) expect(result.valid).toBe(true) expect(result.data.lat).toBeUndefined() }) it('handles lng undefined', () => { const result = validateUpdatePoi({ label: 'Test' }) expect(result.valid).toBe(true) expect(result.data.lng).toBeUndefined() }) }) })