/** * Property-Based Tests: Ivanti Queue Remediation — Vendor Validation * * Feature: ivanti-queue-remediation * Property 1: Remediate vendor validation * * For any non-empty string of 1–200 characters (trimmed, with at least one * non-whitespace character), submitting it as the vendor field with workflow_type * "Remediate" to the queue API SHALL be accepted; and for any empty, * whitespace-only, or >200 character vendor string, the request SHALL be * rejected with a 400 status. * * **Validates: Requirements 1.2, 1.3** */ const fc = require('fast-check'); // --------------------------------------------------------------------------- // Replicate the pure validation logic from ivantiTodoQueue.js // --------------------------------------------------------------------------- const VALID_WORKFLOW_TYPES = ['FP', 'Archer', 'CARD', 'GRANITE', 'DECOM', 'Remediate']; const INVENTORY_TYPES = ['CARD', 'GRANITE', 'DECOM']; function isValidVendor(vendor) { if (typeof vendor !== 'string') return false; const trimmed = vendor.trim(); return trimmed.length > 0 && trimmed.length <= 200; } /** * Simulates the validation logic for the batch/single add endpoints. * Returns { accepted: boolean, status: number } mirroring the route behavior. */ function validateRemediateRequest(vendor) { const workflow_type = 'Remediate'; if (!VALID_WORKFLOW_TYPES.includes(workflow_type)) { return { accepted: false, status: 400 }; } // Remediate is NOT in INVENTORY_TYPES, so vendor is required if (!INVENTORY_TYPES.includes(workflow_type)) { if (!isValidVendor(vendor)) { return { accepted: false, status: 400 }; } } return { accepted: true, status: 201 }; } // --------------------------------------------------------------------------- // Arbitraries // --------------------------------------------------------------------------- // Valid vendor: 1–200 chars trimmed, at least one non-whitespace char const arbValidVendor = fc.string({ minLength: 1, maxLength: 200 }).filter(s => { const trimmed = s.trim(); return trimmed.length > 0 && trimmed.length <= 200; }); // Invalid vendor: empty string const arbEmptyVendor = fc.constant(''); // Invalid vendor: whitespace-only strings const arbWhitespaceOnlyVendor = fc.array( fc.constantFrom(' ', '\t', '\n', '\r'), { minLength: 1, maxLength: 50 } ).map(arr => arr.join('')); // Invalid vendor: strings > 200 chars when trimmed const arbOverlengthVendor = fc.string({ minLength: 201, maxLength: 400 }).filter(s => { return s.trim().length > 200; }); // --------------------------------------------------------------------------- // Property 1: Remediate vendor validation // --------------------------------------------------------------------------- describe('Feature: ivanti-queue-remediation, Property 1: Remediate vendor validation', () => { it('accepts any non-empty vendor string of 1–200 trimmed characters with at least one non-whitespace', () => { fc.assert( fc.property(arbValidVendor, (vendor) => { const result = validateRemediateRequest(vendor); expect(result.accepted).toBe(true); expect(result.status).toBe(201); }), { numRuns: 100 } ); }); it('rejects empty string as vendor for Remediate workflow', () => { fc.assert( fc.property(arbEmptyVendor, (vendor) => { const result = validateRemediateRequest(vendor); expect(result.accepted).toBe(false); expect(result.status).toBe(400); }), { numRuns: 10 } ); }); it('rejects whitespace-only strings as vendor for Remediate workflow', () => { fc.assert( fc.property(arbWhitespaceOnlyVendor, (vendor) => { const result = validateRemediateRequest(vendor); expect(result.accepted).toBe(false); expect(result.status).toBe(400); }), { numRuns: 100 } ); }); it('rejects vendor strings exceeding 200 characters when trimmed', () => { fc.assert( fc.property(arbOverlengthVendor, (vendor) => { const result = validateRemediateRequest(vendor); expect(result.accepted).toBe(false); expect(result.status).toBe(400); }), { numRuns: 100 } ); }); it('rejects non-string vendor values (undefined, null, number)', () => { const arbNonString = fc.oneof( fc.constant(undefined), fc.constant(null), fc.integer(), fc.boolean() ); fc.assert( fc.property(arbNonString, (vendor) => { const result = validateRemediateRequest(vendor); expect(result.accepted).toBe(false); expect(result.status).toBe(400); }), { numRuns: 100 } ); }); });