tempDir = sys_get_temp_dir() . '/integrity_test_' . uniqid(); mkdir($this->tempDir, 0755, true); } protected function tearDown(): void { $this->removeDirectory($this->tempDir); } #[Test] public function itPassesForUnmodifiedFiles(): void { $this->createFile('test.php', 'tempDir); $hashes = $checker->generateHashes(['test.php']); $verifier = new IntegrityChecker($hashes, $this->tempDir); $verifier->verify(); self::assertTrue($verifier->isValid()); } #[Test] public function itFailsForModifiedFiles(): void { $this->createFile('test.php', 'tempDir); $hashes = $checker->generateHashes(['test.php']); // Modify the file $this->createFile('test.php', 'tempDir); $this->expectException(IntegrityException::class); $this->expectExceptionMessage('Modified file: test.php'); $verifier->verify(); } #[Test] public function itFailsForMissingFiles(): void { $this->createFile('test.php', 'tempDir); $hashes = $checker->generateHashes(['test.php']); // Delete the file unlink($this->tempDir . '/test.php'); $verifier = new IntegrityChecker($hashes, $this->tempDir); $this->expectException(IntegrityException::class); $this->expectExceptionMessage('Missing file: test.php'); $verifier->verify(); } #[Test] public function itReturnsFailureDetailsInException(): void { $this->createFile('file1.php', 'content1'); $this->createFile('file2.php', 'content2'); $checker = new IntegrityChecker([], $this->tempDir); $hashes = $checker->generateHashes(['file1.php', 'file2.php']); // Modify both files $this->createFile('file1.php', 'modified1'); $this->createFile('file2.php', 'modified2'); $verifier = new IntegrityChecker($hashes, $this->tempDir); try { $verifier->verify(); self::fail('Expected IntegrityException'); } catch (IntegrityException $e) { self::assertCount(2, $e->failures); self::assertContains('Modified file: file1.php', $e->failures); self::assertContains('Modified file: file2.php', $e->failures); } } #[Test] public function itIsValidReturnsFalseForModifiedFiles(): void { $this->createFile('test.php', 'original'); $checker = new IntegrityChecker([], $this->tempDir); $hashes = $checker->generateHashes(['test.php']); $this->createFile('test.php', 'modified'); $verifier = new IntegrityChecker($hashes, $this->tempDir); self::assertFalse($verifier->isValid()); } #[Test] public function itGeneratesConsistentHashes(): void { $this->createFile('test.php', 'tempDir); $checker2 = new IntegrityChecker([], $this->tempDir); $hash1 = $checker1->generateHashes(['test.php']); $hash2 = $checker2->generateHashes(['test.php']); self::assertSame($hash1, $hash2); } #[Test] public function itNormalizesLineEndings(): void { // Create file with Unix line endings $this->createFile('test.php', "line1\nline2\n"); $checker = new IntegrityChecker([], $this->tempDir); $hash1 = $checker->generateHashes(['test.php']); // Create same file with Windows line endings $this->createFile('test.php', "line1\r\nline2\r\n"); $hash2 = $checker->generateHashes(['test.php']); self::assertSame($hash1['test.php'], $hash2['test.php']); } #[Test] public function itGeneratesPhpCode(): void { $this->createFile('test.php', 'content'); $checker = new IntegrityChecker([], $this->tempDir); $code = $checker->generateHashesAsPhpCode(['test.php']); self::assertStringContainsString('[', $code); self::assertStringContainsString(']', $code); self::assertStringContainsString("'test.php'", $code); } #[Test] public function itThrowsForNonExistentFileInGenerate(): void { $checker = new IntegrityChecker([], $this->tempDir); $this->expectException(\InvalidArgumentException::class); $checker->generateHashes(['nonexistent.php']); } #[Test] public function itHandlesSubdirectories(): void { mkdir($this->tempDir . '/subdir', 0755, true); $this->createFile('subdir/test.php', 'content'); $checker = new IntegrityChecker([], $this->tempDir); $hashes = $checker->generateHashes(['subdir/test.php']); self::assertArrayHasKey('subdir/test.php', $hashes); $verifier = new IntegrityChecker($hashes, $this->tempDir); self::assertTrue($verifier->isValid()); } private function createFile(string $relativePath, string $content): void { $fullPath = $this->tempDir . '/' . $relativePath; $dir = dirname($fullPath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($fullPath, $content); } private function removeDirectory(string $dir): void { if (!is_dir($dir)) { return; } $files = array_diff(scandir($dir), ['.', '..']); foreach ($files as $file) { $path = $dir . '/' . $file; is_dir($path) ? $this->removeDirectory($path) : unlink($path); } rmdir($dir); } }