N°8129 Handle badly formatted default date (e.g. "27/01/2025")

This commit is contained in:
XGUI
2025-01-29 15:35:26 +01:00
parent 807283505d
commit e9d21bca39
4 changed files with 77 additions and 69 deletions

View File

@@ -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());
}

View File

@@ -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);

View File

@@ -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 <https://mstdn.social/@tekkie>
*
* @since 3.2.1
*/
static protected function ReadTail($sFilename, $iLines = 1)
{

View File

@@ -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;
}
}