142 lines
4.9 KiB
JavaScript
142 lines
4.9 KiB
JavaScript
|
|
/**
|
|||
|
|
* 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 }
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
});
|