Skip to content

Commit af5d4e6

Browse files
committed
feat(context): Add API test coverage
1 parent 28d6fd6 commit af5d4e6

File tree

4 files changed

+286
-11
lines changed

4 files changed

+286
-11
lines changed

components/context/src/main/java/datadog/context/propagation/Concern.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class Concern {
1919
* @param name the concern name, for debugging purpose only.
2020
* @return The created concern.
2121
*/
22-
public static Concern create(String name) {
22+
public static Concern named(String name) {
2323
return new Concern(name, DEFAULT_PRIORITY);
2424
}
2525

@@ -31,13 +31,15 @@ public static Concern create(String name) {
3131
* {@link #DEFAULT_PRIORITY}),
3232
* @return The created concern.
3333
*/
34-
public static Concern createWithPriority(String name, int priority) {
34+
public static Concern withPriority(String name, int priority) {
3535
return new Concern(name, priority);
3636
}
3737

3838
private Concern(String name, int priority) {
3939
requireNonNull(name, "Concern name cannot be null");
40-
assert priority >= 0 : "Concern priority cannot be negative";
40+
if (priority < 0) {
41+
throw new IllegalArgumentException("Concern priority cannot be negative");
42+
}
4143
this.name = name;
4244
this.priority = priority;
4345
}

components/context/src/main/java/datadog/context/propagation/Propagators.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
public final class Propagators {
1010
private static final Map<Concern, Propagator> PROPAGATORS = synchronizedMap(new HashMap<>());
1111
private static volatile Propagator defaultPropagator = null;
12-
private static volatile boolean isDefaultPropagatorSet = false;
12+
private static volatile boolean defaultPropagatorSet = false;
1313

1414
private Propagators() {}
1515

@@ -19,14 +19,14 @@ private Propagators() {}
1919
* @return The default propagator.
2020
*/
2121
public static Propagator defaultPropagator() {
22-
if (!isDefaultPropagatorSet) {
22+
if (!defaultPropagatorSet) {
2323
Propagator[] propagatorsByPriority =
2424
PROPAGATORS.entrySet().stream()
2525
.sorted(comparingInt(entry -> entry.getKey().priority()))
2626
.map(Map.Entry::getValue)
2727
.toArray(Propagator[]::new);
2828
defaultPropagator = composite(propagatorsByPriority);
29-
isDefaultPropagatorSet = true;
29+
defaultPropagatorSet = true;
3030
}
3131
return defaultPropagator;
3232
}
@@ -37,21 +37,21 @@ public static Propagator defaultPropagator() {
3737
* @param concern the concern to get propagator for.
3838
* @return the related propagator if registered, a {@link #noop()} propagator otherwise.
3939
*/
40-
public static Propagator propagatorFor(Concern concern) {
40+
public static Propagator forConcern(Concern concern) {
4141
return PROPAGATORS.getOrDefault(concern, NoopPropagator.INSTANCE);
4242
}
4343

4444
/**
45-
* Gets the propagator for given concerns.
45+
* Gets the propagator for the given concerns.
4646
*
4747
* @param concerns the concerns to get propagators for.
4848
* @return A propagator that will apply the concern propagators if registered, in the given
4949
* concern order.
5050
*/
51-
public static Propagator propagatorsFor(Concern... concerns) {
51+
public static Propagator forConcerns(Concern... concerns) {
5252
Propagator[] propagators = new Propagator[concerns.length];
5353
for (int i = 0; i < concerns.length; i++) {
54-
propagators[i] = propagatorFor(concerns[i]);
54+
propagators[i] = forConcern(concerns[i]);
5555
}
5656
return composite(propagators);
5757
}
@@ -72,7 +72,13 @@ public static Propagator noop() {
7272
* @return the composite propagator that will apply the propagators in their given order.
7373
*/
7474
public static Propagator composite(Propagator... propagators) {
75-
return new CompositePropagator(propagators);
75+
if (propagators.length == 0) {
76+
return NoopPropagator.INSTANCE;
77+
} else if (propagators.length == 1) {
78+
return propagators[0];
79+
} else {
80+
return new CompositePropagator(propagators);
81+
}
7682
}
7783

7884
/**
@@ -83,5 +89,12 @@ public static Propagator composite(Propagator... propagators) {
8389
*/
8490
public static void register(Concern concern, Propagator propagator) {
8591
PROPAGATORS.put(concern, propagator);
92+
defaultPropagatorSet = false;
93+
}
94+
95+
/** Clear all registered propagators. For testing purpose only. */
96+
static void reset() {
97+
PROPAGATORS.clear();
98+
defaultPropagatorSet = false;
8699
}
87100
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package datadog.context.propagation;
2+
3+
import static datadog.context.propagation.Concern.DEFAULT_PRIORITY;
4+
import static org.junit.jupiter.api.Assertions.*;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
class ConcernTest {
9+
@Test
10+
void testNamed() {
11+
assertThrows(
12+
NullPointerException.class,
13+
() -> Concern.named(null),
14+
"Should not create null named concern");
15+
assertNotNull(Concern.named("name"));
16+
}
17+
18+
@Test
19+
void testWithPriority() {
20+
assertThrows(
21+
NullPointerException.class,
22+
() -> Concern.withPriority(null, DEFAULT_PRIORITY),
23+
"Should not create null named concern");
24+
assertThrows(
25+
IllegalArgumentException.class,
26+
() -> Concern.withPriority("name", -1),
27+
"Should not create negative priority concern");
28+
assertNotNull(Concern.withPriority("high-priority", DEFAULT_PRIORITY - 10));
29+
assertNotNull(Concern.withPriority("low-priority", DEFAULT_PRIORITY + 10));
30+
}
31+
32+
@Test
33+
void testName() {
34+
String debugName = "name";
35+
Concern concern = Concern.named(debugName);
36+
assertEquals(debugName, concern.toString(), "Concern name mismatch");
37+
}
38+
}
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
package datadog.context.propagation;
2+
3+
import static datadog.context.Context.root;
4+
import static datadog.context.propagation.Concern.DEFAULT_PRIORITY;
5+
import static java.util.Objects.requireNonNull;
6+
import static org.junit.jupiter.api.Assertions.assertEquals;
7+
import static org.junit.jupiter.api.Assertions.assertFalse;
8+
import static org.junit.jupiter.api.Assertions.assertNotNull;
9+
import static org.junit.jupiter.api.Assertions.assertTrue;
10+
11+
import datadog.context.Context;
12+
import datadog.context.ContextKey;
13+
import java.util.HashMap;
14+
import java.util.Map;
15+
import java.util.function.BiConsumer;
16+
import javax.annotation.Nullable;
17+
import org.junit.jupiter.api.AfterEach;
18+
import org.junit.jupiter.api.BeforeEach;
19+
import org.junit.jupiter.api.Test;
20+
21+
class PropagatorsTest {
22+
static final MapCarrierAccessor ACCESSOR = new MapCarrierAccessor();
23+
24+
static final Concern TRACING = Concern.named("tracing");
25+
static final ContextKey<String> TRACING_KEY = ContextKey.named("tracing");
26+
static final Propagator TRACING_PROPAGATOR = new BasicPropagator(TRACING_KEY, "tracing");
27+
28+
static final Concern IAST = Concern.named("iast");
29+
static final ContextKey<String> IAST_KEY = ContextKey.named("iast");
30+
static final Propagator IAST_PROPAGATOR = new BasicPropagator(IAST_KEY, "iast");
31+
32+
static final Concern DEBUGGER = Concern.withPriority("debugger", DEFAULT_PRIORITY - 10);
33+
static final ContextKey<String> DEBUGGER_KEY = ContextKey.named("debugger");
34+
static final DependentPropagator DEBUGGER_PROPAGATOR =
35+
new DependentPropagator(DEBUGGER_KEY, "debugger", TRACING_KEY);
36+
37+
static final Concern PROFILING = Concern.withPriority("profiling", DEFAULT_PRIORITY + 10);
38+
static final ContextKey<String> PROFILING_KEY = ContextKey.named("profiling");
39+
static final DependentPropagator PROFILING_PROPAGATOR =
40+
new DependentPropagator(PROFILING_KEY, "profiling", TRACING_KEY);
41+
42+
static final Context CONTEXT =
43+
root()
44+
.with(TRACING_KEY, "sampled")
45+
.with(IAST_KEY, "standalone")
46+
.with(DEBUGGER_KEY, "debug")
47+
.with(PROFILING_KEY, "profile");
48+
49+
static class MapCarrierAccessor
50+
implements CarrierSetter<Map<String, String>>, CarrierVisitor<Map<String, String>> {
51+
@Override
52+
public void set(@Nullable Map<String, String> carrier, String key, String value) {
53+
if (carrier != null && key != null) {
54+
carrier.put(key, value);
55+
}
56+
}
57+
58+
@Override
59+
public void forEachKeyValue(Map<String, String> carrier, BiConsumer<String, String> visitor) {
60+
carrier.forEach(visitor);
61+
}
62+
}
63+
64+
static class BasicPropagator implements Propagator {
65+
private final ContextKey<String> contextKey;
66+
private final String carrierKey;
67+
68+
public BasicPropagator(ContextKey<String> contextKey, String carrierKey) {
69+
this.contextKey = requireNonNull(contextKey);
70+
this.carrierKey = requireNonNull(carrierKey);
71+
}
72+
73+
@Override
74+
public <C> void inject(Context context, C carrier, CarrierSetter<C> setter) {
75+
String value = context.get(this.contextKey);
76+
if (value != null) {
77+
setter.set(carrier, this.carrierKey, value);
78+
}
79+
}
80+
81+
@Override
82+
public <C> Context extract(Context context, C carrier, CarrierVisitor<C> visitor) {
83+
String[] valueRef = new String[1];
84+
visitor.forEachKeyValue(
85+
carrier,
86+
(key, value) -> {
87+
if (this.carrierKey.equals(key)) {
88+
valueRef[0] = value;
89+
}
90+
});
91+
if (valueRef[0] != null) {
92+
context = context.with(this.contextKey, valueRef[0]);
93+
}
94+
return context;
95+
}
96+
}
97+
98+
static class DependentPropagator extends BasicPropagator implements Propagator {
99+
private final ContextKey<String> requiredContextKey;
100+
private boolean keyFound;
101+
102+
public DependentPropagator(
103+
ContextKey<String> contextKey, String carrierKey, ContextKey<String> requiredContextKey) {
104+
super(contextKey, carrierKey);
105+
this.requiredContextKey = requiredContextKey;
106+
this.keyFound = false;
107+
}
108+
109+
@Override
110+
public <C> Context extract(Context context, C carrier, CarrierVisitor<C> visitor) {
111+
this.keyFound = context.get(this.requiredContextKey) != null;
112+
return super.extract(context, carrier, visitor);
113+
}
114+
115+
public void reset() {
116+
this.keyFound = false;
117+
}
118+
}
119+
120+
@BeforeEach
121+
@AfterEach
122+
void resetPropagators() {
123+
Propagators.reset();
124+
DEBUGGER_PROPAGATOR.reset();
125+
PROFILING_PROPAGATOR.reset();
126+
}
127+
128+
@Test
129+
void testDefaultPropagator() {
130+
Propagator noopPropagator = Propagators.defaultPropagator();
131+
assertNotNull(
132+
noopPropagator, "Default propagator should not be null when no propagator is registered");
133+
assertInjectExtractContext(CONTEXT, noopPropagator);
134+
135+
Propagators.register(TRACING, TRACING_PROPAGATOR);
136+
Propagator single = Propagators.defaultPropagator();
137+
assertInjectExtractContext(CONTEXT, single, TRACING_KEY);
138+
139+
Propagators.register(IAST, IAST_PROPAGATOR);
140+
Propagators.register(DEBUGGER, DEBUGGER_PROPAGATOR);
141+
Propagators.register(PROFILING, PROFILING_PROPAGATOR);
142+
Propagator composite = Propagators.defaultPropagator();
143+
assertInjectExtractContext(
144+
CONTEXT, composite, TRACING_KEY, IAST_KEY, DEBUGGER_KEY, PROFILING_KEY);
145+
assertFalse(
146+
DEBUGGER_PROPAGATOR.keyFound,
147+
"Debugger propagator should have run before tracing propagator");
148+
assertTrue(
149+
PROFILING_PROPAGATOR.keyFound,
150+
"Profiling propagator should have run after tracing propagator");
151+
152+
Propagator cached = Propagators.defaultPropagator();
153+
assertEquals(composite, cached, "default propagator should be cached");
154+
}
155+
156+
@Test
157+
void testForConcern() {
158+
// Test when not registered
159+
Propagator propagator = Propagators.forConcern(TRACING);
160+
assertNotNull(propagator, "Propagator should not be null when no propagator is registered");
161+
assertNoopPropagator(propagator);
162+
// Test when registered
163+
Propagators.register(TRACING, TRACING_PROPAGATOR);
164+
propagator = Propagators.forConcern(TRACING);
165+
assertNotNull(propagator, "Propagator should not be null when registered");
166+
assertInjectExtractContext(CONTEXT, propagator, TRACING_KEY);
167+
}
168+
169+
@Test
170+
void testForConcerns() {
171+
// Test when none registered
172+
Propagator propagator = Propagators.forConcerns(TRACING, IAST);
173+
assertNotNull(propagator, "Propagator should not be null when no propagator is registered");
174+
assertNoopPropagator(propagator);
175+
// Test when only one is registered
176+
Propagators.register(TRACING, TRACING_PROPAGATOR);
177+
propagator = Propagators.forConcerns(TRACING, IAST);
178+
assertNotNull(propagator, "Propagator should not be null when one is registered");
179+
assertInjectExtractContext(CONTEXT, propagator, TRACING_KEY);
180+
// Test when all registered
181+
Propagators.register(IAST, IAST_PROPAGATOR);
182+
propagator = Propagators.forConcerns(TRACING, IAST);
183+
assertNotNull(propagator, "Propagator should not be null when all are registered");
184+
assertInjectExtractContext(CONTEXT, propagator, TRACING_KEY, IAST_KEY);
185+
// Test propagator order follow the given concerns order despite concern priority
186+
Propagators.register(DEBUGGER, DEBUGGER_PROPAGATOR);
187+
Propagators.register(PROFILING, PROFILING_PROPAGATOR);
188+
propagator = Propagators.forConcerns(PROFILING, TRACING, DEBUGGER);
189+
assertInjectExtractContext(CONTEXT, propagator, PROFILING_KEY, TRACING_KEY, DEBUGGER_KEY);
190+
assertFalse(
191+
PROFILING_PROPAGATOR.keyFound,
192+
"Profiling propagator should have run before tracing propagator");
193+
assertTrue(
194+
DEBUGGER_PROPAGATOR.keyFound,
195+
"Debugger propagator should have run before tracing propagator");
196+
}
197+
198+
@Test
199+
void testNoopPropagator() {
200+
Propagator noopPropagator = Propagators.noop();
201+
assertNotNull(noopPropagator, "noop propagator should not be null");
202+
assertNoopPropagator(noopPropagator);
203+
}
204+
205+
void assertNoopPropagator(Propagator noopPropagator) {
206+
Map<String, String> carrier = new HashMap<>();
207+
noopPropagator.inject(CONTEXT, carrier, ACCESSOR);
208+
assertTrue(carrier.isEmpty(), "carrier should be empty");
209+
Context extracted = noopPropagator.extract(root(), carrier, ACCESSOR);
210+
assertEquals(root(), extracted, "extracted context should be empty");
211+
}
212+
213+
void assertInjectExtractContext(Context context, Propagator propagator, ContextKey<?>... keys) {
214+
Map<String, String> carrier = new HashMap<>();
215+
propagator.inject(context, carrier, ACCESSOR);
216+
Context extracted = propagator.extract(root(), carrier, ACCESSOR);
217+
for (ContextKey<?> key : keys) {
218+
assertEquals(
219+
context.get(key), extracted.get(key), "Key " + key + " not injected nor extracted");
220+
}
221+
}
222+
}

0 commit comments

Comments
 (0)