// Seam tests for the Y/N ↔ boolean codec shared by services (write) and // views (read). Bug-fix coverage: `toApiBool('N')` must return 'N', even // though 'N' is a non-empty (truthy) string in JS — a naive // `value ? 'Y' : 'N'` returned 'Y' for that case and silently corrupted // every disabled_flag write. import { describe, it, expect } from 'vitest' import { toApiBool, fromApiBool } from '@/api/boolCodec.js' describe('fromApiBool', () => { it.each([ ['Y', true], ['N', false], [true, true], [false, false], [1, true], [0, false], [null, false], [undefined, false], ['', false], ['maybe', false], [{}, false], [[], false], ])('fromApiBool(%j) === %j', (input, expected) => { expect(fromApiBool(input)).toBe(expected) }) }) describe('toApiBool', () => { it.each([ [true, 'Y'], [false, 'N'], ['Y', 'Y'], ['N', 'N'], [1, 'Y'], [0, 'N'], [null, 'N'], [undefined, 'N'], ['', 'N'], ['maybe', 'N'], ])('toApiBool(%j) === %j', (input, expected) => { expect(toApiBool(input)).toBe(expected) }) it("correctly encodes 'N' as 'N' (catches the truthy-string trap)", () => { // Regression: a naive `value ? 'Y' : 'N'` would return 'Y' for the // non-empty string 'N' since non-empty strings are truthy. expect(toApiBool('N')).toBe('N') }) }) describe('round-trip', () => { it.each([ [true], [false], [null], [undefined], [''], ['Y'], ['N'], ['maybe'], [1], [0], ])('toApiBool stabilizes after one pass for %j', (input) => { const once = toApiBool(input) expect(['Y', 'N']).toContain(once) expect(toApiBool(once)).toBe(once) }) })