Skip to content

Commit c745dbc

Browse files
feat: Add unit test for Audit Otlp Strategy
1 parent d47c9a8 commit c745dbc

5 files changed

Lines changed: 304 additions & 2 deletions

File tree

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ OTEL_TRACES_SAMPLER_PARENT=false
246246
# OTEL_INSTRUMENTATION_VIEW=true
247247
# OTEL_INSTRUMENTATION_LIVEWIRE=true
248248
# OTEL_INSTRUMENTATION_CONSOLE=true
249-
AUDIT_ELASTICSEARCH_INDEX=logs-audit
249+
OTEL_AUDIT_ELASTICSEARCH_INDEX=logs-audit
250250
# SWAGGER CONFIG
251251

252252
L5_SWAGGER_CONST_HOST=${APP_URL}

app/Audit/AuditLogOtlpStrategy.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function __construct()
3434

3535
$this->enabled = env('OTEL_SERVICE_ENABLED', false);
3636

37-
$this->elasticIndex = env('AUDIT_ELASTICSEARCH_INDEX', 'logs-audit');
37+
$this->elasticIndex = env('OTEL_AUDIT_ELASTICSEARCH_INDEX', 'logs-audit');
3838
}
3939

4040
public function audit($subject, array $change_set, string $event_type): void
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
3+
namespace Tests\OpenTelemetry;
4+
5+
use App\Audit\AuditLogOtlpStrategy;
6+
use OpenTelemetry\API\Trace\StatusCode;
7+
8+
class AuditEventTypesTest extends OpenTelemetryTestCase
9+
{
10+
private AuditLogOtlpStrategy $auditStrategy;
11+
12+
protected function setUp(): void
13+
{
14+
parent::setUp();
15+
16+
$this->auditStrategy = $this->app->make(AuditLogOtlpStrategy::class);
17+
}
18+
19+
public function testEntityCreationAudit(): void
20+
{
21+
if (!$this->isOpenTelemetryEnabled()) {
22+
$this->markTestSkipped('OpenTelemetry is disabled');
23+
}
24+
25+
$tracer = $this->app->make(\OpenTelemetry\API\Trace\TracerInterface::class);
26+
$span = $tracer->spanBuilder('test.audit.creation')->startSpan();
27+
$spanScope = $span->activate();
28+
29+
try {
30+
$mockEntity = (object) ['id' => 999, 'title' => 'Test Entity'];
31+
$data = ['title' => 'New Entity', 'type' => 'test'];
32+
33+
$this->auditStrategy->audit(
34+
$mockEntity,
35+
$data,
36+
AuditLogOtlpStrategy::EVENT_ENTITY_CREATION
37+
);
38+
39+
$span->setStatus(StatusCode::STATUS_OK, 'Creation audit completed');
40+
$this->assertTrue(true);
41+
} catch (\Exception $e) {
42+
$span->recordException($e);
43+
throw $e;
44+
} finally {
45+
$span->end();
46+
$spanScope->detach();
47+
}
48+
}
49+
50+
public function testEntityUpdateAudit(): void
51+
{
52+
if (!$this->isOpenTelemetryEnabled()) {
53+
$this->markTestSkipped('OpenTelemetry is disabled');
54+
}
55+
56+
$tracer = $this->app->make(\OpenTelemetry\API\Trace\TracerInterface::class);
57+
$span = $tracer->spanBuilder('test.audit.update')->startSpan();
58+
$spanScope = $span->activate();
59+
60+
try {
61+
$mockEntity = (object) ['id' => 999, 'title' => 'Test Entity'];
62+
$data = ['title' => ['Old Title', 'New Title']];
63+
64+
$this->auditStrategy->audit(
65+
$mockEntity,
66+
$data,
67+
AuditLogOtlpStrategy::EVENT_ENTITY_UPDATE
68+
);
69+
70+
$span->setStatus(StatusCode::STATUS_OK, 'Update audit completed');
71+
$this->assertTrue(true);
72+
} catch (\Exception $e) {
73+
$span->recordException($e);
74+
throw $e;
75+
} finally {
76+
$span->end();
77+
$spanScope->detach();
78+
}
79+
}
80+
81+
public function testEntityDeletionAudit(): void
82+
{
83+
if (!$this->isOpenTelemetryEnabled()) {
84+
$this->markTestSkipped('OpenTelemetry is disabled');
85+
}
86+
87+
$tracer = $this->app->make(\OpenTelemetry\API\Trace\TracerInterface::class);
88+
$span = $tracer->spanBuilder('test.audit.deletion')->startSpan();
89+
$spanScope = $span->activate();
90+
91+
try {
92+
$mockEntity = (object) ['id' => 999, 'title' => 'Test Entity'];
93+
$data = ['deleted_id' => 999, 'reason' => 'Test cleanup'];
94+
95+
$this->auditStrategy->audit(
96+
$mockEntity,
97+
$data,
98+
AuditLogOtlpStrategy::EVENT_ENTITY_DELETION
99+
);
100+
101+
$span->setStatus(StatusCode::STATUS_OK, 'Deletion audit completed');
102+
$this->assertTrue(true);
103+
} catch (\Exception $e) {
104+
$span->recordException($e);
105+
throw $e;
106+
} finally {
107+
$span->end();
108+
$spanScope->detach();
109+
}
110+
}
111+
112+
/**
113+
* Check if OpenTelemetry is enabled
114+
*/
115+
private function isOpenTelemetryEnabled(): bool
116+
{
117+
return getenv('OTEL_SERVICE_ENABLED') === 'true';
118+
}
119+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
<?php
2+
3+
/**
4+
* Test suite for verifying OpenTelemetry audit strategy integration.
5+
*
6+
* This test class validates that the AuditLogOtlpStrategy correctly processes
7+
* simulated entity changes and generates appropriate telemetry spans and events
8+
* without persisting any actual data to the database. Uses mocks to avoid
9+
* dependencies on external OpenTelemetry collectors.
10+
*/
11+
12+
namespace Tests\OpenTelemetry;
13+
14+
use App\Audit\AuditLogOtlpStrategy;
15+
use App\Models\Foundation\Main\IGroup;
16+
use Tests\InsertMemberTestData;
17+
use Tests\InsertSummitTestData;
18+
use OpenTelemetry\API\Trace\StatusCode;
19+
use OpenTelemetry\API\Trace\TracerInterface;
20+
21+
class AuditOtlpStrategyTest extends OpenTelemetryTestCase
22+
{
23+
use InsertSummitTestData;
24+
use InsertMemberTestData;
25+
26+
private AuditLogOtlpStrategy $auditStrategy;
27+
28+
protected function setUp(): void
29+
{
30+
parent::setUp();
31+
32+
self::insertMemberTestData(IGroup::TrackChairs);
33+
self::$defaultMember = self::$member;
34+
self::insertSummitTestData();
35+
36+
$this->auditStrategy = $this->app->make(AuditLogOtlpStrategy::class);
37+
}
38+
39+
protected function tearDown(): void
40+
{
41+
self::clearSummitTestData();
42+
self::clearMemberTestData();
43+
parent::tearDown();
44+
}
45+
46+
public function testAuditSummitChangeWithOtlp(): void
47+
{
48+
$this->skipIfOpenTelemetryDisabled();
49+
50+
$tracer = $this->app->make(TracerInterface::class);
51+
$span = $tracer->spanBuilder('test.audit.summit_change')->startSpan();
52+
$spanScope = $span->activate();
53+
54+
try {
55+
$span->addEvent('test.started', [
56+
'summit_id' => self::$summit->getId(),
57+
'summit_name' => self::$summit->getName()
58+
]);
59+
60+
$simulatedChangeSet = $this->createSummitChangeSet();
61+
62+
$this->auditStrategy->audit(
63+
self::$summit,
64+
$simulatedChangeSet,
65+
AuditLogOtlpStrategy::EVENT_ENTITY_UPDATE
66+
);
67+
68+
$span->setStatus(StatusCode::STATUS_OK, 'Summit audit completed');
69+
$this->assertTrue(true);
70+
71+
} catch (\Exception $e) {
72+
$span->recordException($e);
73+
$span->setStatus(StatusCode::STATUS_ERROR, $e->getMessage());
74+
throw $e;
75+
} finally {
76+
$span->end();
77+
$spanScope->detach();
78+
}
79+
}
80+
81+
public function testAuditSummitEventChangeWithOtlp(): void
82+
{
83+
$this->skipIfOpenTelemetryDisabled();
84+
85+
$tracer = $this->app->make(TracerInterface::class);
86+
$span = $tracer->spanBuilder('test.audit.summit_event_change')->startSpan();
87+
$spanScope = $span->activate();
88+
89+
try {
90+
$summitEvent = self::$summit->getEvents()[0];
91+
$simulatedChangeSet = $this->createSummitEventChangeSet($summitEvent);
92+
93+
$this->auditStrategy->audit(
94+
$summitEvent,
95+
$simulatedChangeSet,
96+
AuditLogOtlpStrategy::EVENT_ENTITY_UPDATE
97+
);
98+
99+
$span->setStatus(StatusCode::STATUS_OK, 'SummitEvent audit completed');
100+
$this->assertTrue(true);
101+
102+
} catch (\Exception $e) {
103+
$span->recordException($e);
104+
$span->setStatus(StatusCode::STATUS_ERROR, $e->getMessage());
105+
throw $e;
106+
} finally {
107+
$span->end();
108+
$spanScope->detach();
109+
}
110+
}
111+
112+
public function testAuditStrategyWithoutActiveSpan(): void
113+
{
114+
$this->skipIfOpenTelemetryDisabled();
115+
116+
$simulatedChangeSet = ['name' => ['Old Name', 'New Name']];
117+
118+
$this->auditStrategy->audit(
119+
self::$summit,
120+
$simulatedChangeSet,
121+
AuditLogOtlpStrategy::EVENT_ENTITY_UPDATE
122+
);
123+
124+
$this->assertTrue(true);
125+
}
126+
127+
public function testAuditStrategyWithEmptyChangeSet(): void
128+
{
129+
$this->skipIfOpenTelemetryDisabled();
130+
131+
$tracer = $this->app->make(TracerInterface::class);
132+
$span = $tracer->spanBuilder('test.audit.empty_changeset')->startSpan();
133+
$spanScope = $span->activate();
134+
135+
try {
136+
$this->auditStrategy->audit(
137+
self::$summit,
138+
[],
139+
AuditLogOtlpStrategy::EVENT_ENTITY_UPDATE
140+
);
141+
142+
$span->setStatus(StatusCode::STATUS_OK, 'Empty changeset audit completed');
143+
$this->assertTrue(true);
144+
145+
} catch (\Exception $e) {
146+
$span->recordException($e);
147+
$span->setStatus(StatusCode::STATUS_ERROR, $e->getMessage());
148+
throw $e;
149+
} finally {
150+
$span->end();
151+
$spanScope->detach();
152+
}
153+
}
154+
155+
private function skipIfOpenTelemetryDisabled(): void
156+
{
157+
if (!$this->isOpenTelemetryEnabled()) {
158+
$this->markTestSkipped('OpenTelemetry is disabled');
159+
}
160+
}
161+
162+
private function createSummitChangeSet(): array
163+
{
164+
return [
165+
'name' => [self::$summit->getName(), self::$summit->getName() . ' [TEST]'],
166+
'description' => ['Original', 'Updated for test']
167+
];
168+
}
169+
170+
private function createSummitEventChangeSet($summitEvent): array
171+
{
172+
return [
173+
'title' => [$summitEvent->getTitle(), $summitEvent->getTitle() . ' [TEST]']
174+
];
175+
}
176+
177+
private function isOpenTelemetryEnabled(): bool
178+
{
179+
return getenv('OTEL_SERVICE_ENABLED') === 'true';
180+
}
181+
}

tests/OpenTelemetry/OpenTelemetryTestCase.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ protected function setOpenTelemetryEnvironmentVariables(): void
5555
putenv('OTEL_INSTRUMENTATION_CONSOLE=false');
5656
putenv('OTEL_TRACES_EXPORTER=none');
5757
putenv('OTEL_METRICS_EXPORTER=none');
58+
putenv('OTEL_LOGS_EXPORTER=none');
59+
5860
}
5961

6062
protected function tearDown(): void

0 commit comments

Comments
 (0)