/** * Property-Based Test: Empty Section Omission * * Feature: queue-collapsible-sections, Property 3: Empty Section Omission * **Validates: Requirements 1.6, 1.7** * * For any array of queue items (including empty arrays), the grouped output * contains no sections with zero items. If no CARD/GRANITE/DECOM items exist * in the input, no section with type 'inventory' appears in the output. */ import fc from 'fast-check'; // Inline the grouping logic since the utility file may not exist yet function groupQueueItems(visibleItems) { const INVENTORY_TYPES = new Set(['CARD', 'GRANITE', 'DECOM']); const inventoryItems = []; const vendorMap = new Map(); for (const item of visibleItems) { if (INVENTORY_TYPES.has(item.workflow_type)) { inventoryItems.push(item); } else { const vendor = item.vendor?.trim() || 'Unknown'; if (!vendorMap.has(vendor)) vendorMap.set(vendor, []); vendorMap.get(vendor).push(item); } } const sections = []; if (inventoryItems.length > 0) { sections.push({ key: 'inventory', label: 'Inventory', type: 'inventory', items: inventoryItems }); } const sortedVendors = [...vendorMap.entries()].sort((a, b) => a[0].localeCompare(b[0])); for (const [vendor, items] of sortedVendors) { sections.push({ key: `vendor:${vendor}`, label: vendor, type: 'vendor', items }); } return sections; } // Generator for queue items with various workflow types and vendors const workflowTypeArb = fc.constantFrom('CARD', 'GRANITE', 'DECOM', 'FP', 'Archer'); const vendorArb = fc.oneof( fc.constant(null), fc.constant(undefined), fc.constant(''), fc.constant(' '), fc.stringMatching(/^[A-Za-z][A-Za-z0-9 ]{0,14}$/) ); const queueItemArb = fc.record({ id: fc.integer({ min: 1, max: 100000 }), workflow_type: workflowTypeArb, vendor: vendorArb, hostname: fc.stringMatching(/^[a-z]{3,10}$/), status: fc.constant('pending'), }); const queueItemsArb = fc.array(queueItemArb, { minLength: 0, maxLength: 50 }); describe('Queue Grouping — Property 3: Empty Section Omission', () => { it('no section in the output has zero items', () => { fc.assert( fc.property(queueItemsArb, (items) => { const sections = groupQueueItems(items); for (const section of sections) { expect(section.items.length).toBeGreaterThan(0); } }), { numRuns: 200 } ); }); it('if no inventory-type items exist, no inventory section appears', () => { // Generate arrays that explicitly exclude inventory types const nonInventoryTypeArb = fc.constantFrom('FP', 'Archer'); const nonInventoryItemArb = fc.record({ id: fc.integer({ min: 1, max: 100000 }), workflow_type: nonInventoryTypeArb, vendor: vendorArb, hostname: fc.stringMatching(/^[a-z]{3,10}$/), status: fc.constant('pending'), }); const nonInventoryItemsArb = fc.array(nonInventoryItemArb, { minLength: 0, maxLength: 50 }); fc.assert( fc.property(nonInventoryItemsArb, (items) => { const sections = groupQueueItems(items); const inventorySection = sections.find(s => s.type === 'inventory'); expect(inventorySection).toBeUndefined(); }), { numRuns: 200 } ); }); it('if no CARD/GRANITE/DECOM items exist in a mixed array, no inventory section appears', () => { fc.assert( fc.property(queueItemsArb, (items) => { const INVENTORY_TYPES = new Set(['CARD', 'GRANITE', 'DECOM']); const hasInventoryItems = items.some(item => INVENTORY_TYPES.has(item.workflow_type)); const sections = groupQueueItems(items); const inventorySection = sections.find(s => s.type === 'inventory'); if (!hasInventoryItems) { expect(inventorySection).toBeUndefined(); } else { expect(inventorySection).toBeDefined(); expect(inventorySection.items.length).toBeGreaterThan(0); } }), { numRuns: 200 } ); }); });