passed++; $this->tests[] = ['✓', $name]; } catch (Exception $e) { $this->failed++; $this->tests[] = ['✗', $name, $e->getMessage()]; } } public function assertEqual($actual, $expected, $message = '') { if ($actual !== $expected) { throw new Exception($message . " (expected: $expected, got: $actual)"); } } public function assertTrue($value, $message = '') { if ($value !== true) { throw new Exception($message); } } public function assertFalse($value, $message = '') { if ($value !== false) { throw new Exception($message); } } public function report() { echo "\n=== TEST RESULTS ===\n"; echo "Passed: {$this->passed}\n"; echo "Failed: {$this->failed}\n"; echo "Total: " . ($this->passed + $this->failed) . "\n\n"; foreach ($this->tests as $test) { if (count($test) === 2) { echo "{$test[0]} {$test[1]}\n"; } else { echo "{$test[0]} {$test[1]}: {$test[2]}\n"; } } echo "\n"; return $this->failed === 0; } } // Database connection for tests function getTestConnection() { $conn = new mysqli("localhost", "root", "", "webgis_unified"); if ($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } $conn->set_charset("utf8mb4"); return $conn; } // Initialize test runner $runner = new TestRunner(); // ============ Database Tests ============ $runner->test("Database Connection", function() use ($runner) { $conn = getTestConnection(); $runner->assertTrue($conn !== null, "Database connection failed"); $conn->close(); }); $runner->test("Table data_unified Exists", function() use ($runner) { $conn = getTestConnection(); $result = $conn->query("SHOW TABLES LIKE 'data_unified'"); $runner->assertTrue($result && $result->num_rows > 0, "Table data_unified not found"); $conn->close(); }); $runner->test("Table audit_log Exists", function() use ($runner) { $conn = getTestConnection(); $result = $conn->query("SHOW TABLES LIKE 'audit_log'"); $runner->assertTrue($result && $result->num_rows > 0, "Table audit_log not found"); $conn->close(); }); $runner->test("Reference Tables Exist", function() use ($runner) { $conn = getTestConnection(); $tables = ['ref_kondisi_rumah', 'ref_status_bantuan', 'ref_sumber_penghasilan']; foreach ($tables as $table) { $result = $conn->query("SHOW TABLES LIKE '$table'"); $runner->assertTrue($result && $result->num_rows > 0, "Table $table not found"); } $conn->close(); }); // ============ Data Insertion Tests ============ $runner->test("Insert Point Feature", function() use ($runner) { $conn = getTestConnection(); $sql = "INSERT INTO data_unified (tipe, nama, alamat, latitude, longitude, status) VALUES ('point', 'Test Point', 'Jln Test', -0.0554, 109.3494, 'aktif')"; $runner->assertTrue($conn->query($sql) === TRUE, "Failed to insert point"); $conn->close(); }); $runner->test("Insert Worship Feature", function() use ($runner) { $conn = getTestConnection(); $sql = "INSERT INTO data_unified (tipe, nama, jenis, alamat, latitude, longitude, radius_meter, kapasitas, status) VALUES ('worship', 'Test Masjid', 'Masjid', 'Jln Imam', -0.0554, 109.3494, 200, 500, 'aktif')"; $runner->assertTrue($conn->query($sql) === TRUE, "Failed to insert worship"); $conn->close(); }); $runner->test("Insert Poor House Feature", function() use ($runner) { $conn = getTestConnection(); $sql = "INSERT INTO data_unified (tipe, nama_kepala_keluarga, alamat, latitude, longitude, jumlah_anggota, kondisi_rumah, status) VALUES ('poor_house', 'Budi Santoso', 'Jln Cendana', -0.0554, 109.3494, 5, 'BURUK', 'aktif')"; $runner->assertTrue($conn->query($sql) === TRUE, "Failed to insert poor_house"); $conn->close(); }); // ============ Data Retrieval Tests ============ $runner->test("Retrieve All Features", function() use ($runner) { $conn = getTestConnection(); $result = $conn->query("SELECT COUNT(*) as count FROM data_unified"); $row = $result->fetch_assoc(); $runner->assertTrue($row['count'] >= 0, "Failed to count features"); $conn->close(); }); $runner->test("Retrieve Worship Features Only", function() use ($runner) { $conn = getTestConnection(); $result = $conn->query("SELECT * FROM data_unified WHERE tipe = 'worship' LIMIT 1"); $runner->assertTrue($result !== FALSE, "Failed to retrieve worship features"); $conn->close(); }); $runner->test("Retrieve Poor House Features Only", function() use ($runner) { $conn = getTestConnection(); $result = $conn->query("SELECT * FROM data_unified WHERE tipe = 'poor_house' LIMIT 1"); $runner->assertTrue($result !== FALSE, "Failed to retrieve poor_house features"); $conn->close(); }); // ============ Data Validation Tests ============ $runner->test("Validate Required Fields - tipe", function() use ($runner) { $conn = getTestConnection(); $sql = "INSERT INTO data_unified (nama, alamat) VALUES ('Test', 'Jln Test')"; $result = $conn->query($sql); $runner->assertFalse($result, "Should reject missing tipe"); $conn->close(); }); $runner->test("Validate Enum Values - tipe", function() use ($runner) { $conn = getTestConnection(); $sql = "INSERT INTO data_unified (tipe, nama) VALUES ('invalid_type', 'Test')"; $result = $conn->query($sql); $runner->assertFalse($result, "Should reject invalid tipe value"); $conn->close(); }); // ============ Data Update Tests ============ $runner->test("Update Feature Data", function() use ($runner) { $conn = getTestConnection(); // Insert test data $insert_sql = "INSERT INTO data_unified (tipe, nama, status) VALUES ('point', 'Update Test', 'aktif')"; if ($conn->query($insert_sql)) { $id = $conn->insert_id; // Update the data $update_sql = "UPDATE data_unified SET nama = 'Updated Name' WHERE id = $id"; $result = $conn->query($update_sql); $runner->assertTrue($result === TRUE, "Failed to update feature"); // Verify update $verify_sql = "SELECT nama FROM data_unified WHERE id = $id"; $verify_result = $conn->query($verify_sql); $row = $verify_result->fetch_assoc(); $runner->assertEqual($row['nama'], 'Updated Name', "Update verification failed"); } $conn->close(); }); // ============ Data Deletion Tests ============ $runner->test("Soft Delete Feature (Status Change)", function() use ($runner) { $conn = getTestConnection(); // Insert test data $insert_sql = "INSERT INTO data_unified (tipe, nama, status) VALUES ('point', 'Delete Test', 'aktif')"; if ($conn->query($insert_sql)) { $id = $conn->insert_id; // Soft delete $delete_sql = "UPDATE data_unified SET status = 'nonaktif' WHERE id = $id"; $result = $conn->query($delete_sql); $runner->assertTrue($result === TRUE, "Failed to soft delete"); // Verify soft delete $verify_sql = "SELECT status FROM data_unified WHERE id = $id"; $verify_result = $conn->query($verify_sql); $row = $verify_result->fetch_assoc(); $runner->assertEqual($row['status'], 'nonaktif', "Soft delete verification failed"); } $conn->close(); }); $runner->test("Hard Delete Feature", function() use ($runner) { $conn = getTestConnection(); // Insert test data $insert_sql = "INSERT INTO data_unified (tipe, nama) VALUES ('point', 'Hard Delete Test')"; if ($conn->query($insert_sql)) { $id = $conn->insert_id; // Hard delete $delete_sql = "DELETE FROM data_unified WHERE id = $id"; $result = $conn->query($delete_sql); $runner->assertTrue($result === TRUE, "Failed to hard delete"); // Verify hard delete $verify_sql = "SELECT COUNT(*) as count FROM data_unified WHERE id = $id"; $verify_result = $conn->query($verify_sql); $row = $verify_result->fetch_assoc(); $runner->assertEqual($row['count'], 0, "Hard delete verification failed"); } $conn->close(); }); // ============ Audit Trail Tests ============ $runner->test("Audit Log Table Has Triggers", function() use ($runner) { $conn = getTestConnection(); // Check triggers exist $result = $conn->query("SHOW TRIGGERS LIKE 'trg_data_unified%'"); $runner->assertTrue($result && $result->num_rows > 0, "Triggers not found"); $conn->close(); }); $runner->test("Audit Log Records Insertions", function() use ($runner) { $conn = getTestConnection(); // Clear audit log $conn->query("DELETE FROM audit_log"); // Insert test data $insert_sql = "INSERT INTO data_unified (tipe, nama) VALUES ('point', 'Audit Test')"; $conn->query($insert_sql); // Check if audit logged $audit_result = $conn->query("SELECT COUNT(*) as count FROM audit_log WHERE action = 'INSERT'"); $row = $audit_result->fetch_assoc(); $runner->assertTrue($row['count'] > 0, "Audit trail not recording insertions"); $conn->close(); }); // ============ Query Performance Tests ============ $runner->test("Query Performance - Count Records", function() use ($runner) { $conn = getTestConnection(); $start_time = microtime(true); $result = $conn->query("SELECT COUNT(*) FROM data_unified"); $end_time = microtime(true); $duration = ($end_time - $start_time) * 1000; // milliseconds $runner->assertTrue($duration < 1000, "Query too slow: {$duration}ms"); $conn->close(); }); $runner->test("Query Performance - Filter by Type", function() use ($runner) { $conn = getTestConnection(); $start_time = microtime(true); $result = $conn->query("SELECT * FROM data_unified WHERE tipe = 'worship' LIMIT 100"); $end_time = microtime(true); $duration = ($end_time - $start_time) * 1000; $runner->assertTrue($duration < 1000, "Filter query too slow: {$duration}ms"); $conn->close(); }); // ============ Reference Data Tests ============ $runner->test("Reference Data - Kondisi Rumah", function() use ($runner) { $conn = getTestConnection(); $result = $conn->query("SELECT COUNT(*) as count FROM ref_kondisi_rumah"); $row = $result->fetch_assoc(); $runner->assertTrue($row['count'] === 4, "Kondisi Rumah reference data incomplete"); $conn->close(); }); $runner->test("Reference Data - Status Bantuan", function() use ($runner) { $conn = getTestConnection(); $result = $conn->query("SELECT COUNT(*) as count FROM ref_status_bantuan"); $row = $result->fetch_assoc(); $runner->assertTrue($row['count'] === 4, "Status Bantuan reference data incomplete"); $conn->close(); }); $runner->test("Reference Data - Sumber Penghasilan", function() use ($runner) { $conn = getTestConnection(); $result = $conn->query("SELECT COUNT(*) as count FROM ref_sumber_penghasilan"); $row = $result->fetch_assoc(); $runner->assertTrue($row['count'] === 8, "Sumber Penghasilan reference data incomplete"); $conn->close(); }); // ============ Views Tests ============ $runner->test("View - v_data_summary Exists", function() use ($runner) { $conn = getTestConnection(); $result = $conn->query("SELECT * FROM v_data_summary LIMIT 1"); $runner->assertTrue($result !== FALSE, "View v_data_summary not found"); $conn->close(); }); $runner->test("View - v_poor_house_analysis Exists", function() use ($runner) { $conn = getTestConnection(); $result = $conn->query("SELECT * FROM v_poor_house_analysis LIMIT 1"); $runner->assertTrue($result !== FALSE, "View v_poor_house_analysis not found"); $conn->close(); }); // Display test results $success = $runner->report(); // Return exit code exit($success ? 0 : 1); ?>