diff --git a/app/Audit/ConcreteFormatters/PrePaidSummitRegistrationDiscountCodeAuditLogFormatter.php b/app/Audit/ConcreteFormatters/PrePaidSummitRegistrationDiscountCodeAuditLogFormatter.php new file mode 100644 index 000000000..e6d2e2821 --- /dev/null +++ b/app/Audit/ConcreteFormatters/PrePaidSummitRegistrationDiscountCodeAuditLogFormatter.php @@ -0,0 +1,101 @@ +getRate(); + $amount = $subject->getAmount(); + $quantity_available = $subject->getQuantityAvailable(); + + if ($rate > 0) { + $details[] = sprintf("rate: %.2f%%", $rate); + } + + if ($amount > 0) { + $details[] = sprintf("amount: $%.2f", $amount); + } + + if ($quantity_available > 0) { + $details[] = sprintf("quantity: %d", $quantity_available); + } + + return implode(", ", $details); + } + + public function format($subject, array $change_set): ?string + { + if (!$subject instanceof PrePaidSummitRegistrationDiscountCode) { + return null; + } + + try { + $code = $subject->getCode() ?? 'Unknown'; + $id = $subject->getId() ?? 'unknown'; + $summit = $subject->getSummit(); + $summit_name = $summit ? ($summit->getName() ?? 'Unknown Summit') : 'Unknown Summit'; + $discount_details = $this->buildDiscountDetails($subject); + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + return sprintf( + "Pre-Paid Discount Code '%s' (%d) created for Summit '%s' with %s by user %s", + $code, + $id, + $summit_name, + $discount_details, + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $change_details = $this->buildChangeDetails($change_set); + return sprintf( + "Pre-Paid Discount Code '%s' (%d) for Summit '%s' updated: %s (current: %s) by user %s", + $code, + $id, + $summit_name, + $change_details, + $discount_details, + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + return sprintf( + "Pre-Paid Discount Code '%s' (%d) for Summit '%s' with %s was deleted by user %s", + $code, + $id, + $summit_name, + $discount_details, + $this->getUserInfo() + ); + } + } catch (\Exception $ex) { + Log::warning("PrePaidSummitRegistrationDiscountCodeAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/app/Audit/ConcreteFormatters/SummitRefundPolicyTypeAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SummitRefundPolicyTypeAuditLogFormatter.php new file mode 100644 index 000000000..52c9e9fe3 --- /dev/null +++ b/app/Audit/ConcreteFormatters/SummitRefundPolicyTypeAuditLogFormatter.php @@ -0,0 +1,79 @@ +getName() ?? 'Unknown'; + $id = $subject->getId() ?? 'unknown'; + $summit = $subject->getSummit(); + $summit_name = $summit ? ($summit->getName() ?? 'Unknown Summit') : 'Unknown Summit'; + $days_before = $subject->getUntilXDaysBeforeEventStarts(); + $refund_rate = $subject->getRefundRate(); + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + return sprintf( + "Refund Policy '%s' (%d) created for Summit '%s': %s%% refund if cancelled %s days before event by user %s", + $name, + $id, + $summit_name, + ($refund_rate !== null ? sprintf("%.0f", $refund_rate) : 'N/A'), + ($days_before !== null ? $days_before : 'N/A'), + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $change_details = $this->buildChangeDetails($change_set); + return sprintf( + "Refund Policy '%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( + "Refund Policy '%s' (%d) for Summit '%s' (%s%% refund, %s days before) was deleted by user %s", + $name, + $id, + $summit_name, + ($refund_rate !== null ? sprintf("%.0f", $refund_rate) : 'N/A'), + ($days_before !== null ? $days_before : 'N/A'), + $this->getUserInfo() + ); + } + } catch (\Exception $ex) { + Log::warning("SummitRefundPolicyTypeAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/app/Audit/ConcreteFormatters/SummitTaxTypeAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SummitTaxTypeAuditLogFormatter.php new file mode 100644 index 000000000..aa73435b9 --- /dev/null +++ b/app/Audit/ConcreteFormatters/SummitTaxTypeAuditLogFormatter.php @@ -0,0 +1,79 @@ +getName() ?? 'Unknown'; + $id = $subject->getId() ?? 'unknown'; + $summit = $subject->getSummit(); + $summit_name = $summit ? ($summit->getName() ?? 'Unknown Summit') : 'Unknown Summit'; + $tax_id = $subject->getTaxId() ?? 'Not set'; + $rate = $subject->getRate(); + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + return sprintf( + "Tax Type '%s' (%d) created for Summit '%s': Tax ID %s, rate %s by user %s", + $name, + $id, + $summit_name, + $tax_id, + ($rate !== null ? sprintf("%.2f%%", $rate) : 'N/A'), + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $change_details = $this->buildChangeDetails($change_set); + return sprintf( + "Tax Type '%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( + "Tax Type '%s' (%d) for Summit '%s' (Tax ID: %s, rate: %s) was deleted by user %s", + $name, + $id, + $summit_name, + $tax_id, + ($rate !== null ? sprintf("%.2f%%", $rate) : 'N/A'), + $this->getUserInfo() + ); + } + } catch (\Exception $ex) { + Log::warning("SummitTaxTypeAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/app/Audit/ConcreteFormatters/SummitTicketTypeAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SummitTicketTypeAuditLogFormatter.php new file mode 100644 index 000000000..7395f8610 --- /dev/null +++ b/app/Audit/ConcreteFormatters/SummitTicketTypeAuditLogFormatter.php @@ -0,0 +1,86 @@ +getName() ?? 'Unknown'; + $id = $subject->getId() ?? 'unknown'; + $summit = $subject->getSummit(); + $summit_name = $summit ? ($summit->getName() ?? 'Unknown Summit') : 'Unknown Summit'; + $cost = $subject->getCost(); + $currency = $subject->getCurrency() ?? 'USD'; + $quantity_available = $subject->getQuantity2Sell(); + $audience = $subject->getAudience() ?? 'All'; + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + return sprintf( + "Ticket Type '%s' (%d) created for Summit '%s': price %s %s, %s available, audience: %s by user %s", + $name, + $id, + $summit_name, + ($cost !== null ? $cost : 'N/A'), + $currency, + ($quantity_available !== null ? $quantity_available : 'N/A'), + $audience, + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $change_details = $this->buildChangeDetails($change_set); + return sprintf( + "Ticket Type '%s' (%d) for Summit '%s' updated: %s by user %s", + $name, + $id, + $summit_name, + $change_details, + $this->getUserInfo() + ); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + $quantity_sold = $subject->getQuantitySold(); + return sprintf( + "Ticket Type '%s' (%d) for Summit '%s' with price %s %s (%s sold, %s available) was deleted by user %s", + $name, + $id, + $summit_name, + ($cost !== null ? $cost : 'N/A'), + $currency, + ($quantity_sold !== null ? $quantity_sold : 'N/A'), + ($quantity_available !== null ? $quantity_available : 'N/A'), + $this->getUserInfo() + ); + } + } catch (\Exception $ex) { + Log::warning("SummitTicketTypeAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/config/audit_log.php b/config/audit_log.php index 2e416a283..e897332f4 100644 --- a/config/audit_log.php +++ b/config/audit_log.php @@ -240,5 +240,21 @@ 'enabled' => true, 'strategy' => \App\Audit\ConcreteFormatters\ExtraQuestionTypeValueAuditLogFormatter::class, ], + \models\summit\SummitTaxType::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\SummitTaxTypeAuditLogFormatter::class, + ], + \models\summit\SummitRefundPolicyType::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\SummitRefundPolicyTypeAuditLogFormatter::class, + ], + \models\summit\SummitTicketType::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\SummitTicketTypeAuditLogFormatter::class, + ], + \models\summit\PrePaidSummitRegistrationDiscountCode::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\PrePaidSummitRegistrationDiscountCodeAuditLogFormatter::class, + ], ] ]; diff --git a/tests/OpenTelemetry/Formatters/PrePaidSummitRegistrationDiscountCodeAuditLogFormatterTest.php b/tests/OpenTelemetry/Formatters/PrePaidSummitRegistrationDiscountCodeAuditLogFormatterTest.php new file mode 100644 index 000000000..a1504eb12 --- /dev/null +++ b/tests/OpenTelemetry/Formatters/PrePaidSummitRegistrationDiscountCodeAuditLogFormatterTest.php @@ -0,0 +1,260 @@ +formatter_creation = new PrePaidSummitRegistrationDiscountCodeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION); + $this->formatter_update = new PrePaidSummitRegistrationDiscountCodeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_UPDATE); + $this->formatter_deletion = new PrePaidSummitRegistrationDiscountCodeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_DELETION); + + $this->audit_context = new AuditContext( + userId: self::USER_ID, + userEmail: self::USER_EMAIL, + userFirstName: self::USER_FIRST_NAME, + userLastName: self::USER_LAST_NAME + ); + } + + protected function tearDown(): void + { + Mockery::close(); + parent::tearDown(); + } + + public function testFormatCreationEventWithRate(): void + { + $code = $this->createMockCode( + self::DISCOUNT_CODE_RATE_EARLY, + self::RATE_EARLY, + self::AMOUNT_ZERO, + self::QUANTITY_EARLY, + true, + self::CREATOR_FIRST_1, + self::CREATOR_LAST_1 + ); + + $this->formatter_creation->setContext($this->audit_context); + $result = $this->formatter_creation->format($code, []); + + $this->assertNotNull($result); + $this->assertStringContainsString(self::DISCOUNT_CODE_RATE_EARLY, $result); + $this->assertStringContainsString(sprintf(self::EXPECTED_ID_FORMAT, self::MOCK_ID), $result); + $this->assertStringContainsString(self::EVENT_CREATED, $result); + $this->assertStringContainsString(self::SUMMIT_NAME, $result); + $this->assertStringContainsString(sprintf(self::EXPECTED_RATE_FORMAT, self::RATE_EARLY), $result); + $this->assertStringContainsString(sprintf(self::EXPECTED_QUANTITY_FORMAT, self::QUANTITY_EARLY), $result); + } + + public function testFormatCreationEventWithAmount(): void + { + $code = $this->createMockCode( + self::DISCOUNT_CODE_AMOUNT, + self::AMOUNT_ZERO, + self::AMOUNT_DISCOUNT, + self::QUANTITY_AMOUNT, + true, + self::CREATOR_FIRST_2, + self::CREATOR_LAST_2 + ); + + $this->formatter_creation->setContext($this->audit_context); + $result = $this->formatter_creation->format($code, []); + + $this->assertNotNull($result); + $this->assertStringContainsString(self::DISCOUNT_CODE_AMOUNT, $result); + $this->assertStringContainsString(sprintf(self::EXPECTED_ID_FORMAT, self::MOCK_ID), $result); + $this->assertStringContainsString(self::EVENT_CREATED, $result); + $this->assertStringContainsString(sprintf(self::EXPECTED_AMOUNT_FORMAT, self::AMOUNT_DISCOUNT), $result); + $this->assertStringContainsString(sprintf(self::EXPECTED_QUANTITY_FORMAT, self::QUANTITY_AMOUNT), $result); + } + + public function testFormatUpdateEvent(): void + { + $code = $this->createMockCode( + self::DISCOUNT_CODE_UPDATE, + self::RATE_UPDATE_NEW, + self::AMOUNT_ZERO, + self::QUANTITY_UPDATE_NEW, + true, + self::CREATOR_FIRST_3, + self::CREATOR_LAST_3 + ); + + $this->formatter_update->setContext($this->audit_context); + $result = $this->formatter_update->format($code, [ + 'rate' => [self::RATE_UPDATE_OLD, self::RATE_UPDATE_NEW], + 'quantity_available' => [self::QUANTITY_UPDATE_OLD, self::QUANTITY_UPDATE_NEW] + ]); + + $this->assertNotNull($result); + $this->assertStringContainsString(self::DISCOUNT_CODE_UPDATE, $result); + $this->assertStringContainsString(sprintf(self::EXPECTED_ID_FORMAT, self::MOCK_ID), $result); + $this->assertStringContainsString(self::EVENT_UPDATED, $result); + $this->assertStringContainsString(sprintf(self::EXPECTED_RATE_FORMAT, self::RATE_UPDATE_NEW), $result); + $this->assertStringContainsString(sprintf(self::EXPECTED_QUANTITY_FORMAT, self::QUANTITY_UPDATE_NEW), $result); + $this->assertStringContainsString(self::EVENT_CURRENT, $result); + } + + public function testFormatDeletionEvent(): void + { + $code = $this->createMockCode( + self::DISCOUNT_CODE_DELETE, + self::RATE_HIGH, + self::AMOUNT_ZERO, + self::QUANTITY_HIGH, + false, + self::CREATOR_FIRST_4, + self::CREATOR_LAST_4 + ); + + $this->formatter_deletion->setContext($this->audit_context); + $result = $this->formatter_deletion->format($code, []); + + $this->assertNotNull($result); + $this->assertStringContainsString(self::DISCOUNT_CODE_DELETE, $result); + $this->assertStringContainsString(sprintf(self::EXPECTED_ID_FORMAT, self::MOCK_ID), $result); + $this->assertStringContainsString(self::EVENT_DELETED, $result); + $this->assertStringContainsString(sprintf(self::EXPECTED_RATE_FORMAT, self::RATE_HIGH), $result); + $this->assertStringContainsString(sprintf(self::EXPECTED_QUANTITY_FORMAT, self::QUANTITY_HIGH), $result); + } + + public function testFormatInactiveCode(): void + { + $code = $this->createMockCode( + self::DISCOUNT_CODE_EXPIRED, + self::RATE_EXPIRED, + self::AMOUNT_ZERO, + self::QUANTITY_NONE, + false, + self::CREATOR_FIRST_5, + self::CREATOR_LAST_5 + ); + + $this->formatter_creation->setContext($this->audit_context); + $result = $this->formatter_creation->format($code, []); + + $this->assertNotNull($result); + $this->assertStringContainsString(self::DISCOUNT_CODE_EXPIRED, $result); + $this->assertStringContainsString(sprintf(self::EXPECTED_ID_FORMAT, self::MOCK_ID), $result); + $this->assertStringContainsString(self::EVENT_CREATED, $result); + $this->assertStringContainsString(sprintf(self::EXPECTED_RATE_FORMAT, self::RATE_EXPIRED), $result); + } + + public function testFormatInvalidSubject(): void + { + $invalid_subject = new \stdClass(); + + $result = $this->formatter_creation->format($invalid_subject, []); + + $this->assertNull($result); + } + + private function createMockCode( + string $code, + float $rate, + float $amount, + int $quantity_available, + bool $is_active, + string $creator_first, + string $creator_last + ): object { + $mock = Mockery::mock(PrePaidSummitRegistrationDiscountCode::class); + $mock->shouldReceive('getCode')->andReturn($code); + $mock->shouldReceive('getId')->andReturn(self::MOCK_ID); + $mock->shouldReceive('getRate')->andReturn($rate); + $mock->shouldReceive('getAmount')->andReturn($amount); + $mock->shouldReceive('getQuantityAvailable')->andReturn($quantity_available); + $mock->shouldReceive('isLive')->andReturn($is_active); + + $creator = Mockery::mock(\models\main\Member::class); + $creator->shouldReceive('getFirstName')->andReturn($creator_first); + $creator->shouldReceive('getLastName')->andReturn($creator_last); + $mock->shouldReceive('getCreatedBy')->andReturn($creator); + + $summit = Mockery::mock(\models\summit\Summit::class); + $summit->shouldReceive('getName')->andReturn(self::SUMMIT_NAME); + $mock->shouldReceive('getSummit')->andReturn($summit); + + return $mock; + } +} diff --git a/tests/OpenTelemetry/Formatters/SummitRefundPolicyTypeAuditLogFormatterTest.php b/tests/OpenTelemetry/Formatters/SummitRefundPolicyTypeAuditLogFormatterTest.php new file mode 100644 index 000000000..ccf5f7bf3 --- /dev/null +++ b/tests/OpenTelemetry/Formatters/SummitRefundPolicyTypeAuditLogFormatterTest.php @@ -0,0 +1,126 @@ +formatter_creation = new SummitRefundPolicyTypeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION); + $this->formatter_update = new SummitRefundPolicyTypeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_UPDATE); + $this->formatter_deletion = new SummitRefundPolicyTypeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_DELETION); + + $this->audit_context = new AuditContext( + userId: self::USER_ID, + userEmail: self::USER_EMAIL, + userFirstName: self::USER_FIRST_NAME, + userLastName: self::USER_LAST_NAME + ); + } + + protected function tearDown(): void + { + Mockery::close(); + parent::tearDown(); + } + + public function testFormatCreationEvent(): void + { + $policy = $this->createMockPolicy('Full Refund', 30, 100.0); + + $this->formatter_creation->setContext($this->audit_context); + $result = $this->formatter_creation->format($policy, []); + + $this->assertNotNull($result); + $this->assertStringContainsString("Full Refund", $result); + $this->assertStringContainsString("created", $result); + $this->assertStringContainsString("100", $result); + $this->assertStringContainsString("30", $result); + } + + public function testFormatUpdateEvent(): void + { + $policy = $this->createMockPolicy('Partial Refund', 14, 50.0); + + $this->formatter_update->setContext($this->audit_context); + $result = $this->formatter_update->format($policy, [ + 'until_x_days_before_event_starts' => [30, 14], + 'refund_rate' => [100.0, 50.0] + ]); + + $this->assertNotNull($result); + $this->assertStringContainsString("Partial Refund", $result); + $this->assertStringContainsString("updated", $result); + } + + public function testFormatDeletionEvent(): void + { + $policy = $this->createMockPolicy('No Refund', 0, 0.0); + + $this->formatter_deletion->setContext($this->audit_context); + $result = $this->formatter_deletion->format($policy, []); + + $this->assertNotNull($result); + $this->assertStringContainsString("No Refund", $result); + $this->assertStringContainsString("deleted", $result); + $this->assertStringContainsString("0", $result); + } + + public function testFormatInvalidSubject(): void + { + $invalid_subject = new \stdClass(); + + $result = $this->formatter_creation->format($invalid_subject, []); + + $this->assertNull($result); + } + + private function createMockPolicy(string $name, int $days, float $rate): object + { + $mock = Mockery::mock(SummitRefundPolicyType::class); + $mock->shouldReceive('getName')->andReturn($name); + $mock->shouldReceive('getId')->andReturn(self::MOCK_ID); + $mock->shouldReceive('getUntilXDaysBeforeEventStarts')->andReturn($days); + $mock->shouldReceive('getRefundRate')->andReturn($rate); + + $summit = Mockery::mock(\models\summit\Summit::class); + $summit->shouldReceive('getName')->andReturn(self::SUMMIT_NAME); + $mock->shouldReceive('getSummit')->andReturn($summit); + + return $mock; + } +} diff --git a/tests/OpenTelemetry/Formatters/SummitTaxTypeAuditLogFormatterTest.php b/tests/OpenTelemetry/Formatters/SummitTaxTypeAuditLogFormatterTest.php new file mode 100644 index 000000000..447a9102f --- /dev/null +++ b/tests/OpenTelemetry/Formatters/SummitTaxTypeAuditLogFormatterTest.php @@ -0,0 +1,144 @@ +formatter_creation = new SummitTaxTypeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION); + $this->formatter_update = new SummitTaxTypeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_UPDATE); + $this->formatter_deletion = new SummitTaxTypeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_DELETION); + + $this->audit_context = new AuditContext( + userId: self::USER_ID, + userEmail: self::USER_EMAIL, + userFirstName: self::USER_FIRST_NAME, + userLastName: self::USER_LAST_NAME + ); + } + + protected function tearDown(): void + { + Mockery::close(); + parent::tearDown(); + } + + public function testFormatCreationEvent(): void + { + $tax = $this->createMockTax('VAT', 'VAT-001', 21.0, 5); + + $this->formatter_creation->setContext($this->audit_context); + $result = $this->formatter_creation->format($tax, []); + + $this->assertNotNull($result); + $this->assertStringContainsString("VAT", $result); + $this->assertStringContainsString("created", $result); + $this->assertStringContainsString("21", $result); + } + + public function testFormatUpdateEvent(): void + { + $tax = $this->createMockTax('Sales Tax', 'ST-002', 8.0, 3); + + $this->formatter_update->setContext($this->audit_context); + $result = $this->formatter_update->format($tax, [ + 'rate' => [7.0, 8.0], + 'ticket_types' => [[], []] + ]); + + $this->assertNotNull($result); + $this->assertStringContainsString("Sales Tax", $result); + $this->assertStringContainsString("updated", $result); + } + + public function testFormatDeletionEvent(): void + { + $tax = $this->createMockTax('GST', 'GST-CA', 5.0, 2); + + $this->formatter_deletion->setContext($this->audit_context); + $result = $this->formatter_deletion->format($tax, []); + + $this->assertNotNull($result); + $this->assertStringContainsString("GST", $result); + $this->assertStringContainsString("deleted", $result); + $this->assertStringContainsString("5", $result); + } + + public function testFormatWithoutTicketTypes(): void + { + $tax = $this->createMockTax('Empty Tax', 'EMPTY', 10.0, 0); + + $this->formatter_creation->setContext($this->audit_context); + $result = $this->formatter_creation->format($tax, []); + + $this->assertNotNull($result); + $this->assertStringContainsString("Empty Tax", $result); + $this->assertStringContainsString("created", $result); + } + + public function testFormatInvalidSubject(): void + { + $invalid_subject = new \stdClass(); + + $result = $this->formatter_creation->format($invalid_subject, []); + + $this->assertNull($result); + } + + private function createMockTax(string $name, string $tax_id, float $rate, int $ticket_count): object + { + $mock = Mockery::mock(SummitTaxType::class); + $mock->shouldReceive('getName')->andReturn($name); + $mock->shouldReceive('getId')->andReturn(self::MOCK_ID); + $mock->shouldReceive('getTaxId')->andReturn($tax_id); + $mock->shouldReceive('getRate')->andReturn($rate); + + $tickets = new ArrayCollection(); + for ($i = 0; $i < $ticket_count; $i++) { + $tickets->add(Mockery::mock(\models\summit\SummitTicketType::class)); + } + $mock->shouldReceive('getTicketTypes')->andReturn($tickets); + + $summit = Mockery::mock(\models\summit\Summit::class); + $summit->shouldReceive('getName')->andReturn(self::SUMMIT_NAME); + $mock->shouldReceive('getSummit')->andReturn($summit); + + return $mock; + } +} diff --git a/tests/OpenTelemetry/Formatters/SummitTicketTypeAuditLogFormatterTest.php b/tests/OpenTelemetry/Formatters/SummitTicketTypeAuditLogFormatterTest.php new file mode 100644 index 000000000..6e6d8e36c --- /dev/null +++ b/tests/OpenTelemetry/Formatters/SummitTicketTypeAuditLogFormatterTest.php @@ -0,0 +1,147 @@ +formatter_creation = new SummitTicketTypeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION); + $this->formatter_update = new SummitTicketTypeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_UPDATE); + $this->formatter_deletion = new SummitTicketTypeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_DELETION); + + $this->audit_context = new AuditContext( + userId: self::USER_ID, + userEmail: self::USER_EMAIL, + userFirstName: self::USER_FIRST_NAME, + userLastName: self::USER_LAST_NAME + ); + } + + protected function tearDown(): void + { + Mockery::close(); + parent::tearDown(); + } + + public function testFormatCreationEvent(): void + { + $ticket_type = $this->createMockTicketType('VIP Pass', 150.00, 'USD', 100, 0); + + $this->formatter_creation->setContext($this->audit_context); + $result = $this->formatter_creation->format($ticket_type, []); + + $this->assertNotNull($result); + $this->assertStringContainsString('VIP Pass', $result); + $this->assertStringContainsString('created', $result); + $this->assertStringContainsString('USD', $result); + } + + public function testFormatUpdateEvent(): void + { + $ticket_type = $this->createMockTicketType('Standard Ticket', 99.99, 'USD', 500, 150); + + $this->formatter_update->setContext($this->audit_context); + $result = $this->formatter_update->format($ticket_type, [ + 'cost' => [49.99, 99.99], + 'quantity_2_sell' => [1000, 500] + ]); + + $this->assertNotNull($result); + $this->assertStringContainsString("Standard Ticket", $result); + $this->assertStringContainsString("updated", $result); + } + + public function testFormatDeletionEvent(): void + { + $ticket_type = $this->createMockTicketType('Early Bird', 79.99, 'EUR', 200, 200); + + $this->formatter_deletion->setContext($this->audit_context); + $result = $this->formatter_deletion->format($ticket_type, []); + + $this->assertNotNull($result); + $this->assertStringContainsString("Early Bird", $result); + $this->assertStringContainsString("deleted", $result); + $this->assertStringContainsString('EUR', $result); + } + + public function testFormatWithoutContext(): void + { + $ticket_type = $this->createMockTicketType('Test Ticket', 50.00, 'USD', 100, 0); + + $result = $this->formatter_creation->format($ticket_type, []); + + $this->assertNotNull($result); + $this->assertStringContainsString("Test Ticket", $result); + $this->assertStringContainsString("created", $result); + } + + public function testFormatInvalidSubject(): void + { + $invalid_subject = new \stdClass(); + + $result = $this->formatter_creation->format($invalid_subject, []); + + $this->assertNull($result); + } + + private function createMockTicketType( + string $name, + float $cost, + string $currency, + int $quantity, + int $sold + ): object { + $mock = Mockery::mock(SummitTicketType::class); + $mock->shouldReceive('getName')->andReturn($name); + $mock->shouldReceive('getId')->andReturn(self::MOCK_ID); + $mock->shouldReceive('getCost')->andReturn($cost); + $mock->shouldReceive('getCurrency')->andReturn($currency); + $mock->shouldReceive('getQuantity2Sell')->andReturn($quantity); + $mock->shouldReceive('getQuantitySold')->andReturn($sold); + $mock->shouldReceive('getAudience')->andReturn(self::AUDIENCE); + + $summit = Mockery::mock(\models\summit\Summit::class); + $summit->shouldReceive('getName')->andReturn(self::SUMMIT_NAME); + $mock->shouldReceive('getSummit')->andReturn($summit); + + return $mock; + } +}