Skip to content

Commit 6fda261

Browse files
committed
chore: in strict mode, match deeply
1 parent c6c7a66 commit 6fda261

File tree

2 files changed

+220
-2
lines changed

2 files changed

+220
-2
lines changed

src/Utils/Test/src/TraceStructureAssertionTrait.php

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,10 @@ private function compareSpans(array $expectedSpan, array $actualSpan, bool $stri
500500
sprintf('Span kinds do not match for span "%s"', $expectedSpan['name'])
501501
);
502502
}
503+
} elseif ($strict && isset($actualSpan['kind']) && $actualSpan['kind'] !== 0) {
504+
// In strict mode, if kind is not specified in expected span but exists in actual span (and is not default),
505+
// the test should fail
506+
Assert::fail(sprintf('Actual span has kind %d but expected span does not specify kind for span "%s"', $actualSpan['kind'], $expectedSpan['name']));
503507
}
504508

505509
// Compare attributes if specified
@@ -510,15 +514,25 @@ private function compareSpans(array $expectedSpan, array $actualSpan, bool $stri
510514
$strict,
511515
$expectedSpan['name']
512516
);
517+
} elseif ($strict && isset($actualSpan['attributes']) && !empty($actualSpan['attributes'])) {
518+
// In strict mode, if attributes are not specified in expected span but exist in actual span,
519+
// the test should fail
520+
Assert::fail(sprintf('Actual span has attributes but expected span does not specify attributes for span "%s"', $expectedSpan['name']));
513521
}
514522

515523
// Compare status if specified
516524
if (isset($expectedSpan['status'])) {
517525
$this->compareStatus(
518526
$expectedSpan['status'],
519527
$actualSpan['status'],
528+
$strict,
520529
$expectedSpan['name']
521530
);
531+
} elseif ($strict && isset($actualSpan['status']) &&
532+
($actualSpan['status']['code'] !== 0 || $actualSpan['status']['description'] !== '')) {
533+
// In strict mode, if status is not specified in expected span but exists in actual span (and is not default),
534+
// the test should fail
535+
Assert::fail(sprintf('Actual span has non-default status but expected span does not specify status for span "%s"', $expectedSpan['name']));
522536
}
523537

524538
// Compare events if specified
@@ -529,6 +543,15 @@ private function compareSpans(array $expectedSpan, array $actualSpan, bool $stri
529543
$strict,
530544
$expectedSpan['name']
531545
);
546+
} elseif ($strict && isset($actualSpan['events']) && !empty($actualSpan['events'])) {
547+
// In strict mode, if events are not specified in expected span but exist in actual span,
548+
// the test should fail
549+
Assert::fail(sprintf('Actual span has events but expected span does not specify events for span "%s"', $expectedSpan['name']));
550+
}
551+
552+
// In strict mode, check for children if not specified in expected span
553+
if ($strict && !isset($expectedSpan['children']) && isset($actualSpan['children']) && !empty($actualSpan['children'])) {
554+
Assert::fail(sprintf('Actual span has children but expected span does not specify children for span "%s"', $expectedSpan['name']));
532555
}
533556
}
534557

@@ -583,6 +606,20 @@ private function isConstraint($value): bool
583606
*/
584607
private function compareAttributes(array $expectedAttributes, array $actualAttributes, bool $strict, string $spanName): void
585608
{
609+
// In strict mode, verify that the number of attributes matches exactly
610+
if ($strict) {
611+
Assert::assertCount(
612+
count($expectedAttributes),
613+
$actualAttributes,
614+
sprintf(
615+
'Expected %d attributes, but found %d in span "%s"',
616+
count($expectedAttributes),
617+
count($actualAttributes),
618+
$spanName
619+
)
620+
);
621+
}
622+
586623
foreach ($expectedAttributes as $key => $expectedValue) {
587624
// Both in strict and non-strict mode, all expected attributes must be present
588625
Assert::assertArrayHasKey(
@@ -618,12 +655,26 @@ private function compareAttributes(array $expectedAttributes, array $actualAttri
618655
*
619656
* @param array $expectedStatus
620657
* @param array $actualStatus
658+
* @param bool $strict
621659
* @param string $spanName
622660
* @throws AssertionFailedError
623661
* @return void
624662
*/
625-
private function compareStatus(array $expectedStatus, array $actualStatus, string $spanName): void
663+
private function compareStatus(array $expectedStatus, array $actualStatus, bool $strict, string $spanName): void
626664
{
665+
// In strict mode, verify that all fields in expected status are also in actual status
666+
if ($strict) {
667+
// Check if code is specified in expected status
668+
if (!isset($expectedStatus['code']) && $actualStatus['code'] !== 0) {
669+
Assert::fail(sprintf('Actual status has non-default code but expected status does not specify code for span "%s"', $spanName));
670+
}
671+
672+
// Check if description is specified in expected status
673+
if (!isset($expectedStatus['description']) && $actualStatus['description'] !== '') {
674+
Assert::fail(sprintf('Actual status has description but expected status does not specify description for span "%s"', $spanName));
675+
}
676+
}
677+
627678
// Compare status code if specified
628679
if (isset($expectedStatus['code'])) {
629680
$expectedCode = $expectedStatus['code'];

src/Utils/Test/tests/Unit/TraceStructureAssertionTraitTest.php

Lines changed: 168 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,15 +188,22 @@ public function test_assert_trace_structure_with_strict_matching(): void
188188
// Assert the trace structure with non-strict matching (should pass)
189189
$this->assertTraceStructure($this->storage, $expectedStructure, false);
190190

191-
// Define the expected structure with all attributes
191+
// Define the expected structure with all attributes and fields
192192
$expectedStructureStrict = [
193193
[
194194
'name' => 'test-span',
195+
'kind' => 0, // Default kind
195196
'attributes' => [
196197
'attribute.one' => 'value1',
197198
'attribute.two' => 42,
198199
'attribute.three' => true,
199200
],
201+
'events' => [], // Empty events array
202+
'children' => [], // Empty children array
203+
'status' => [
204+
'code' => 'Unset', // Default status code
205+
'description' => '', // Default status description
206+
],
200207
],
201208
];
202209

@@ -682,6 +689,166 @@ public function test_trace_structure_diff_output_with_missing_nested_span(): voi
682689
}
683690
}
684691

692+
/**
693+
* Test that strict mode fails when actual span has extra attributes.
694+
*/
695+
public function test_assert_fails_with_extra_attributes_in_strict_mode(): void
696+
{
697+
$tracer = $this->tracerProvider->getTracer('test-tracer');
698+
699+
// Create a span with multiple attributes
700+
$span = $tracer->spanBuilder('test-span')
701+
->startSpan();
702+
703+
$span->setAttribute('attribute.one', 'value1');
704+
$span->setAttribute('attribute.two', 42);
705+
$span->setAttribute('attribute.three', true);
706+
707+
$span->end();
708+
709+
// Define expected structure with only a subset of attributes
710+
$expectedStructure = [
711+
[
712+
'name' => 'test-span',
713+
'attributes' => [
714+
'attribute.one' => 'value1',
715+
'attribute.two' => 42,
716+
],
717+
],
718+
];
719+
720+
// Expect assertion to fail in strict mode
721+
$this->expectException(\PHPUnit\Framework\AssertionFailedError::class);
722+
$this->expectExceptionMessageMatches('/No matching span found for expected span "test-span"/');
723+
724+
$this->assertTraceStructure($this->storage, $expectedStructure, true);
725+
}
726+
727+
/**
728+
* Test that strict mode fails when actual span has a kind but expected doesn't.
729+
*/
730+
public function test_assert_fails_with_extra_kind_in_strict_mode(): void
731+
{
732+
$tracer = $this->tracerProvider->getTracer('test-tracer');
733+
734+
// Create a span with a specific kind
735+
$span = $tracer->spanBuilder('test-span')
736+
->setSpanKind(SpanKind::KIND_SERVER)
737+
->startSpan();
738+
739+
$span->end();
740+
741+
// Define expected structure without kind
742+
$expectedStructure = [
743+
[
744+
'name' => 'test-span',
745+
],
746+
];
747+
748+
// Expect assertion to fail in strict mode
749+
$this->expectException(\PHPUnit\Framework\AssertionFailedError::class);
750+
$this->expectExceptionMessageMatches('/No matching span found for expected span "test-span"/');
751+
752+
$this->assertTraceStructure($this->storage, $expectedStructure, true);
753+
}
754+
755+
/**
756+
* Test that strict mode fails when actual span has events but expected doesn't.
757+
*/
758+
public function test_assert_fails_with_extra_events_in_strict_mode(): void
759+
{
760+
$tracer = $this->tracerProvider->getTracer('test-tracer');
761+
762+
// Create a span with events
763+
$span = $tracer->spanBuilder('test-span')
764+
->startSpan();
765+
766+
$span->addEvent('event-1');
767+
$span->addEvent('event-2');
768+
769+
$span->end();
770+
771+
// Define expected structure without events
772+
$expectedStructure = [
773+
[
774+
'name' => 'test-span',
775+
],
776+
];
777+
778+
// Expect assertion to fail in strict mode
779+
$this->expectException(\PHPUnit\Framework\AssertionFailedError::class);
780+
$this->expectExceptionMessageMatches('/No matching span found for expected span "test-span"/');
781+
782+
$this->assertTraceStructure($this->storage, $expectedStructure, true);
783+
}
784+
785+
/**
786+
* Test that strict mode fails when actual span has a non-default status but expected doesn't.
787+
*/
788+
public function test_assert_fails_with_extra_status_in_strict_mode(): void
789+
{
790+
$tracer = $this->tracerProvider->getTracer('test-tracer');
791+
792+
// Create a span with a non-default status
793+
$span = $tracer->spanBuilder('test-span')
794+
->startSpan();
795+
796+
$span->setStatus(StatusCode::STATUS_ERROR, 'Something went wrong');
797+
798+
$span->end();
799+
800+
// Define expected structure without status
801+
$expectedStructure = [
802+
[
803+
'name' => 'test-span',
804+
],
805+
];
806+
807+
// Expect assertion to fail in strict mode
808+
$this->expectException(\PHPUnit\Framework\AssertionFailedError::class);
809+
$this->expectExceptionMessageMatches('/No matching span found for expected span "test-span"/');
810+
811+
$this->assertTraceStructure($this->storage, $expectedStructure, true);
812+
}
813+
814+
/**
815+
* Test that strict mode fails when actual span has children but expected doesn't.
816+
*/
817+
public function test_assert_fails_with_extra_children_in_strict_mode(): void
818+
{
819+
$tracer = $this->tracerProvider->getTracer('test-tracer');
820+
821+
// Create a root span
822+
$rootSpan = $tracer->spanBuilder('root-span')
823+
->startSpan();
824+
825+
// Activate the root span
826+
$rootScope = $rootSpan->activate();
827+
828+
try {
829+
// Create a child span
830+
$childSpan = $tracer->spanBuilder('child-span')
831+
->startSpan();
832+
$childSpan->end();
833+
} finally {
834+
$rootSpan->end();
835+
$rootScope->detach();
836+
}
837+
838+
// Define expected structure without children
839+
$expectedStructure = [
840+
[
841+
'name' => 'root-span',
842+
],
843+
];
844+
845+
// Expect assertion to fail in strict mode
846+
$this->expectException(\PHPUnit\Framework\AssertionFailedError::class);
847+
$this->expectExceptionMessageMatches('/No matching span found for expected span "root-span"/');
848+
849+
$this->assertTraceStructure($this->storage, $expectedStructure, true);
850+
}
851+
685852
/**
686853
* Test asserting a trace structure using PHPUnit matchers.
687854
*/

0 commit comments

Comments
 (0)