/** * Property-Based Tests: FP Submissions Cleanup * * Feature: fp-submissions-cleanup * * Tests the pure filtering functions used to determine which FP submissions * are visible in the Queue Panel and which show the dismiss button. * * Validates: Requirements 1.1, 2.1, 2.2, 2.3 */ const fc = require('fast-check'); // Mock db pool before importing the route module (avoids DATABASE_URL requirement) jest.mock('../db', () => ({ query: jest.fn(() => Promise.resolve({ rows: [], rowCount: 0 })), })); // Mock dependencies that the route module imports jest.mock('../helpers/auditLog', () => jest.fn()); jest.mock('../helpers/ivantiApi', () => ({ ivantiFormPost: jest.fn(), ivantiPost: jest.fn(), })); const { filterVisibleSubmissions, shouldShowDismissButton } = require('../routes/ivantiFpWorkflow'); // --- Generators --- const lifecycleStatusArb = fc.constantFrom('submitted', 'approved', 'rejected', 'rework', 'resubmitted'); const dismissedAtArb = fc.oneof( fc.constant(null), fc.date({ min: new Date('2020-01-01'), max: new Date('2030-12-31') }).map(d => d.toISOString()) ); const submissionArb = fc.record({ id: fc.integer({ min: 1, max: 100000 }), lifecycle_status: lifecycleStatusArb, dismissed_at: dismissedAtArb, user_id: fc.integer({ min: 1, max: 1000 }), ivanti_workflow_batch_id: fc.string({ minLength: 1, maxLength: 20 }) }); const submissionsArrayArb = fc.array(submissionArb, { minLength: 0, maxLength: 50 }); // --- Property 1: Submission Visibility Filter --- describe('Feature: fp-submissions-cleanup, Property 1: Submission Visibility Filter', () => { /** * For any array of FP submission objects with arbitrary lifecycle_status values * and arbitrary dismissed_at values, filterVisibleSubmissions(submissions) should * return only submissions where lifecycle_status is NOT "approved" AND dismissed_at * is null. Additionally, every submission in the input that satisfies both conditions * must appear in the output, and the output length must be <= input length. * * Validates: Requirements 1.1, 2.2, 2.3 */ it('returns only non-approved and non-dismissed submissions', () => { fc.assert( fc.property(submissionsArrayArb, (submissions) => { const result = filterVisibleSubmissions(submissions); // Output length must be <= input length expect(result.length).toBeLessThanOrEqual(submissions.length); // Every item in the result must be non-approved and non-dismissed for (const s of result) { expect(s.lifecycle_status).not.toBe('approved'); expect(s.dismissed_at).toBeNull(); } // Every input item that satisfies both conditions must appear in the output const expected = submissions.filter( s => s.lifecycle_status !== 'approved' && s.dismissed_at == null ); expect(result).toEqual(expected); }), { numRuns: 100 } ); }); }); // --- Property 2: Dismiss Button Visibility Predicate --- describe('Feature: fp-submissions-cleanup, Property 2: Dismiss Button Visibility Predicate', () => { /** * For any FP submission object with a lifecycle_status value drawn from * {submitted, approved, rejected, rework, resubmitted} and a dismissed_at value * (null or timestamp), the dismiss button should be rendered if and only if * lifecycle_status === 'rejected' AND dismissed_at is null. * * Validates: Requirements 2.1 */ it('returns true iff status is rejected and dismissed_at is null', () => { fc.assert( fc.property(submissionArb, (submission) => { const result = shouldShowDismissButton(submission); const expected = submission.lifecycle_status === 'rejected' && submission.dismissed_at == null; expect(result).toBe(expected); }), { numRuns: 100 } ); }); });