diff --git a/src/Migration/Sources/CSV.php b/src/Migration/Sources/CSV.php index 7d02090f..507b62b7 100644 --- a/src/Migration/Sources/CSV.php +++ b/src/Migration/Sources/CSV.php @@ -469,7 +469,9 @@ private function validateCSVHeaders(array $headers, array $columnTypes, array $r $internals = ['$id', '$permissions', '$createdAt', '$updatedAt']; $allKnown = \array_keys($columnTypes); - // Only validate that required columns are present + $messages = []; + + // Check for missing required columns — warn but don't fail $missingRequired = []; foreach (\array_keys($requiredColumns) as $requiredColumn) { if (!\in_array($requiredColumn, $headers)) { @@ -477,28 +479,23 @@ private function validateCSVHeaders(array $headers, array $columnTypes, array $r } } - $messages = []; - - // If there are missing required columns, throw an exception if (!empty($missingRequired)) { $label = \count($missingRequired) === 1 ? 'Missing required column' : 'Missing required columns'; - $messages[] = "$label: '" . \implode("', '", $missingRequired) . "'"; - } - if (!empty($missingRequired)) { - throw new \Exception('CSV header validation failed: ' . \implode('. ', $messages), Exception::CODE_VALIDATION); + $messages[] = "$label: '" . \implode("', '", $missingRequired) . "' (rows will have null values)"; } - // If there are unknown columns but no missing required columns, just log a warning + // Check for unknown columns $unknown = \array_diff($headers, $allKnown, $internals); if (!empty($unknown)) { $label = \count($unknown) === 1 ? 'Unknown column' : 'Unknown columns'; $messages[] = "$label: '" . \implode("', '", $unknown) . "' (will be ignored)"; } - if (!empty($unknown)) { + + if (!empty($messages)) { $this->addWarning(new Warning( UtopiaResource::TYPE_ROW, Transfer::GROUP_DATABASES, - \implode(', ', $messages), + \implode('. ', $messages), $this->resourceId )); } diff --git a/tests/Migration/Unit/General/CSVTest.php b/tests/Migration/Unit/General/CSVTest.php index e4fd3ec1..afd4c0b4 100644 --- a/tests/Migration/Unit/General/CSVTest.php +++ b/tests/Migration/Unit/General/CSVTest.php @@ -416,6 +416,49 @@ public function testCSVExportImportCompatibility() } } + /** + * @throws \ReflectionException + */ + private function callValidateCSVHeaders(array $headers, array $columnTypes, array $requiredColumns): void + { + $reflection = new \ReflectionClass(CSV::class); + $instance = $reflection->newInstanceWithoutConstructor(); + + $refProp = $reflection->getProperty('resourceId'); + $refProp->setAccessible(true); + $refProp->setValue($instance, 'test_db:test_table'); + + $refMethod = $reflection->getMethod('validateCSVHeaders'); + $refMethod->setAccessible(true); + + $refMethod->invoke($instance, $headers, $columnTypes, $requiredColumns); + } + + public function testValidateCSVHeadersMissingRequiredColumnDoesNotThrow(): void + { + // A CSV with only 'name' column, but 'texte' is required in the schema + $headers = ['name']; + $columnTypes = ['name' => 'string', 'texte' => 'string']; + $requiredColumns = ['texte' => true]; + + // This should NOT throw — missing required columns should be a warning, not an error + $this->callValidateCSVHeaders($headers, $columnTypes, $requiredColumns); + + // If we reach here, the test passes (no exception thrown) + $this->assertTrue(true); + } + + public function testValidateCSVHeadersAllRequiredPresent(): void + { + $headers = ['name', 'texte']; + $columnTypes = ['name' => 'string', 'texte' => 'string']; + $requiredColumns = ['texte' => true]; + + // Should not throw when all required columns are present + $this->callValidateCSVHeaders($headers, $columnTypes, $requiredColumns); + $this->assertTrue(true); + } + private function recursiveDelete(string $dir): void { if (is_dir($dir)) {