diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 64bd7bd4b..6a2081cb3 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -6458,15 +6458,20 @@ class AttributeDateTime extends AttributeDBField $sDefaultValue = $this->Get('default_value'); if (utils::IsNotNullOrEmptyString($sDefaultValue)) { try { - $oDate = new DateTimeImmutable($sDefaultValue); + $sDefaultDate = Expression::FromOQL($sDefaultValue)->Evaluate([]); } catch (Exception $e) { try { - $oDate = new DateTimeImmutable(Expression::FromOQL($sDefaultValue)->Evaluate([])); + $sDefaultDate = Expression::FromOQL('"'.$sDefaultValue.'"')->Evaluate([]); } catch (Exception $e) { IssueLog::Error("Invalid default value '$sDefaultValue' for field '{$this->GetCode()}' on class '{$this->GetHostClass()}', defaulting to null"); return $this->GetNullValue(); } - + } + try { + $oDate = new DateTimeImmutable($sDefaultDate); + } catch (Exception $e) { + IssueLog::Error("Invalid default value '$sDefaultValue' for field '{$this->GetCode()}' on class '{$this->GetHostClass()}', defaulting to null"); + return $this->GetNullValue(); } return $oDate->format($this->GetInternalFormat()); } diff --git a/tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php b/tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php index 9c6f47425..05bda7bbd 100644 --- a/tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php +++ b/tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php @@ -1492,7 +1492,10 @@ abstract class ItopDataTestCase extends ItopTestCase $this->assertEquals(1, $oSet->Count(), $sMessage); } - protected function AssertLastErrorLogEntryContains(string $sNeedle, string $sMessage = '') + /** + * @since 3.2.1 + */ + protected function AssertLastErrorLogEntryContains(string $sNeedle, string $sMessage = ''): void { $aLastLines = self::ReadTail(APPROOT.'/log/error.log'); $this->assertStringContainsString($sNeedle, $aLastLines[0], $sMessage); diff --git a/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php b/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php index fda90fbbb..62e913b75 100644 --- a/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php +++ b/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php @@ -7,6 +7,7 @@ namespace Combodo\iTop\Test\UnitTest; use CMDBSource; +use DateTime; use DeprecatedCallsLog; use MySQLTransactionNotClosedException; use PHPUnit\Framework\TestCase; @@ -550,6 +551,31 @@ abstract class ItopTestCase extends TestCase $this->AssertArraysHaveSameItems($aExpected, $aFiles, $sMessage); } + /** + * @since 3.2.1 + */ + static protected function AssertDateEqualsNow($sActualDate, $sMessage = ''): void + { + $oActualDate = \DateTime::createFromFormat(\AttributeDate::GetInternalFormat(), $sActualDate); + $oNow = new DateTime(); + + $iTimeInterval = $oNow->diff($oActualDate)->s; + + self::assertLessThan(2, $iTimeInterval, $sMessage); + } + /** + * @since 3.2.1 + */ + static protected function AssertDateTimeEqualsNow($sActualDate, $sMessage = ''): void + { + $oActualDateTime = \DateTime::createFromFormat(\AttributeDateTime::GetInternalFormat(), $sActualDate); + $oNow = new DateTime(); + + $iTimeInterval = $oNow->diff($oActualDateTime)->s; + + self::assertLessThan(2, $iTimeInterval, $sMessage); + } + /** * Control which Kernel will be loaded when invoking the bootKernel method * @@ -575,6 +601,8 @@ abstract class ItopTestCase extends TestCase /** * @author Ain Tohvri + * + * @since 3.2.1 */ static protected function ReadTail($sFilename, $iLines = 1) { diff --git a/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php b/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php index 82d5c4a17..85462cc8c 100644 --- a/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php +++ b/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php @@ -243,113 +243,75 @@ PHP public function testDateTimeEmptyDefaultReturnsNullAsDefaultValue() { - // Given - $oDateAttribute = new AttributeDateTime('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]); - $oDateAttribute->SetHostClass('WorkOrder'); + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDateTime::class, '', false); - //When $defaultValue = $oDateAttribute->GetDefaultValue(); - // Then self::assertNull($defaultValue, 'Empty default value for DateTime attribute should give null default value'); } public function testDateTimeInvalidDefaultReturnsNullAsDefaultValue() { - // Given - $oDateAttribute = new AttributeDateTime('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => 'zabugomeuh', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]); - $oDateAttribute->SetHostClass('WorkOrder'); + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDateTime::class, 'zabugomeuh', false); - //When $defaultValue = $oDateAttribute->GetDefaultValue(); - // Then self::assertNull($defaultValue, 'Invalid default value for DateTime attribute should give null default value'); self::AssertLastErrorLogEntryContains("Invalid default value 'zabugomeuh' for field 'start_date' on class 'WorkOrder', defaulting to null", "Last error log entry should contain a meaningful message"); } public function testDateEmptyDefaultReturnsNullAsDefaultValue() { - // Given - $oDateAttribute = new AttributeDate('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]); - $oDateAttribute->SetHostClass('WorkOrder'); + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDate::class, '', false); - //When $defaultValue = $oDateAttribute->GetDefaultValue(); - // Then self::assertNull($defaultValue, 'Empty default value for Date attribute should give null default value'); } - public function testDateInvalidDefaultReturnsNullAsDefaultValue() + public function testDateInvalidDefaultReturnsNullAsDefaultValue_Case1() { - // Given - $oDateAttribute = new AttributeDate('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => 'zabugomeuh', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]); - $oDateAttribute->SetHostClass('WorkOrder'); + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDate::class, 'zabugomeuh', false); - //When $defaultValue = $oDateAttribute->GetDefaultValue(); - self::AssertLastErrorLogEntryContains("Invalid default value 'zabugomeuh' for field 'start_date' on class 'WorkOrder', defaulting to null", "Last error log entry should contain a meaningful message"); - // Then + self::AssertLastErrorLogEntryContains("Invalid default value 'zabugomeuh' for field 'start_date' on class 'WorkOrder', defaulting to null", "Last error log entry should contain a meaningful message"); self::assertNull($defaultValue, 'Invalid default value for Date attribute should give null default value'); } + public function testDateInvalidDefaultReturnsNullAsDefaultValue_Case2() + { + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDate::class, '"27/01/2025"', false); + + $defaultValue = $oDateAttribute->GetDefaultValue(); + + self::AssertLastErrorLogEntryContains("Invalid default value '\"27/01/2025\"' for field 'start_date' on class 'WorkOrder', defaulting to null", "Last error log entry should contain a meaningful message"); + self::assertNull($defaultValue, 'Invalid default value for Date attribute should give null default value'); + } public function testDateTimeNowAsDefaultGivesCurrentDateAsDefaultValue() { - // Given - $oDateAttribute = new AttributeDateTime('start_date', [ - 'sql' => 'start_date', - 'is_null_allowed' => false, - 'default_value' => 'NOW()', - 'allowed_values' => null, - 'depends_on' => [], - 'always_load_in_tables' => false - ]); - $oDateAttribute->SetHostClass('WorkOrder'); + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDateTime::class, 'NOW()', false); - //When - $defaultValue = $oDateAttribute->GetDefaultValue(); - - // Then - $sNow = date($oDateAttribute->GetInternalFormat()); - self::assertEquals($sNow, $defaultValue, 'Now as default value for DateTime attribute should give current date as default value'); + $sDefaultValue = $oDateAttribute->GetDefaultValue(); + self::AssertDateTimeEqualsNow($sDefaultValue, 'NOW() should be evaluated as the current date and time'); } - - public function testDateNowAsDefaultGivesCurrentDateAsDefaultValue() { - // Given - $oDateAttribute = new AttributeDate('start_date', [ - 'sql' => 'start_date', - 'is_null_allowed' => false, - 'default_value' => 'NOW()', - 'allowed_values' => null, - 'depends_on' => [], - 'always_load_in_tables' => false - ]); - $oDateAttribute->SetHostClass('WorkOrder'); + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDate::class, 'NOW()', false); - //When - $defaultValue = $oDateAttribute->GetDefaultValue(); + $sDefaultValue = $oDateAttribute->GetDefaultValue(); - // Then - $sNow = date($oDateAttribute->GetInternalFormat()); - self::assertEquals($sNow, $defaultValue, 'Now as default value for Date attribute should give current date as default value'); + self::AssertDateEqualsNow($sDefaultValue, 'NOW() should be evaluated as the current date'); } public function testDateTimeIntervalAsDefaultGivesCorrectDateAsDefaultValue() { - // Given - $oDateAttribute = new AttributeDateTime('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => 'DATE_ADD(NOW(), INTERVAL 1 DAY)', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]); - $oDateAttribute->SetHostClass('WorkOrder'); + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDateTime::class, 'DATE_ADD(NOW(), INTERVAL 1 DAY)', false); - //When $defaultValue = $oDateAttribute->GetDefaultValue(); - // Then $oDate = new \DateTimeImmutable('+1day'); $sExpected = $oDate->format($oDateAttribute->GetInternalFormat()); self::assertEquals($sExpected, $defaultValue, 'Interval as default value for DateTime attribute should give correct date as default value'); @@ -357,17 +319,27 @@ PHP public function testDateIntervalAsDefaultGivesCorrectDateAsDefaultValue() { - // Given - $oDateAttribute = new AttributeDate('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => 'DATE_ADD(NOW(), INTERVAL 1 DAY)', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]); - $oDateAttribute->SetHostClass('WorkOrder'); + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDate::class, 'DATE_ADD(NOW(), INTERVAL 1 DAY)', false); - //When $defaultValue = $oDateAttribute->GetDefaultValue(); - // Then $oDate = new \DateTimeImmutable('+1day'); $sExpected = $oDate->format($oDateAttribute->GetInternalFormat()); self::assertEquals($sExpected, $defaultValue, 'Interval as default value for Date attribute should give correct date as default value'); } + public function GivenAttribute(string $sHostClass, string $sAttCode, string $sAttributeClass, mixed $defaultValue, bool $bIsNullAllowed): \AttributeDefinition + { + $oAttribute = new $sAttributeClass($sAttCode, [ + 'sql' => $sAttCode, + 'is_null_allowed' => $bIsNullAllowed, + 'default_value' => $defaultValue, + 'allowed_values' => null, + 'depends_on' => [], + 'always_load_in_tables' => false + ]); + $oAttribute->SetHostClass($sHostClass); + return $oAttribute; + } + } \ No newline at end of file