Skip to content

Commit 2fd01f5

Browse files
committed
test(audit): cover no-op update suppression end to end
Add PresentationEventApiAuditLogFormatterTest with the change set a summit-admin "save & publish" produces on a presentation (same-instant start/end/published_date instances plus attending_media false -> 0): the republish alone is suppressed, a real change (show_sponsors, moved start_date) is reported without the noise, and publish/unpublish transitions of published_date (null <-> DateTime) are still logged. Add a strategy-level case to AuditOtlpStrategyTest asserting that a change set with no meaningful change does not dispatch EmitAuditLogJob.
1 parent 07512cf commit 2fd01f5

2 files changed

Lines changed: 158 additions & 0 deletions

File tree

‎tests/OpenTelemetry/AuditOtlpStrategyTest.php‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,4 +250,32 @@ public function testAuditSummitEntityPopulatesSummitIdCorrectly(): void
250250
return true;
251251
});
252252
}
253+
254+
/**
255+
* Doctrine reports a change when a fresh DateTime instance or a type-coerced scalar
256+
* is assigned with the same value; the formatter returns null for such a change set
257+
* and the strategy must not emit an audit job for it.
258+
*/
259+
public function testAuditWithNoMeaningfulChangeDoesNotDispatchJob(): void
260+
{
261+
$this->skipIfOpenTelemetryDisabled();
262+
263+
Queue::fake();
264+
265+
$ctx = $this->createAuditContext();
266+
$simulatedChangeSet = [
267+
'name' => [self::$summit->getName(), self::$summit->getName()],
268+
'begin_date' => [new \DateTime('2026-10-12 09:00:00'), new \DateTime('2026-10-12 09:00:00')],
269+
'active' => [true, 1],
270+
];
271+
272+
$this->auditStrategy->audit(
273+
self::$summit,
274+
$simulatedChangeSet,
275+
AuditLogOtlpStrategy::EVENT_ENTITY_UPDATE,
276+
$ctx
277+
);
278+
279+
Queue::assertNotPushed(EmitAuditLogJob::class);
280+
}
253281
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<?php namespace Tests\OpenTelemetry\Formatters;
2+
/**
3+
* Copyright 2026 OpenStack Foundation
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
**/
14+
15+
use App\Audit\ConcreteFormatters\PresentationFormatters\PresentationEventApiAuditLogFormatter;
16+
use App\Audit\Interfaces\IAuditStrategy;
17+
use Mockery;
18+
use models\summit\Presentation;
19+
use Tests\OpenTelemetry\Formatters\Support\AuditContextBuilder;
20+
use Tests\TestCase;
21+
22+
/**
23+
* Covers the change set a "save & publish" from summit-admin produces on a presentation:
24+
* updateEventDates() and publish() assign fresh DateTime instances (same instant) and
25+
* PresentationFactory writes int 0 over attending_media=false, none of which is a real change.
26+
*/
27+
class PresentationEventApiAuditLogFormatterTest extends TestCase
28+
{
29+
private const MOCK_ID = 9643;
30+
private const TITLE = 'Rack-Scale AI Systems';
31+
32+
private PresentationEventApiAuditLogFormatter $formatter_update;
33+
34+
protected function setUp(): void
35+
{
36+
parent::setUp();
37+
38+
$this->formatter_update = new PresentationEventApiAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_UPDATE);
39+
$this->formatter_update->setContext(AuditContextBuilder::default()->build());
40+
}
41+
42+
protected function tearDown(): void
43+
{
44+
Mockery::close();
45+
parent::tearDown();
46+
}
47+
48+
private function createMockPresentation(): object
49+
{
50+
$mock = Mockery::mock(Presentation::class);
51+
$mock->shouldReceive('getTitle')->andReturn(self::TITLE);
52+
$mock->shouldReceive('getId')->andReturn(self::MOCK_ID);
53+
$mock->shouldReceive('getCreator')->andReturn(null);
54+
$mock->shouldReceive('getCategory')->andReturn(null);
55+
$mock->shouldReceive('getSelectionPlan')->andReturn(null);
56+
57+
return $mock;
58+
}
59+
60+
/**
61+
* The change set Doctrine reports for a republish without edits.
62+
*/
63+
private function republishNoiseChangeSet(): array
64+
{
65+
return [
66+
'start_date' => [new \DateTime('2026-10-12 21:30:00'), new \DateTime('2026-10-12 21:30:00')],
67+
'end_date' => [new \DateTime('2026-10-12 21:50:00'), new \DateTime('2026-10-12 21:50:00')],
68+
'published_date' => [new \DateTime('2026-09-24 00:44:26'), new \DateTime('2026-09-24 00:44:26')],
69+
'attending_media' => [false, 0],
70+
];
71+
}
72+
73+
public function testRepublishWithoutEditsIsSuppressed(): void
74+
{
75+
$result = $this->formatter_update->format($this->createMockPresentation(), $this->republishNoiseChangeSet());
76+
77+
$this->assertNull($result);
78+
}
79+
80+
public function testRealChangeSurvivesRepublishNoise(): void
81+
{
82+
$change_set = $this->republishNoiseChangeSet();
83+
$change_set['show_sponsors'] = [false, true];
84+
85+
$result = $this->formatter_update->format($this->createMockPresentation(), $change_set);
86+
87+
$this->assertNotNull($result);
88+
$this->assertStringContainsString("Presentation '" . self::TITLE . "' (" . self::MOCK_ID . ") updated: 1 field(s) modified:", $result);
89+
$this->assertStringContainsString('Property "show_sponsors" has changed from "false" to "true"', $result);
90+
$this->assertStringNotContainsString('start_date', $result);
91+
$this->assertStringNotContainsString('end_date', $result);
92+
$this->assertStringNotContainsString('published_date', $result);
93+
$this->assertStringNotContainsString('attending_media', $result);
94+
}
95+
96+
public function testPublishingSetsPublishedDateAndIsLogged(): void
97+
{
98+
$result = $this->formatter_update->format($this->createMockPresentation(), [
99+
'published_date' => [null, new \DateTime('2026-09-24 00:44:26')],
100+
'published' => [false, true],
101+
]);
102+
103+
$this->assertNotNull($result);
104+
$this->assertStringContainsString('2 field(s) modified', $result);
105+
$this->assertStringContainsString('Property "published_date" has changed from "null" to "2026-09-24 00:44:26"', $result);
106+
$this->assertStringContainsString('Property "published" has changed from "false" to "true"', $result);
107+
}
108+
109+
public function testUnpublishingClearsPublishedDateAndIsLogged(): void
110+
{
111+
$result = $this->formatter_update->format($this->createMockPresentation(), [
112+
'published_date' => [new \DateTime('2026-09-24 00:44:26'), null],
113+
]);
114+
115+
$this->assertNotNull($result);
116+
$this->assertStringContainsString('Property "published_date" has changed from "2026-09-24 00:44:26" to "null"', $result);
117+
}
118+
119+
public function testMovedStartDateIsLogged(): void
120+
{
121+
$change_set = $this->republishNoiseChangeSet();
122+
$change_set['start_date'] = [new \DateTime('2026-10-12 21:30:00'), new \DateTime('2026-10-12 22:00:00')];
123+
124+
$result = $this->formatter_update->format($this->createMockPresentation(), $change_set);
125+
126+
$this->assertNotNull($result);
127+
$this->assertStringContainsString('1 field(s) modified', $result);
128+
$this->assertStringContainsString('Property "start_date" has changed from "2026-10-12 21:30:00" to "2026-10-12 22:00:00"', $result);
129+
}
130+
}

0 commit comments

Comments
 (0)