> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Test Data Strategy

> Version: 1.1.0 Status: 📋 Active Last Updated: 2025-12-11

**Version:** 1.1.0\
**Status:** 📋 Active\
**Last Updated:** 2025-12-11

This document defines the strategy for generating, managing, and cleaning up test data in the Encore Health OS Platform.

***

## Overview

This document establishes standards for test data generation, management, and cleanup across all platform tests. It is particularly important for features requiring multi-tenant isolation testing, such as PF-30 (Permissions System V2).

***

## Principles

### 1. Synthetic Data Only

* **NEVER** use real patient data (PHI)
* **NEVER** use real employee data (PII)
* **NEVER** use production data in tests
* Generate realistic but fake data using libraries like `@faker-js/faker`

### 2. Deterministic & Repeatable

* Tests should produce same results every run
* Use seeded random generators
* Document test data assumptions

### 3. Isolated & Clean

* Each test creates its own data
* Tests clean up after themselves
* No shared mutable state between tests
* Use transactions when possible for automatic rollback

### 4. Realistic & Representative

* Data should reflect real-world scenarios
* Include edge cases (empty values, maximum lengths, special characters)
* Model actual business workflows

***

## Planned Sections

### Test Data Factory

**Location:** `/tests/utils/test-data-factory.ts`

**Responsibilities:**

* Create test organizations
* Create test users with roles
* Create test employees with credentials
* Create test forms and submissions
* Create test shifts and timesheets
* Generate realistic fake data

**Example Usage:**

```typescript theme={null}
import { 
  createTestOrganization,
  createTestUser,
  createTestEmployee,
  createTestForm
} from '@/tests/utils/test-data-factory';

// In test
const org = await createTestOrganization({
  name: 'Test Healthcare Org'
});

const admin = await createTestUser({
  organization_id: org.id,
  role: 'org_admin'
});

const employee = await createTestEmployee({
  organization_id: org.id,
  department_id: dept.id
});
```

### Seed Data Management

**Local Development Seeds:**

* Sample organizations
* Sample users for each role
* Sample forms and templates
* Sample employees and credentials

**Test Seeds:**

* Minimal data for fast tests
* Comprehensive data for integration tests
* Edge case data for boundary tests

### Data Cleanup Strategies

**Per-Test Cleanup:**

```typescript theme={null}
afterEach(async () => {
  // Delete test data created in this test
  await cleanupTestData(testDataIds);
});
```

**Transaction-Based Cleanup:**

```typescript theme={null}
// Use database transactions for automatic rollback
const { data, error } = await supabase.rpc('begin_test_transaction');
// ... test operations ...
await supabase.rpc('rollback_test_transaction');
```

**Cascade Deletes:**

* Configure foreign keys with CASCADE
* Delete parent records to clean up children

### Tenant Isolation Testing

**Multi-Tenant Test Setup:**

```typescript theme={null}
// Create two isolated organizations
const org1 = await createTestOrganization();
const org2 = await createTestOrganization();

const user1 = await createTestUser({ organization_id: org1.id });
const user2 = await createTestUser({ organization_id: org2.id });

// Verify user1 cannot see org2 data
const { data } = await user1Client.from('employees').select('*');
expect(data.every(emp => emp.organization_id === org1.id)).toBe(true);
```

### Performance Test Data

**Volume Testing:**

* Generate 1000+ records
* Measure query performance
* Test pagination
* Test search functionality

**Realistic Distribution:**

* Model real-world data skew
* Include inactive records
* Include historical data

***

## Data Generation Patterns

### Employee Records

```typescript theme={null}
// Planned implementation
function generateTestEmployee(overrides = {}) {
  return {
    employee_number: faker.string.alphanumeric(6),
    first_name: faker.person.firstName(),
    last_name: faker.person.lastName(),
    email: faker.internet.email(),
    hire_date: faker.date.past(),
    job_title: faker.person.jobTitle(),
    ...overrides
  };
}
```

### Form Submissions

```typescript theme={null}
// Planned implementation
function generateTestSubmission(formId, overrides = {}) {
  return {
    form_id: formId,
    submission_data: {
      // Generate data matching form fields
    },
    submitted_by: faker.string.uuid(),
    ...overrides
  };
}
```

### Credentials

```typescript theme={null}
// Planned implementation
function generateTestCredential(employeeId, credentialTypeId) {
  return {
    employee_id: employeeId,
    credential_type_id: credentialTypeId,
    issue_date: faker.date.past(),
    expiration_date: faker.date.future(),
    credential_number: faker.string.alphanumeric(10)
  };
}
```

***

## Compliance & Privacy

### PHI/PII Guidelines

* ❌ Never use real names from production
* ❌ Never use real SSNs, DoBs, or medical record numbers
* ❌ Never use real addresses or contact information
* ✅ Use faker-generated synthetic data
* ✅ Use obvious fake values ([test@example.com](mailto:test@example.com))
* ✅ Document that test data is synthetic

### Data Retention

* Test data in CI: Delete after test run
* Local test data: Clean up regularly
* Seed data: Version controlled, clearly marked as fake

***

## Test Environment Setup

### Local Development

```bash theme={null}
# Planned scripts
npm run test:seed        # Seed local database
npm run test:clean       # Clean test data
npm run test:reset       # Reset to clean state
```

### CI/CD

* Ephemeral test databases
* Automatic cleanup after test run
* Isolated per-PR environments

***

## Database Fixtures

### Fixture Files

**Location:** `/tests/fixtures/`

**Structure:**

```
/tests/fixtures/
  ├── organizations.json
  ├── users.json
  ├── employees.json
  ├── forms.json
  └── credentials.json
```

### Loading Fixtures

```typescript theme={null}
// Planned implementation
import { loadFixture } from '@/tests/utils/fixture-loader';

const org = await loadFixture('organizations', 'default-org');
const admin = await loadFixture('users', 'org-admin');
```

***

## Snapshot Testing

### When to Use

* Form field configurations
* Automation rule schemas
* Report query results
* API response formats

### Snapshot Updates

* Review snapshot changes in PRs
* Update snapshots intentionally
* Document breaking changes

***

## PF-30 Permissions System Test Data

### Multi-Tenant Permission Testing

The PF-30 Permissions System V2 requires comprehensive test data for validating:

* Cross-organization isolation (users cannot see other org's roles/permissions)
* Role-based access control (org\_admin-only operations)
* Permission inheritance (custom roles inheriting from base system roles)
* Site-scoped role assignments
* Expiration-based access control

### Test Organization Setup

```typescript theme={null}
// Standard test organization structure for PF-30 tests
const testSetup = {
  // Primary test organization
  org_a: {
    id: 'org-a-uuid',
    name: 'Test Organization A',
    permissions_v2_enabled: true // Historical - V2 is mandatory, column exists for compatibility
  },
  
  // Secondary organization for isolation testing
  org_b: {
    id: 'org-b-uuid', 
    name: 'Test Organization B',
    permissions_v2_enabled: true
  },
  
  // Sites within org_a
  sites: {
    site_a_org_a: { id: 'site-a-uuid', organization_id: 'org-a-uuid' },
    site_b_org_a: { id: 'site-b-uuid', organization_id: 'org-a-uuid' }
  }
};
```

### Test User Personas

| User ID            | Role                  | Organization | Purpose                           |
| ------------------ | --------------------- | ------------ | --------------------------------- |
| `admin_org_a`      | org\_admin            | org\_a       | Can create/manage custom roles    |
| `staff_org_a`      | staff                 | org\_a       | Regular user, cannot manage roles |
| `site_admin_org_a` | site\_admin (site\_a) | org\_a       | Site-scoped permissions test      |
| `user_org_b`       | staff                 | org\_b       | Cross-org isolation testing       |

### Custom Role Test Data

```typescript theme={null}
// Sample custom roles for testing
const testRoles = [
  {
    name: 'HR Recruiter',
    organization_id: 'org-a-uuid',
    base_role: 'staff',
    description: 'Can manage candidates and job postings'
  },
  {
    name: 'AP Clerk',
    organization_id: 'org-a-uuid',
    base_role: 'finance_staff',
    description: 'Can process bills and payments'
  }
];
```

### Permission Assignment Test Data

```typescript theme={null}
// Sample permission assignments for testing
const testPermissions = [
  // HR Recruiter permissions
  { role: 'HR Recruiter', permissions: ['hr.candidates.view', 'hr.candidates.create', 'hr.applications.view'] },
  // AP Clerk permissions
  { role: 'AP Clerk', permissions: ['fa.bills.view', 'fa.bills.create', 'fa.payments.view'] }
];
```

### Site-Scoped Assignment Test Data

```typescript theme={null}
// Site-scoped role assignments for testing
const siteScopes = [
  {
    user_id: 'user-1-uuid',
    role_id: 'hr-recruiter-role-uuid',
    site_ids: ['site-a-uuid'], // Limited to site A only
    expires_at: null
  },
  {
    user_id: 'user-2-uuid',
    role_id: 'hr-recruiter-role-uuid',
    site_ids: null, // All sites (org-wide)
    expires_at: '2025-12-31T23:59:59Z' // Temporary access
  }
];
```

### RLS Test Scenarios

| Scenario                  | Actor                  | Action                         | Expected Result          |
| ------------------------- | ---------------------- | ------------------------------ | ------------------------ |
| Cross-org isolation       | user\_org\_b           | View org\_a custom roles       | Empty result (no access) |
| Role creation             | admin\_org\_a          | Create custom role             | Success                  |
| Role creation             | staff\_org\_a          | Create custom role             | Permission denied        |
| Permission assignment     | admin\_org\_a          | Assign permission to role      | Success                  |
| Self-privilege escalation | staff\_org\_a          | Assign self higher permissions | Permission denied        |
| Site-scoped access        | site\_admin\_org\_a    | Access site\_b data            | Permission denied        |
| Expired assignment        | user with expired role | Access protected resource      | Permission denied        |

### Cleanup Strategy for PF-30 Tests

```typescript theme={null}
// Cleanup order (respects FK constraints)
const cleanupOrder = [
  'pf_user_role_assignments',  // Delete assignments first
  'pf_role_permissions',       // Then permission mappings
  'pf_custom_roles',           // Then custom roles
  // pf_module_permissions is system-wide, rarely needs cleanup
];

async function cleanupPF30TestData(testOrgId: string) {
  for (const table of cleanupOrder) {
    await supabase.from(table)
      .delete()
      .eq('organization_id', testOrgId);
  }
}
```

***

## References

* `/tests/utils/test-data-factory.ts` - Data factory implementation
* `/tests/utils/supabase-test-client.ts` - Test client utilities
* `/docs/testing/index.md` - Testing overview
* `/constitution.md` - Security and privacy requirements (Section 5.7)
* `/specs/pf/PF-30-permissions-system-v2.md` - Permissions System V2 specification
* `/specs/pf/PF-30-permissions-system-v2-PLAN.md` - Implementation plan with test scenarios

***

**Maintained by:** Platform QA Team\
**Questions?** Raise in #engineering Slack
