From bde92d0ef5f44365cab23fd5622be85a8ce642f5 Mon Sep 17 00:00:00 2001 From: romanetar Date: Fri, 2 Oct 2026 18:08:32 +0200 Subject: [PATCH 1/4] fix(audit): log many-to-many collection changes on badge types access_levels, badge_features and allowed_view_types now report added and removed ids through SummitBadgeTypeAuditLogFormatter instead of being dropped (ADR-004). --- .../SummitBadgeTypeAuditLogFormatter.php | 4 + ...dgeTypeManyToManyAuditLogFormatterTest.php | 155 ++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 tests/OpenTelemetry/Formatters/SummitBadgeTypeManyToManyAuditLogFormatterTest.php diff --git a/app/Audit/ConcreteFormatters/SummitBadgeTypeAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SummitBadgeTypeAuditLogFormatter.php index 0c1d4e655..731e4347d 100644 --- a/app/Audit/ConcreteFormatters/SummitBadgeTypeAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/SummitBadgeTypeAuditLogFormatter.php @@ -62,6 +62,10 @@ public function format($subject, array $change_set): ?string $summit_name, $this->getUserInfo() ); + + case IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE: + case IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_DELETE: + return $this->formatManyToManyCollectionChange($change_set, sprintf("Badge Type '%s' (%s) in Summit '%s'", $name, $id, $summit_name)); } } catch (\Exception $ex) { Log::warning("SummitBadgeTypeAuditLogFormatter error: " . $ex->getMessage()); diff --git a/tests/OpenTelemetry/Formatters/SummitBadgeTypeManyToManyAuditLogFormatterTest.php b/tests/OpenTelemetry/Formatters/SummitBadgeTypeManyToManyAuditLogFormatterTest.php new file mode 100644 index 000000000..8a454aedc --- /dev/null +++ b/tests/OpenTelemetry/Formatters/SummitBadgeTypeManyToManyAuditLogFormatterTest.php @@ -0,0 +1,155 @@ +makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('access_levels', SummitAccessLevelType::class, [10, 11], [11, 12]), + ]); + + $this->assertStringStartsWith( + self::MESSAGE_PREFIX . " access_levels (SummitAccessLevelType) updated: Added IDs: [12], Removed IDs: [10] by user ", + $result + ); + } + + public function testUpdateEventReportsAddedIds(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('badge_features', SummitBadgeFeatureType::class, [], [5, 6]), + ]); + + $this->assertStringStartsWith( + self::MESSAGE_PREFIX . " badge_features (SummitBadgeFeatureType) updated: Added IDs: [5,6] by user ", + $result + ); + } + + public function testDeleteEventReportsRemovedIdsFromPayload(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_DELETE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('access_levels', SummitAccessLevelType::class, [], []), + 'deleted_ids' => [3, 4], + ]); + + $this->assertStringStartsWith( + self::MESSAGE_PREFIX . " access_levels (SummitAccessLevelType) deleted: Removed IDs: [3,4] by user ", + $result + ); + } + + public function testUpdateEventWithoutDiffIsSuppressed(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('badge_features', SummitBadgeFeatureType::class, [3], [3]), + ]); + + $this->assertNull($result); + } + + public function testDeleteEventWithNothingToRemoveIsSuppressed(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_DELETE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('badge_features', SummitBadgeFeatureType::class, [], []), + 'deleted_ids' => [], + ]); + + $this->assertNull($result); + } + + public function testManyToManyEventWithoutCollectionReturnsNull(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $this->assertNull($formatter->format($this->makeSubject(), [])); + } + + public function testManyToManyEventWithInvalidSubjectReturnsNull(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $this->assertNull($formatter->format(new \stdClass(), [ + 'collection' => $this->makeCollection('access_levels', SummitAccessLevelType::class, [], [7]), + ])); + } + + private function makeFormatter(string $eventType): SummitBadgeTypeAuditLogFormatter + { + $formatter = new SummitBadgeTypeAuditLogFormatter($eventType); + $formatter->setContext(AuditContextBuilder::default()->build()); + return $formatter; + } + + private function makeCollection(string $field, string $targetEntity, array $snapshotIds, array $currentIds) + { + return PersistentCollectionTestHelper::buildManyToManyCollection( + SummitBadgeType::class, + $field, + $targetEntity, + $snapshotIds, + $currentIds + ); + } + + private function makeSubject(): SummitBadgeType + { + $summit = Mockery::mock(Summit::class); + $summit->shouldReceive('getName')->andReturn('Test Summit'); + + $type = Mockery::mock(SummitBadgeType::class); + $type->shouldReceive('getId')->andReturn(2); + $type->shouldReceive('getName')->andReturn('General'); + $type->shouldReceive('getSummit')->andReturn($summit); + + return $type; + } +} From 962d500f80b165a4a6a4187800492eb5d5f963bd Mon Sep 17 00:00:00 2001 From: romanetar Date: Fri, 2 Oct 2026 18:08:32 +0200 Subject: [PATCH 2/4] fix(audit): log many-to-many collection changes on attendee badges features now reports added and removed ids through SummitAttendeeBadgeAuditLogFormatter instead of being dropped (ADR-004). --- .../SummitAttendeeBadgeAuditLogFormatter.php | 4 + ...eeBadgeManyToManyAuditLogFormatterTest.php | 157 ++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 tests/OpenTelemetry/Formatters/SummitAttendeeBadgeManyToManyAuditLogFormatterTest.php diff --git a/app/Audit/ConcreteFormatters/SummitAttendeeBadgeAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SummitAttendeeBadgeAuditLogFormatter.php index f97ffe24b..99de03abf 100644 --- a/app/Audit/ConcreteFormatters/SummitAttendeeBadgeAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/SummitAttendeeBadgeAuditLogFormatter.php @@ -41,6 +41,10 @@ public function format($subject, array $change_set): ?string return $this->formatUpdateMessage($change_set, fn($details) => sprintf("Attendee Badge (%s) for '%s' updated: %s by user %s", $id, $owner_name, $details, $this->getUserInfo())); case IAuditStrategy::EVENT_ENTITY_DELETION: return sprintf("Attendee Badge (%s) for '%s' deleted by user %s", $id, $owner_name, $this->getUserInfo()); + + case IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE: + case IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_DELETE: + return $this->formatManyToManyCollectionChange($change_set, sprintf("Attendee Badge (%s) for '%s'", $id, $owner_name)); } } catch (\Exception $ex) { Log::warning("SummitAttendeeBadgeAuditLogFormatter error: " . $ex->getMessage()); diff --git a/tests/OpenTelemetry/Formatters/SummitAttendeeBadgeManyToManyAuditLogFormatterTest.php b/tests/OpenTelemetry/Formatters/SummitAttendeeBadgeManyToManyAuditLogFormatterTest.php new file mode 100644 index 000000000..7e56d6c1f --- /dev/null +++ b/tests/OpenTelemetry/Formatters/SummitAttendeeBadgeManyToManyAuditLogFormatterTest.php @@ -0,0 +1,157 @@ +makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('features', SummitBadgeFeatureType::class, [10, 11], [11, 12]), + ]); + + $this->assertStringStartsWith( + self::MESSAGE_PREFIX . " features (SummitBadgeFeatureType) updated: Added IDs: [12], Removed IDs: [10] by user ", + $result + ); + } + + public function testUpdateEventReportsAddedIds(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('features', SummitBadgeFeatureType::class, [], [5, 6]), + ]); + + $this->assertStringStartsWith( + self::MESSAGE_PREFIX . " features (SummitBadgeFeatureType) updated: Added IDs: [5,6] by user ", + $result + ); + } + + public function testDeleteEventReportsRemovedIdsFromPayload(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_DELETE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('features', SummitBadgeFeatureType::class, [], []), + 'deleted_ids' => [3, 4], + ]); + + $this->assertStringStartsWith( + self::MESSAGE_PREFIX . " features (SummitBadgeFeatureType) deleted: Removed IDs: [3,4] by user ", + $result + ); + } + + public function testUpdateEventWithoutDiffIsSuppressed(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('features', SummitBadgeFeatureType::class, [3], [3]), + ]); + + $this->assertNull($result); + } + + public function testDeleteEventWithNothingToRemoveIsSuppressed(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_DELETE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('features', SummitBadgeFeatureType::class, [], []), + 'deleted_ids' => [], + ]); + + $this->assertNull($result); + } + + public function testManyToManyEventWithoutCollectionReturnsNull(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $this->assertNull($formatter->format($this->makeSubject(), [])); + } + + public function testManyToManyEventWithInvalidSubjectReturnsNull(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $this->assertNull($formatter->format(new \stdClass(), [ + 'collection' => $this->makeCollection('features', SummitBadgeFeatureType::class, [], [7]), + ])); + } + + private function makeFormatter(string $eventType): SummitAttendeeBadgeAuditLogFormatter + { + $formatter = new SummitAttendeeBadgeAuditLogFormatter($eventType); + $formatter->setContext(AuditContextBuilder::default()->build()); + return $formatter; + } + + private function makeCollection(string $field, string $targetEntity, array $snapshotIds, array $currentIds) + { + return PersistentCollectionTestHelper::buildManyToManyCollection( + SummitAttendeeBadge::class, + $field, + $targetEntity, + $snapshotIds, + $currentIds + ); + } + + private function makeSubject(): SummitAttendeeBadge + { + $owner = Mockery::mock(SummitAttendee::class); + $owner->shouldReceive('getFirstName')->andReturn('Jane'); + $owner->shouldReceive('getSurname')->andReturn('Doe'); + + $ticket = Mockery::mock(SummitAttendeeTicket::class); + $ticket->shouldReceive('getOwner')->andReturn($owner); + + $badge = Mockery::mock(SummitAttendeeBadge::class); + $badge->shouldReceive('getId')->andReturn(6); + $badge->shouldReceive('getTicket')->andReturn($ticket); + + return $badge; + } +} From b2607a87d34664064c7c9d74d2db25bad6a468a1 Mon Sep 17 00:00:00 2001 From: romanetar Date: Fri, 2 Oct 2026 18:08:33 +0200 Subject: [PATCH 3/4] fix(audit): log many-to-many collection changes on order extra questions allowed_ticket_types and allowed_badge_features_types now report added and removed ids through SummitOrderExtraQuestionTypeAuditLogFormatter instead of being dropped (ADR-004). --- ...rderExtraQuestionTypeAuditLogFormatter.php | 4 + ...ionTypeManyToManyAuditLogFormatterTest.php | 155 ++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 tests/OpenTelemetry/Formatters/SummitOrderExtraQuestionTypeManyToManyAuditLogFormatterTest.php diff --git a/app/Audit/ConcreteFormatters/SummitOrderExtraQuestionTypeAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SummitOrderExtraQuestionTypeAuditLogFormatter.php index ded0127f3..ee41ca5b7 100644 --- a/app/Audit/ConcreteFormatters/SummitOrderExtraQuestionTypeAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/SummitOrderExtraQuestionTypeAuditLogFormatter.php @@ -42,6 +42,10 @@ public function format($subject, array $change_set): ?string return $this->formatUpdateMessage($change_set, fn($details) => sprintf("Order Extra Question '%s' (%s) in Summit '%s' updated: %s by user %s", $label, $id, $summit_name, $details, $this->getUserInfo())); case IAuditStrategy::EVENT_ENTITY_DELETION: return sprintf("Order Extra Question '%s' (%s) in Summit '%s' deleted by user %s", $label, $id, $summit_name, $this->getUserInfo()); + + case IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE: + case IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_DELETE: + return $this->formatManyToManyCollectionChange($change_set, sprintf("Order Extra Question '%s' (%s) in Summit '%s'", $label, $id, $summit_name)); } } catch (\Exception $ex) { Log::warning("SummitOrderExtraQuestionTypeAuditLogFormatter error: " . $ex->getMessage()); diff --git a/tests/OpenTelemetry/Formatters/SummitOrderExtraQuestionTypeManyToManyAuditLogFormatterTest.php b/tests/OpenTelemetry/Formatters/SummitOrderExtraQuestionTypeManyToManyAuditLogFormatterTest.php new file mode 100644 index 000000000..53728ed80 --- /dev/null +++ b/tests/OpenTelemetry/Formatters/SummitOrderExtraQuestionTypeManyToManyAuditLogFormatterTest.php @@ -0,0 +1,155 @@ +makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('allowed_ticket_types', SummitTicketType::class, [10, 11], [11, 12]), + ]); + + $this->assertStringStartsWith( + self::MESSAGE_PREFIX . " allowed_ticket_types (SummitTicketType) updated: Added IDs: [12], Removed IDs: [10] by user ", + $result + ); + } + + public function testUpdateEventReportsAddedIds(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('allowed_badge_features_types', SummitBadgeFeatureType::class, [], [5, 6]), + ]); + + $this->assertStringStartsWith( + self::MESSAGE_PREFIX . " allowed_badge_features_types (SummitBadgeFeatureType) updated: Added IDs: [5,6] by user ", + $result + ); + } + + public function testDeleteEventReportsRemovedIdsFromPayload(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_DELETE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('allowed_ticket_types', SummitTicketType::class, [], []), + 'deleted_ids' => [3, 4], + ]); + + $this->assertStringStartsWith( + self::MESSAGE_PREFIX . " allowed_ticket_types (SummitTicketType) deleted: Removed IDs: [3,4] by user ", + $result + ); + } + + public function testUpdateEventWithoutDiffIsSuppressed(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('allowed_badge_features_types', SummitBadgeFeatureType::class, [3], [3]), + ]); + + $this->assertNull($result); + } + + public function testDeleteEventWithNothingToRemoveIsSuppressed(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_DELETE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('allowed_badge_features_types', SummitBadgeFeatureType::class, [], []), + 'deleted_ids' => [], + ]); + + $this->assertNull($result); + } + + public function testManyToManyEventWithoutCollectionReturnsNull(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $this->assertNull($formatter->format($this->makeSubject(), [])); + } + + public function testManyToManyEventWithInvalidSubjectReturnsNull(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $this->assertNull($formatter->format(new \stdClass(), [ + 'collection' => $this->makeCollection('allowed_ticket_types', SummitTicketType::class, [], [7]), + ])); + } + + private function makeFormatter(string $eventType): SummitOrderExtraQuestionTypeAuditLogFormatter + { + $formatter = new SummitOrderExtraQuestionTypeAuditLogFormatter($eventType); + $formatter->setContext(AuditContextBuilder::default()->build()); + return $formatter; + } + + private function makeCollection(string $field, string $targetEntity, array $snapshotIds, array $currentIds) + { + return PersistentCollectionTestHelper::buildManyToManyCollection( + SummitOrderExtraQuestionType::class, + $field, + $targetEntity, + $snapshotIds, + $currentIds + ); + } + + private function makeSubject(): SummitOrderExtraQuestionType + { + $summit = Mockery::mock(Summit::class); + $summit->shouldReceive('getName')->andReturn('Test Summit'); + + $question = Mockery::mock(SummitOrderExtraQuestionType::class); + $question->shouldReceive('getId')->andReturn(33); + $question->shouldReceive('getLabel')->andReturn('Dietary needs'); + $question->shouldReceive('getType')->andReturn('Text'); + $question->shouldReceive('getSummit')->andReturn($summit); + + return $question; + } +} From 3b382a6ca65401ea6469f315227b0f7da5ccdfa6 Mon Sep 17 00:00:00 2001 From: romanetar Date: Fri, 2 Oct 2026 18:08:33 +0200 Subject: [PATCH 4/4] fix(audit): log many-to-many collection changes on tax types ticket_types now reports added and removed ids through SummitTaxTypeAuditLogFormatter instead of being dropped (ADR-004). --- .../SummitTaxTypeAuditLogFormatter.php | 4 + ...TaxTypeManyToManyAuditLogFormatterTest.php | 155 ++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 tests/OpenTelemetry/Formatters/SummitTaxTypeManyToManyAuditLogFormatterTest.php diff --git a/app/Audit/ConcreteFormatters/SummitTaxTypeAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SummitTaxTypeAuditLogFormatter.php index 4cc3da825..329ec7938 100644 --- a/app/Audit/ConcreteFormatters/SummitTaxTypeAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/SummitTaxTypeAuditLogFormatter.php @@ -68,6 +68,10 @@ public function format($subject, array $change_set): ?string ($rate !== null ? sprintf("%.2f%%", $rate) : 'N/A'), $this->getUserInfo() ); + + case IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE: + case IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_DELETE: + return $this->formatManyToManyCollectionChange($change_set, sprintf("Tax Type '%s' (%s) for Summit '%s'", $name, $id, $summit_name)); } } catch (\Exception $ex) { Log::warning("SummitTaxTypeAuditLogFormatter error: " . $ex->getMessage()); diff --git a/tests/OpenTelemetry/Formatters/SummitTaxTypeManyToManyAuditLogFormatterTest.php b/tests/OpenTelemetry/Formatters/SummitTaxTypeManyToManyAuditLogFormatterTest.php new file mode 100644 index 000000000..4a73201df --- /dev/null +++ b/tests/OpenTelemetry/Formatters/SummitTaxTypeManyToManyAuditLogFormatterTest.php @@ -0,0 +1,155 @@ +makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('ticket_types', SummitTicketType::class, [10, 11], [11, 12]), + ]); + + $this->assertStringStartsWith( + self::MESSAGE_PREFIX . " ticket_types (SummitTicketType) updated: Added IDs: [12], Removed IDs: [10] by user ", + $result + ); + } + + public function testUpdateEventReportsAddedIds(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('ticket_types', SummitTicketType::class, [], [5, 6]), + ]); + + $this->assertStringStartsWith( + self::MESSAGE_PREFIX . " ticket_types (SummitTicketType) updated: Added IDs: [5,6] by user ", + $result + ); + } + + public function testDeleteEventReportsRemovedIdsFromPayload(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_DELETE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('ticket_types', SummitTicketType::class, [], []), + 'deleted_ids' => [3, 4], + ]); + + $this->assertStringStartsWith( + self::MESSAGE_PREFIX . " ticket_types (SummitTicketType) deleted: Removed IDs: [3,4] by user ", + $result + ); + } + + public function testUpdateEventWithoutDiffIsSuppressed(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('ticket_types', SummitTicketType::class, [3], [3]), + ]); + + $this->assertNull($result); + } + + public function testDeleteEventWithNothingToRemoveIsSuppressed(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_DELETE); + + $result = $formatter->format($this->makeSubject(), [ + 'collection' => $this->makeCollection('ticket_types', SummitTicketType::class, [], []), + 'deleted_ids' => [], + ]); + + $this->assertNull($result); + } + + public function testManyToManyEventWithoutCollectionReturnsNull(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $this->assertNull($formatter->format($this->makeSubject(), [])); + } + + public function testManyToManyEventWithInvalidSubjectReturnsNull(): void + { + $formatter = $this->makeFormatter(IAuditStrategy::EVENT_COLLECTION_MANYTOMANY_UPDATE); + + $this->assertNull($formatter->format(new \stdClass(), [ + 'collection' => $this->makeCollection('ticket_types', SummitTicketType::class, [], [7]), + ])); + } + + private function makeFormatter(string $eventType): SummitTaxTypeAuditLogFormatter + { + $formatter = new SummitTaxTypeAuditLogFormatter($eventType); + $formatter->setContext(AuditContextBuilder::default()->build()); + return $formatter; + } + + private function makeCollection(string $field, string $targetEntity, array $snapshotIds, array $currentIds) + { + return PersistentCollectionTestHelper::buildManyToManyCollection( + SummitTaxType::class, + $field, + $targetEntity, + $snapshotIds, + $currentIds + ); + } + + private function makeSubject(): SummitTaxType + { + $summit = Mockery::mock(Summit::class); + $summit->shouldReceive('getName')->andReturn('Test Summit'); + + $tax = Mockery::mock(SummitTaxType::class); + $tax->shouldReceive('getId')->andReturn(14); + $tax->shouldReceive('getName')->andReturn('VAT'); + $tax->shouldReceive('getTaxId')->andReturn('T-1'); + $tax->shouldReceive('getRate')->andReturn(21.0); + $tax->shouldReceive('getSummit')->andReturn($summit); + + return $tax; + } +}