489 lines
14 KiB
JavaScript
489 lines
14 KiB
JavaScript
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()
|
|
})
|
|
})
|
|
})
|