diff --git a/app/Audit/ConcreteFormatters/SummitDocumentAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SummitDocumentAuditLogFormatter.php new file mode 100644 index 000000000..bdb64cd28 --- /dev/null +++ b/app/Audit/ConcreteFormatters/SummitDocumentAuditLogFormatter.php @@ -0,0 +1,75 @@ +getName() ?? 'Unknown Document'; + $id = $subject->getId() ?? 'unknown'; + $label = $subject->getLabel() ?? $name; + $summit = $subject->getSummit(); + $summit_name = $summit ? ($summit->getName() ?? 'Unknown Summit') : 'Unknown Summit'; + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + return sprintf( + "Summit Document '%s' (%d) created for Summit '%s' with label '%s' by user %s", + $name, + $id, + $summit_name, + $label, + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $change_details = $this->buildChangeDetails($change_set); + return sprintf( + "Summit Document '%s' (%d) for Summit '%s' updated: %s by user %s", + $name, + $id, + $summit_name, + $change_details, + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + return sprintf( + "Summit Document '%s' (%d) for Summit '%s' was deleted by user %s", + $name, + $id, + $summit_name, + $this->getUserInfo() + ); + } + } catch (\Exception $ex) { + Log::warning("SummitDocumentAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/app/Audit/ConcreteFormatters/SummitScheduleConfigAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SummitScheduleConfigAuditLogFormatter.php new file mode 100644 index 000000000..55eae585d --- /dev/null +++ b/app/Audit/ConcreteFormatters/SummitScheduleConfigAuditLogFormatter.php @@ -0,0 +1,77 @@ +getKey() ?? 'Unknown Config'; + $id = $subject->getId() ?? 'unknown'; + $summit = $subject->getSummit(); + $summit_name = $summit ? ($summit->getName() ?? 'Unknown Summit') : 'Unknown Summit'; + $is_default = $subject->getIsDefault() ? 'default' : 'non-default'; + $color_source = $subject->getColorSource() ?? 'Unknown'; + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + return sprintf( + "Schedule Config '%s' (%d) created for Summit '%s' (%s, color source: %s) by user %s", + $key, + $id, + $summit_name, + $is_default, + $color_source, + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $change_details = $this->buildChangeDetails($change_set); + return sprintf( + "Schedule Config '%s' (%d) for Summit '%s' updated: %s by user %s", + $key, + $id, + $summit_name, + $change_details, + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + return sprintf( + "Schedule Config '%s' (%d) for Summit '%s' was deleted by user %s", + $key, + $id, + $summit_name, + $this->getUserInfo() + ); + } + } catch (\Exception $ex) { + Log::warning("SummitScheduleConfigAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/app/Audit/ConcreteFormatters/SummitSchedulePreFilterElementConfigAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SummitSchedulePreFilterElementConfigAuditLogFormatter.php new file mode 100644 index 000000000..ac9fbac28 --- /dev/null +++ b/app/Audit/ConcreteFormatters/SummitSchedulePreFilterElementConfigAuditLogFormatter.php @@ -0,0 +1,72 @@ +getId() ?? 'unknown'; + $type = $subject->getType() ?? 'Unknown Type'; + + $schedule_config = $subject->getConfig(); + $config_key = $schedule_config ? ($schedule_config->getKey() ?? 'Unknown Config') : 'Unknown Config'; + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + return sprintf( + "Schedule Pre-Filter Element Config (%d) created for Config '%s' with type '%s' by user %s", + $id, + $config_key, + $type, + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $change_details = $this->buildChangeDetails($change_set); + return sprintf( + "Schedule Pre-Filter Element Config (%d) for Config '%s' updated: %s by user %s", + $id, + $config_key, + $change_details, + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + return sprintf( + "Schedule Pre-Filter Element Config (%d) for Config '%s' was deleted by user %s", + $id, + $config_key, + $this->getUserInfo() + ); + } + } catch (\Exception $ex) { + Log::warning("SummitSchedulePreFilterElementConfigAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/app/Audit/ConcreteFormatters/SummitSignAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SummitSignAuditLogFormatter.php new file mode 100644 index 000000000..19c2d66ac --- /dev/null +++ b/app/Audit/ConcreteFormatters/SummitSignAuditLogFormatter.php @@ -0,0 +1,76 @@ +getId() ?? 'unknown'; + $template = $subject->getTemplate() ?? 'Unknown Template'; + $location = $subject->getLocation(); + $location_name = $location ? ($location->getName() ?? 'Unknown Location') : 'No Location'; + $summit = $subject->getSummit(); + $summit_name = $summit ? ($summit->getName() ?? 'Unknown Summit') : 'Unknown Summit'; + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + return sprintf( + "Summit Sign (%d) created for Summit '%s' at Location '%s' with template '%s' by user %s", + $id, + $summit_name, + $location_name, + $template, + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $change_details = $this->buildChangeDetails($change_set); + return sprintf( + "Summit Sign (%d) for Summit '%s' at Location '%s' updated: %s by user %s", + $id, + $summit_name, + $location_name, + $change_details, + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + return sprintf( + "Summit Sign (%d) for Summit '%s' at Location '%s' was deleted by user %s", + $id, + $summit_name, + $location_name, + $this->getUserInfo() + ); + } + } catch (\Exception $ex) { + Log::warning("SummitSignAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/app/Audit/ConcreteFormatters/TrackTagGroupAuditLogFormatter.php b/app/Audit/ConcreteFormatters/TrackTagGroupAuditLogFormatter.php new file mode 100644 index 000000000..b4009265d --- /dev/null +++ b/app/Audit/ConcreteFormatters/TrackTagGroupAuditLogFormatter.php @@ -0,0 +1,75 @@ +getName() ?? 'Unknown Tag Group'; + $id = $subject->getId() ?? 'unknown'; + $label = $subject->getLabel() ?? $name; + $summit = $subject->getSummit(); + $summit_name = $summit ? ($summit->getName() ?? 'Unknown Summit') : 'Unknown Summit'; + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + return sprintf( + "Track Tag Group '%s' (%d) created for Summit '%s' with label '%s' by user %s", + $name, + $id, + $summit_name, + $label, + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $change_details = $this->buildChangeDetails($change_set); + return sprintf( + "Track Tag Group '%s' (%d) for Summit '%s' updated: %s by user %s", + $name, + $id, + $summit_name, + $change_details, + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + return sprintf( + "Track Tag Group '%s' (%d) for Summit '%s' was deleted by user %s", + $name, + $id, + $summit_name, + $this->getUserInfo() + ); + } + } catch (\Exception $ex) { + Log::warning("TrackTagGroupAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/config/audit_log.php b/config/audit_log.php index 2e416a283..70489c6de 100644 --- a/config/audit_log.php +++ b/config/audit_log.php @@ -240,5 +240,25 @@ 'enabled' => true, 'strategy' => \App\Audit\ConcreteFormatters\ExtraQuestionTypeValueAuditLogFormatter::class, ], + \models\summit\SummitDocument::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\SummitDocumentAuditLogFormatter::class, + ], + \models\summit\SummitScheduleConfig::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\SummitScheduleConfigAuditLogFormatter::class, + ], + \models\summit\SummitSchedulePreFilterElementConfig::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\SummitSchedulePreFilterElementConfigAuditLogFormatter::class, + ], + \App\Models\Foundation\Summit\Signs\SummitSign::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\SummitSignAuditLogFormatter::class, + ], + \App\Models\Foundation\Summit\TrackTagGroup::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\TrackTagGroupAuditLogFormatter::class, + ], ] ]; diff --git a/tests/OpenTelemetry/Formatters/SummitDocumentAuditLogFormatterTest.php b/tests/OpenTelemetry/Formatters/SummitDocumentAuditLogFormatterTest.php new file mode 100644 index 000000000..33b26c0e0 --- /dev/null +++ b/tests/OpenTelemetry/Formatters/SummitDocumentAuditLogFormatterTest.php @@ -0,0 +1,106 @@ +mockSubject = $this->createMockSubject(); + } + + protected function tearDown(): void + { + Mockery::close(); + parent::tearDown(); + } + + private function createMockSubject(): mixed + { + $mockSummit = Mockery::mock('models\summit\Summit'); + $mockSummit->shouldReceive('getName')->andReturn(self::SUMMIT_NAME); + + $mock = Mockery::mock('models\summit\SummitDocument'); + + // Configure return values + $mock->shouldReceive('getId')->andReturn(self::DOCUMENT_ID); + $mock->shouldReceive('getName')->andReturn(self::DOCUMENT_NAME); + $mock->shouldReceive('getLabel')->andReturn(self::DOCUMENT_LABEL); + $mock->shouldReceive('getSummit')->andReturn($mockSummit); + + return $mock; + } + + public function testSubjectCreationAuditMessage(): void + { + $formatter = new SummitDocumentAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format($this->mockSubject, []); + + $this->assertNotNull($result); + $this->assertStringContainsString('created', $result); + $this->assertStringContainsString(self::DOCUMENT_NAME, $result); + $this->assertStringContainsString(self::DOCUMENT_LABEL, $result); + $this->assertStringContainsString((string)self::DOCUMENT_ID, $result); + } + + public function testSubjectUpdateAuditMessage(): void + { + $formatter = new SummitDocumentAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_UPDATE); + $formatter->setContext(AuditContextBuilder::default()->build()); + $changeSet = [ + 'name' => [self::DOCUMENT_NAME, 'Summit Rules'], + 'label' => [self::DOCUMENT_LABEL, 'Critical Document'] + ]; + + $result = $formatter->format($this->mockSubject, $changeSet); + + $this->assertNotNull($result); + $this->assertStringContainsString('updated', $result); + } + + public function testSubjectDeletionAuditMessage(): void + { + $formatter = new SummitDocumentAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_DELETION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format($this->mockSubject, []); + + $this->assertNotNull($result); + $this->assertStringContainsString('deleted', $result); + } + + public function testFormatterReturnsNullForInvalidSubject(): void + { + $formatter = new SummitDocumentAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format(new \stdClass(), []); + $this->assertNull($result); + } +} diff --git a/tests/OpenTelemetry/Formatters/SummitScheduleConfigAuditLogFormatterTest.php b/tests/OpenTelemetry/Formatters/SummitScheduleConfigAuditLogFormatterTest.php new file mode 100644 index 000000000..b0dfc22b2 --- /dev/null +++ b/tests/OpenTelemetry/Formatters/SummitScheduleConfigAuditLogFormatterTest.php @@ -0,0 +1,110 @@ +mockSubject = $this->createMockSubject(); + } + + protected function tearDown(): void + { + Mockery::close(); + parent::tearDown(); + } + + private function createMockSubject(): mixed + { + $mockSummit = Mockery::mock('models\summit\Summit'); + $mockSummit->shouldReceive('getName')->andReturn(self::SUMMIT_NAME); + + $mock = Mockery::mock('models\summit\SummitScheduleConfig'); + + // Configure return values + $mock->shouldReceive('getId')->andReturn(self::CONFIG_ID); + $mock->shouldReceive('getKey')->andReturn(self::CONFIG_KEY); + $mock->shouldReceive('getIsDefault')->andReturn(self::CONFIG_IS_DEFAULT); + $mock->shouldReceive('getColorSource')->andReturn(self::CONFIG_COLOR_SOURCE); + $mock->shouldReceive('getSummit')->andReturn($mockSummit); + + return $mock; + } + + public function testSubjectCreationAuditMessage(): void + { + $formatter = new SummitScheduleConfigAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format($this->mockSubject, []); + + $this->assertNotNull($result); + $this->assertStringContainsString('created', $result); + $this->assertStringContainsString(self::CONFIG_KEY, $result); + $this->assertStringContainsString((string)self::CONFIG_ID, $result); + $this->assertStringContainsString('default', $result); + } + + public function testSubjectUpdateAuditMessage(): void + { + $formatter = new SummitScheduleConfigAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_UPDATE); + $formatter->setContext(AuditContextBuilder::default()->build()); + $changeSet = [ + 'key' => [self::CONFIG_KEY, self::ALTERNATE_CONFIG_KEY], + 'color_source' => [self::CONFIG_COLOR_SOURCE, self::ALTERNATE_COLOR_SOURCE] + ]; + + $result = $formatter->format($this->mockSubject, $changeSet); + + $this->assertNotNull($result); + $this->assertStringContainsString('updated', $result); + } + + public function testSubjectDeletionAuditMessage(): void + { + $formatter = new SummitScheduleConfigAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_DELETION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format($this->mockSubject, []); + + $this->assertNotNull($result); + $this->assertStringContainsString('deleted', $result); + } + + public function testFormatterReturnsNullForInvalidSubject(): void + { + $formatter = new SummitScheduleConfigAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format(new \stdClass(), []); + $this->assertNull($result); + } +} diff --git a/tests/OpenTelemetry/Formatters/SummitSchedulePreFilterElementConfigAuditLogFormatterTest.php b/tests/OpenTelemetry/Formatters/SummitSchedulePreFilterElementConfigAuditLogFormatterTest.php new file mode 100644 index 000000000..c2e10fd37 --- /dev/null +++ b/tests/OpenTelemetry/Formatters/SummitSchedulePreFilterElementConfigAuditLogFormatterTest.php @@ -0,0 +1,102 @@ +mockSubject = $this->createMockSubject(); + } + + protected function tearDown(): void + { + Mockery::close(); + parent::tearDown(); + } + + private function createMockSubject(): mixed + { + $mockConfig = Mockery::mock('models\summit\SummitScheduleConfig'); + $mockConfig->shouldReceive('getKey')->andReturn(self::CONFIG_KEY); + + $mock = Mockery::mock('models\summit\SummitSchedulePreFilterElementConfig'); + + // Configure return values + $mock->shouldReceive('getId')->andReturn(self::FILTER_ELEMENT_ID); + $mock->shouldReceive('getType')->andReturn(self::FILTER_ELEMENT_TYPE); + $mock->shouldReceive('getConfig')->andReturn($mockConfig); + + return $mock; + } + + public function testSubjectCreationAuditMessage(): void + { + $formatter = new SummitSchedulePreFilterElementConfigAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format($this->mockSubject, []); + + $this->assertNotNull($result); + $this->assertStringContainsString('created', $result); + $this->assertStringContainsString((string)self::FILTER_ELEMENT_ID, $result); + $this->assertStringContainsString(self::FILTER_ELEMENT_TYPE, $result); + $this->assertStringContainsString(self::CONFIG_KEY, $result); + } + + public function testSubjectUpdateAuditMessage(): void + { + $formatter = new SummitSchedulePreFilterElementConfigAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_UPDATE); + $formatter->setContext(AuditContextBuilder::default()->build()); + $changeSet = ['type' => [self::FILTER_ELEMENT_TYPE, self::FILTER_ELEMENT_TYPE_UPDATE]]; + + $result = $formatter->format($this->mockSubject, $changeSet); + + $this->assertNotNull($result); + $this->assertStringContainsString('updated', $result); + } + + public function testSubjectDeletionAuditMessage(): void + { + $formatter = new SummitSchedulePreFilterElementConfigAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_DELETION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format($this->mockSubject, []); + + $this->assertNotNull($result); + $this->assertStringContainsString('deleted', $result); + } + + public function testFormatterReturnsNullForInvalidSubject(): void + { + $formatter = new SummitSchedulePreFilterElementConfigAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format(new \stdClass(), []); + $this->assertNull($result); + } +} diff --git a/tests/OpenTelemetry/Formatters/SummitSignAuditLogFormatterTest.php b/tests/OpenTelemetry/Formatters/SummitSignAuditLogFormatterTest.php new file mode 100644 index 000000000..cce83e7d6 --- /dev/null +++ b/tests/OpenTelemetry/Formatters/SummitSignAuditLogFormatterTest.php @@ -0,0 +1,106 @@ +mockSubject = $this->createMockSubject(); + } + + protected function tearDown(): void + { + Mockery::close(); + parent::tearDown(); + } + + private function createMockSubject(): mixed + { + $mockSummit = Mockery::mock('models\summit\Summit'); + $mockSummit->shouldReceive('getName')->andReturn(self::SUMMIT_NAME); + + $mockLocation = Mockery::mock('models\summit\SummitAbstractLocation'); + $mockLocation->shouldReceive('getName')->andReturn(self::LOCATION_NAME); + + $mock = Mockery::mock('App\Models\Foundation\Summit\Signs\SummitSign'); + + // Configure return values + $mock->shouldReceive('getId')->andReturn(self::SIGN_ID); + $mock->shouldReceive('getTemplate')->andReturn(self::SIGN_TEMPLATE); + $mock->shouldReceive('getLocation')->andReturn($mockLocation); + $mock->shouldReceive('getSummit')->andReturn($mockSummit); + + return $mock; + } + + public function testSubjectCreationAuditMessage(): void + { + $formatter = new SummitSignAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format($this->mockSubject, []); + + $this->assertNotNull($result); + $this->assertStringContainsString('created', $result); + $this->assertStringContainsString((string)self::SIGN_ID, $result); + $this->assertStringContainsString(self::SIGN_TEMPLATE, $result); + $this->assertStringContainsString(self::LOCATION_NAME, $result); + } + + public function testSubjectUpdateAuditMessage(): void + { + $formatter = new SummitSignAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_UPDATE); + $formatter->setContext(AuditContextBuilder::default()->build()); + $changeSet = ['template' => [self::SIGN_TEMPLATE, 'Goodbye Template']]; + + $result = $formatter->format($this->mockSubject, $changeSet); + + $this->assertNotNull($result); + $this->assertStringContainsString('updated', $result); + } + + public function testSubjectDeletionAuditMessage(): void + { + $formatter = new SummitSignAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_DELETION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format($this->mockSubject, []); + + $this->assertNotNull($result); + $this->assertStringContainsString('deleted', $result); + } + + public function testFormatterReturnsNullForInvalidSubject(): void + { + $formatter = new SummitSignAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format(new \stdClass(), []); + $this->assertNull($result); + } +} diff --git a/tests/OpenTelemetry/Formatters/TrackTagGroupAuditLogFormatterTest.php b/tests/OpenTelemetry/Formatters/TrackTagGroupAuditLogFormatterTest.php new file mode 100644 index 000000000..32635bfd4 --- /dev/null +++ b/tests/OpenTelemetry/Formatters/TrackTagGroupAuditLogFormatterTest.php @@ -0,0 +1,106 @@ +mockSubject = $this->createMockSubject(); + } + + protected function tearDown(): void + { + Mockery::close(); + parent::tearDown(); + } + + private function createMockSubject(): mixed + { + $mockSummit = Mockery::mock('models\summit\Summit'); + $mockSummit->shouldReceive('getName')->andReturn(self::SUMMIT_NAME); + + $mock = Mockery::mock('App\Models\Foundation\Summit\TrackTagGroup'); + + // Configure return values + $mock->shouldReceive('getId')->andReturn(self::TAG_GROUP_ID); + $mock->shouldReceive('getName')->andReturn(self::TAG_GROUP_NAME); + $mock->shouldReceive('getLabel')->andReturn(self::TAG_GROUP_LABEL); + $mock->shouldReceive('getSummit')->andReturn($mockSummit); + + return $mock; + } + + public function testSubjectCreationAuditMessage(): void + { + $formatter = new TrackTagGroupAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format($this->mockSubject, []); + + $this->assertNotNull($result); + $this->assertStringContainsString('created', $result); + $this->assertStringContainsString(self::TAG_GROUP_NAME, $result); + $this->assertStringContainsString(self::TAG_GROUP_LABEL, $result); + $this->assertStringContainsString((string)self::TAG_GROUP_ID, $result); + } + + public function testSubjectUpdateAuditMessage(): void + { + $formatter = new TrackTagGroupAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_UPDATE); + $formatter->setContext(AuditContextBuilder::default()->build()); + $changeSet = [ + 'name' => [self::TAG_GROUP_NAME, 'Infrastructure'], + 'label' => [self::TAG_GROUP_LABEL, 'Infrastructure Tags'] + ]; + + $result = $formatter->format($this->mockSubject, $changeSet); + + $this->assertNotNull($result); + $this->assertStringContainsString('updated', $result); + } + + public function testSubjectDeletionAuditMessage(): void + { + $formatter = new TrackTagGroupAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_DELETION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format($this->mockSubject, []); + + $this->assertNotNull($result); + $this->assertStringContainsString('deleted', $result); + } + + public function testFormatterReturnsNullForInvalidSubject(): void + { + $formatter = new TrackTagGroupAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION); + $formatter->setContext(AuditContextBuilder::default()->build()); + $result = $formatter->format(new \stdClass(), []); + $this->assertNull($result); + } +}