Some checks failed
Build and Push Docker Images (Trusted) / build-and-push (., docker/caddy/Dockerfile, caddy) (push) Has been cancelled
Build and Push Docker Images (Trusted) / build-and-push (., docker/l4-port-manager/Dockerfile, l4-port-manager) (push) Has been cancelled
Build and Push Docker Images (Trusted) / build-and-push (., docker/web/Dockerfile, web) (push) Has been cancelled
Tests / test (push) Has been cancelled
285 lines
7.5 KiB
TypeScript
Executable File
285 lines
7.5 KiB
TypeScript
Executable File
/**
|
|
* Unit tests for src/lib/form-parse.ts
|
|
* Tests all pure FormData parsing helpers.
|
|
*/
|
|
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
parseCsv,
|
|
parseUpstreams,
|
|
parseCheckbox,
|
|
parseOptionalText,
|
|
parseCertificateId,
|
|
parseAccessListId,
|
|
parseOptionalNumber,
|
|
} from '@/src/lib/form-parse';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// parseCsv
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('parseCsv', () => {
|
|
it('splits by comma', () => {
|
|
expect(parseCsv('a,b,c')).toEqual(['a', 'b', 'c']);
|
|
});
|
|
|
|
it('splits by newline (newlines converted to commas)', () => {
|
|
expect(parseCsv('a\nb\nc')).toEqual(['a', 'b', 'c']);
|
|
});
|
|
|
|
it('trims whitespace from each item', () => {
|
|
expect(parseCsv(' a , b ')).toEqual(['a', 'b']);
|
|
});
|
|
|
|
it('filters empty items after split', () => {
|
|
expect(parseCsv('a,,b,')).toEqual(['a', 'b']);
|
|
});
|
|
|
|
it('returns empty array for null', () => {
|
|
expect(parseCsv(null)).toEqual([]);
|
|
});
|
|
|
|
it('returns empty array for empty string', () => {
|
|
expect(parseCsv('')).toEqual([]);
|
|
});
|
|
|
|
it('handles single item without delimiter', () => {
|
|
expect(parseCsv('example.com')).toEqual(['example.com']);
|
|
});
|
|
|
|
it('handles mixed comma and newline delimiters', () => {
|
|
expect(parseCsv('a,b\nc,d')).toEqual(['a', 'b', 'c', 'd']);
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// parseUpstreams
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('parseUpstreams', () => {
|
|
it('splits by newline', () => {
|
|
expect(parseUpstreams('http://a\nhttp://b')).toEqual(['http://a', 'http://b']);
|
|
});
|
|
|
|
it('does NOT split on commas (URLs may contain commas in query strings)', () => {
|
|
const url = 'http://example.com/path?a=1,b=2';
|
|
expect(parseUpstreams(url)).toEqual([url]);
|
|
});
|
|
|
|
it('trims whitespace from each line', () => {
|
|
expect(parseUpstreams(' backend:8080 \n backend2:9090 ')).toEqual([
|
|
'backend:8080',
|
|
'backend2:9090',
|
|
]);
|
|
});
|
|
|
|
it('filters empty lines', () => {
|
|
expect(parseUpstreams('a\n\nb\n')).toEqual(['a', 'b']);
|
|
});
|
|
|
|
it('returns empty array for null', () => {
|
|
expect(parseUpstreams(null)).toEqual([]);
|
|
});
|
|
|
|
it('returns empty array for empty string', () => {
|
|
expect(parseUpstreams('')).toEqual([]);
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// parseCheckbox
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('parseCheckbox', () => {
|
|
it('"on" → true', () => {
|
|
expect(parseCheckbox('on')).toBe(true);
|
|
});
|
|
|
|
it('"true" → true', () => {
|
|
expect(parseCheckbox('true')).toBe(true);
|
|
});
|
|
|
|
it('"1" → true', () => {
|
|
expect(parseCheckbox('1')).toBe(true);
|
|
});
|
|
|
|
it('null → false', () => {
|
|
expect(parseCheckbox(null)).toBe(false);
|
|
});
|
|
|
|
it('"off" → false', () => {
|
|
expect(parseCheckbox('off')).toBe(false);
|
|
});
|
|
|
|
it('"false" → false', () => {
|
|
expect(parseCheckbox('false')).toBe(false);
|
|
});
|
|
|
|
it('"0" → false', () => {
|
|
expect(parseCheckbox('0')).toBe(false);
|
|
});
|
|
|
|
it('empty string → false', () => {
|
|
expect(parseCheckbox('')).toBe(false);
|
|
});
|
|
|
|
it('arbitrary string → false', () => {
|
|
expect(parseCheckbox('yes')).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// parseOptionalText
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('parseOptionalText', () => {
|
|
it('returns trimmed string for non-empty input', () => {
|
|
expect(parseOptionalText(' hello ')).toBe('hello');
|
|
});
|
|
|
|
it('returns the exact string when already trimmed', () => {
|
|
expect(parseOptionalText('hello')).toBe('hello');
|
|
});
|
|
|
|
it('returns null for null', () => {
|
|
expect(parseOptionalText(null)).toBeNull();
|
|
});
|
|
|
|
it('returns null for empty string', () => {
|
|
expect(parseOptionalText('')).toBeNull();
|
|
});
|
|
|
|
it('returns null for whitespace-only string', () => {
|
|
expect(parseOptionalText(' ')).toBeNull();
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// parseCertificateId
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('parseCertificateId', () => {
|
|
it('parses a valid positive integer', () => {
|
|
expect(parseCertificateId('42')).toBe(42);
|
|
});
|
|
|
|
it('parses "1" as 1', () => {
|
|
expect(parseCertificateId('1')).toBe(1);
|
|
});
|
|
|
|
it('returns null for null', () => {
|
|
expect(parseCertificateId(null)).toBeNull();
|
|
});
|
|
|
|
it('returns null for empty string', () => {
|
|
expect(parseCertificateId('')).toBeNull();
|
|
});
|
|
|
|
it('returns null for "0" (must be > 0)', () => {
|
|
expect(parseCertificateId('0')).toBeNull();
|
|
});
|
|
|
|
it('returns null for negative numbers', () => {
|
|
expect(parseCertificateId('-1')).toBeNull();
|
|
});
|
|
|
|
it('returns null for decimal values', () => {
|
|
expect(parseCertificateId('1.5')).toBeNull();
|
|
});
|
|
|
|
it('returns null for NaN', () => {
|
|
expect(parseCertificateId('NaN')).toBeNull();
|
|
});
|
|
|
|
it('returns null for Infinity', () => {
|
|
expect(parseCertificateId('Infinity')).toBeNull();
|
|
});
|
|
|
|
it('returns null for the literal "null"', () => {
|
|
expect(parseCertificateId('null')).toBeNull();
|
|
});
|
|
|
|
it('returns null for the literal "undefined"', () => {
|
|
expect(parseCertificateId('undefined')).toBeNull();
|
|
});
|
|
|
|
it('returns null for non-numeric text', () => {
|
|
expect(parseCertificateId('abc')).toBeNull();
|
|
});
|
|
|
|
it('handles leading/trailing whitespace', () => {
|
|
expect(parseCertificateId(' 5 ')).toBe(5);
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// parseAccessListId — identical rules to parseCertificateId
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('parseAccessListId', () => {
|
|
it('parses a valid positive integer', () => {
|
|
expect(parseAccessListId('7')).toBe(7);
|
|
});
|
|
|
|
it('returns null for null', () => {
|
|
expect(parseAccessListId(null)).toBeNull();
|
|
});
|
|
|
|
it('returns null for "0"', () => {
|
|
expect(parseAccessListId('0')).toBeNull();
|
|
});
|
|
|
|
it('returns null for float', () => {
|
|
expect(parseAccessListId('2.5')).toBeNull();
|
|
});
|
|
|
|
it('returns null for NaN', () => {
|
|
expect(parseAccessListId('NaN')).toBeNull();
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// parseOptionalNumber
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('parseOptionalNumber', () => {
|
|
it('parses integer', () => {
|
|
expect(parseOptionalNumber('42')).toBe(42);
|
|
});
|
|
|
|
it('parses float', () => {
|
|
expect(parseOptionalNumber('3.14')).toBe(3.14);
|
|
});
|
|
|
|
it('parses negative number', () => {
|
|
expect(parseOptionalNumber('-5')).toBe(-5);
|
|
});
|
|
|
|
it('parses zero', () => {
|
|
expect(parseOptionalNumber('0')).toBe(0);
|
|
});
|
|
|
|
it('returns null for null', () => {
|
|
expect(parseOptionalNumber(null)).toBeNull();
|
|
});
|
|
|
|
it('returns null for empty string', () => {
|
|
expect(parseOptionalNumber('')).toBeNull();
|
|
});
|
|
|
|
it('returns null for whitespace-only', () => {
|
|
expect(parseOptionalNumber(' ')).toBeNull();
|
|
});
|
|
|
|
it('returns null for NaN text', () => {
|
|
expect(parseOptionalNumber('NaN')).toBeNull();
|
|
});
|
|
|
|
it('returns null for Infinity', () => {
|
|
expect(parseOptionalNumber('Infinity')).toBeNull();
|
|
});
|
|
|
|
it('returns null for non-numeric text', () => {
|
|
expect(parseOptionalNumber('abc')).toBeNull();
|
|
});
|
|
});
|