path = tempnam(sys_get_temp_dir(), 'gis-test-'); $zip = new ZipArchive; $zip->open($this->path, ZipArchive::OVERWRITE); $zip->addFromString('xl/workbook.xml', ''); $zip->addFromString('xl/_rels/workbook.xml.rels', ''); $zip->addFromString('xl/worksheets/sheet1.xml', 'Private synthetic labelPrivate synthetic label1+12'); $zip->close(); } protected function tearDown(): void { unlink($this->path); parent::tearDown(); } /** @return array */ private function manifest(): array { return ['sha256' => hash_file('sha256', $this->path), 'owner' => 'Synthetic owner', 'version' => 'test-1', 'received_at' => '2026-09-28', 'classification' => 'internal', 'inspection_authorization' => 'Synthetic test only']; } public function test_audit_quarantines_unknown_crs_and_reports_duplicates_without_copying_source_values(): void { $report = (new CartographyWorkbookAudit)->inspect($this->path, $this->manifest()); $this->assertSame(3, $report['rows_inspected']); $this->assertSame('quarantined', $report['status']); $this->assertFalse($report['publication_allowed']); $this->assertSame([], $report['transformations']); $this->assertSame('duplicate', $report['sheets'][0]['rows'][1]['status']); $this->assertStringNotContainsString('Private synthetic label', json_encode($report)); $this->assertStringContainsString('formula_not_evaluated', json_encode($report)); $this->assertStringContainsString('original_crs_unknown', json_encode($report)); } public function test_checksum_mismatch_is_rejected(): void { $this->expectException(RuntimeException::class); $this->expectExceptionMessage('checksum'); (new CartographyWorkbookAudit)->inspect($this->path, [...$this->manifest(), 'sha256' => str_repeat('0', 64)]); } public function test_inspection_requires_authorization_evidence(): void { $this->expectException(RuntimeException::class); (new CartographyWorkbookAudit)->inspect($this->path, [...$this->manifest(), 'inspection_authorization' => '']); } public function test_xml_entities_are_rejected(): void { $zip = new ZipArchive; $zip->open($this->path); $zip->addFromString('xl/workbook.xml', ']>&x;'); $zip->close(); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('unsafe'); (new CartographyWorkbookAudit)->inspect($this->path, $this->manifest()); } }