7, 'name' => 'Acme IT', 'status' => 'active', 'support_email' => 'support@example.test', 'internal_notes' => 'never disclose', 'password' => 'secret', 'credentials' => 'token', 'technical_ip' => '10.0.0.1', 'unknown_field' => 'not approved', ]; $mapper = new ReportDataMapper(); reporting_assert_same( ['id' => 7, 'name' => 'Acme IT', 'status' => 'active', 'support_email' => 'support@example.test'], $mapper->clientFacing($record), 'Client-facing report data must be allow-listed.' ); reporting_assert_same( ['internal_notes' => 'never disclose', 'password' => 'secret', 'credentials' => 'token', 'technical_ip' => '10.0.0.1', 'unknown_field' => 'not approved'], $mapper->internal($record), 'Internal report data must remain separate from client-facing data.' ); $hours = new HoursPerClientReport(); reporting_assert_same( [ ['client_id' => 7, 'client_name' => 'Acme IT', 'hours' => 3.0], ['client_id' => 2, 'client_name' => 'Beta', 'hours' => 3.5], ], $hours->aggregate([ ['client_id' => 7, 'client_name' => 'Acme IT', 'hours' => 1.25], ['client_id' => 2, 'client_name' => 'Beta', 'hours' => 3.5, 'internal_notes' => 'omit'], ['client_id' => 7, 'client_name' => 'Acme IT', 'hours' => 1.75, 'password' => 'omit'], ]), 'Hours must aggregate deterministically per client.' ); $sla = new SlaReport(); reporting_assert_same( [ ['client_id' => 7, 'client_name' => 'Acme IT', 'allocated_hours' => 10.0, 'used_hours' => 9.0, 'remaining_hours' => 1.0, 'usage_percentage' => 90.0, 'status' => 'critical'], ], $sla->rows([[ 'client_id' => 7, 'client_name' => 'Acme IT', 'allocated_hours' => 10, 'hours' => [4, 5, -2], 'internal_notes' => 'omit', 'credentials' => 'omit', ]]), 'SLA rows must expose only safe, deterministic report fields.' ); printf("Reporting contract tests: 3 passed\n");