diff --git a/opamp-client/src/main/java/io/opentelemetry/opamp/client/OpampClientBuilder.java b/opamp-client/src/main/java/io/opentelemetry/opamp/client/OpampClientBuilder.java index c9ef52df9e..c41c42a96c 100644 --- a/opamp-client/src/main/java/io/opentelemetry/opamp/client/OpampClientBuilder.java +++ b/opamp-client/src/main/java/io/opentelemetry/opamp/client/OpampClientBuilder.java @@ -17,9 +17,11 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -28,6 +30,7 @@ import opamp.proto.AnyValue; import opamp.proto.ArrayValue; import opamp.proto.ComponentHealth; +import opamp.proto.CustomCapabilities; import opamp.proto.KeyValue; import opamp.proto.RemoteConfigStatus; @@ -35,6 +38,7 @@ public final class OpampClientBuilder { private final Map identifyingAttributes = new HashMap<>(); private final Map nonIdentifyingAttributes = new HashMap<>(); + private final Set customCapabilities = new HashSet<>(); private long capabilities = 0; private RequestService service = HttpRequestService.create(OkHttpSender.create("http://localhost:4320/v1/opamp")); @@ -391,6 +395,21 @@ public OpampClientBuilder enableHealthReporting(@Nonnull ComponentHealth initial return this; } + /** + * Adds a custom capability supported by this Client for messages received from the Server. The + * configured capabilities are reported in {@code AgentToServer.custom_capabilities}. Note that + * incoming custom messages are not filtered, it is the user's responsibility to check the + * capability + * + * @param capability The reverse FQDN identifying the custom capability. + * @return this + */ + @CanIgnoreReturnValue + public OpampClientBuilder addCustomCapability(String capability) { + customCapabilities.add(Objects.requireNonNull(capability)); + return this; + } + public OpampClient build(OpampClient.Callbacks callbacks) { List protoIdentifyingAttributes = new ArrayList<>(); List protoNonIdentifyingAttributes = new ArrayList<>(); @@ -414,6 +433,7 @@ public OpampClient build(OpampClient.Callbacks callbacks) { .non_identifying_attributes(protoNonIdentifyingAttributes) .build()), new State.Capabilities(capabilities), + new State.CustomCapabilities(createCustomCapabilities()), new State.Health(initialHealth), new State.InstanceUid(instanceUid), new State.Flags(0L), @@ -425,6 +445,16 @@ private void enableCapability(AgentCapabilities capability) { capabilities = capabilities | capability.getValue(); } + @Nullable + private CustomCapabilities createCustomCapabilities() { + if (customCapabilities.isEmpty()) { + return null; + } + return new CustomCapabilities.Builder() + .capabilities(new ArrayList<>(customCapabilities)) + .build(); + } + private static State.EffectiveConfig createEffectiveConfigNoop() { return new State.EffectiveConfig() { @Nullable diff --git a/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/OpampClientImpl.java b/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/OpampClientImpl.java index 07cef7af8f..926bfcb1b5 100644 --- a/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/OpampClientImpl.java +++ b/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/OpampClientImpl.java @@ -12,6 +12,7 @@ import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.AgentDescriptionAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.AgentDisconnectAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.CapabilitiesAppender; +import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.CustomCapabilitiesAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.EffectiveConfigAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.FlagsAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.HealthAppender; @@ -79,6 +80,7 @@ public final class OpampClientImpl // Compressable fields init List compressableFields = new ArrayList<>(); compressableFields.add(Field.AGENT_DESCRIPTION); + compressableFields.add(Field.CUSTOM_CAPABILITIES); compressableFields.add(Field.HEALTH); compressableFields.add(Field.EFFECTIVE_CONFIG); compressableFields.add(Field.REMOTE_CONFIG_STATUS); @@ -94,6 +96,7 @@ public static OpampClientImpl create( RemoteConfigStatusAppender.create(state.remoteConfigStatus), SequenceNumberAppender.create(state.sequenceNum), CapabilitiesAppender.create(state.capabilities), + CustomCapabilitiesAppender.create(state.customCapabilities), HealthAppender.create(state.health), InstanceUidAppender.create(state.instanceUid), FlagsAppender.create(state.flags), @@ -206,6 +209,11 @@ private void handleResponsePayload(ServerToAgent response) { messageBuilder.setRemoteConfig(response.remote_config); } + if (response.custom_message != null) { + notifyOnMessage = true; + messageBuilder.setCustomMessage(response.custom_message); + } + if (notifyOnMessage) { callbacks.onMessage(this, messageBuilder.build()); } diff --git a/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/OpampClientState.java b/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/OpampClientState.java index f72574db63..2215e854b4 100644 --- a/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/OpampClientState.java +++ b/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/OpampClientState.java @@ -19,17 +19,20 @@ public final class OpampClientState { public final State.SequenceNum sequenceNum; public final State.AgentDescription agentDescription; public final State.Capabilities capabilities; + public final State.CustomCapabilities customCapabilities; public final State.Health health; public final State.InstanceUid instanceUid; public final State.Flags flags; public final State.EffectiveConfig effectiveConfig; private final List> items; + @SuppressWarnings("TooManyParameters") public OpampClientState( State.RemoteConfigStatus remoteConfigStatus, State.SequenceNum sequenceNum, State.AgentDescription agentDescription, State.Capabilities capabilities, + State.CustomCapabilities customCapabilities, State.Health health, State.InstanceUid instanceUid, State.Flags flags, @@ -38,6 +41,7 @@ public OpampClientState( this.sequenceNum = sequenceNum; this.agentDescription = agentDescription; this.capabilities = capabilities; + this.customCapabilities = customCapabilities; this.health = health; this.instanceUid = instanceUid; this.flags = flags; @@ -48,6 +52,7 @@ public OpampClientState( providedItems.add(sequenceNum); providedItems.add(agentDescription); providedItems.add(capabilities); + providedItems.add(customCapabilities); providedItems.add(health); providedItems.add(instanceUid); providedItems.add(flags); diff --git a/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/recipe/AgentToServerAppenders.java b/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/recipe/AgentToServerAppenders.java index f6072ce426..76c8308f66 100644 --- a/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/recipe/AgentToServerAppenders.java +++ b/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/recipe/AgentToServerAppenders.java @@ -9,6 +9,7 @@ import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.AgentDisconnectAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.AgentToServerAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.CapabilitiesAppender; +import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.CustomCapabilitiesAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.EffectiveConfigAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.FlagsAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.HealthAppender; @@ -30,6 +31,7 @@ public final class AgentToServerAppenders { public final RemoteConfigStatusAppender remoteConfigStatusAppender; public final SequenceNumberAppender sequenceNumberAppender; public final CapabilitiesAppender capabilitiesAppender; + public final CustomCapabilitiesAppender customCapabilitiesAppender; public final HealthAppender healthAppender; public final InstanceUidAppender instanceUidAppender; public final FlagsAppender flagsAppender; @@ -43,6 +45,7 @@ public AgentToServerAppenders( RemoteConfigStatusAppender remoteConfigStatusAppender, SequenceNumberAppender sequenceNumberAppender, CapabilitiesAppender capabilitiesAppender, + CustomCapabilitiesAppender customCapabilitiesAppender, HealthAppender healthAppender, InstanceUidAppender instanceUidAppender, FlagsAppender flagsAppender, @@ -52,6 +55,7 @@ public AgentToServerAppenders( this.remoteConfigStatusAppender = remoteConfigStatusAppender; this.sequenceNumberAppender = sequenceNumberAppender; this.capabilitiesAppender = capabilitiesAppender; + this.customCapabilitiesAppender = customCapabilitiesAppender; this.healthAppender = healthAppender; this.instanceUidAppender = instanceUidAppender; this.flagsAppender = flagsAppender; @@ -63,6 +67,7 @@ public AgentToServerAppenders( appenders.put(Field.REMOTE_CONFIG_STATUS, remoteConfigStatusAppender); appenders.put(Field.SEQUENCE_NUM, sequenceNumberAppender); appenders.put(Field.CAPABILITIES, capabilitiesAppender); + appenders.put(Field.CUSTOM_CAPABILITIES, customCapabilitiesAppender); appenders.put(Field.HEALTH, healthAppender); appenders.put(Field.INSTANCE_UID, instanceUidAppender); appenders.put(Field.FLAGS, flagsAppender); diff --git a/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/recipe/appenders/CustomCapabilitiesAppender.java b/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/recipe/appenders/CustomCapabilitiesAppender.java new file mode 100644 index 0000000000..d95ff83d47 --- /dev/null +++ b/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/recipe/appenders/CustomCapabilitiesAppender.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.opamp.client.internal.impl.recipe.appenders; + +import java.util.function.Supplier; +import opamp.proto.AgentToServer; +import opamp.proto.CustomCapabilities; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. TODO AgentToServer.custom_message support + */ +public final class CustomCapabilitiesAppender implements AgentToServerAppender { + private final Supplier customCapabilities; + + public static CustomCapabilitiesAppender create(Supplier customCapabilities) { + return new CustomCapabilitiesAppender(customCapabilities); + } + + private CustomCapabilitiesAppender(Supplier customCapabilities) { + this.customCapabilities = customCapabilities; + } + + @Override + public void appendTo(AgentToServer.Builder builder) { + builder.custom_capabilities(customCapabilities.get()); + } +} diff --git a/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/request/Field.java b/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/request/Field.java index 64cdc0494a..efa51369d1 100644 --- a/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/request/Field.java +++ b/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/request/Field.java @@ -18,6 +18,7 @@ public enum Field { SEQUENCE_NUM, AGENT_DESCRIPTION, CAPABILITIES, + CUSTOM_CAPABILITIES, HEALTH, EFFECTIVE_CONFIG, REMOTE_CONFIG_STATUS, diff --git a/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/response/MessageData.java b/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/response/MessageData.java index af47027ef5..e838a13e56 100644 --- a/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/response/MessageData.java +++ b/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/response/MessageData.java @@ -9,6 +9,7 @@ import io.opentelemetry.opamp.client.OpampClient; import javax.annotation.Nullable; import opamp.proto.AgentRemoteConfig; +import opamp.proto.CustomMessage; /** * Data class provided in {@link OpampClient.Callbacks#onMessage(OpampClient, MessageData)} with @@ -19,6 +20,9 @@ public abstract class MessageData { @Nullable public abstract AgentRemoteConfig getRemoteConfig(); + @Nullable + public abstract CustomMessage getCustomMessage(); + public static Builder builder() { return new AutoValue_MessageData.Builder(); } @@ -27,6 +31,8 @@ public static Builder builder() { public abstract static class Builder { public abstract Builder setRemoteConfig(AgentRemoteConfig remoteConfig); + public abstract Builder setCustomMessage(CustomMessage customMessage); + public abstract MessageData build(); } } diff --git a/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/state/State.java b/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/state/State.java index 09053b216c..9aa8b2b642 100644 --- a/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/state/State.java +++ b/opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/state/State.java @@ -77,6 +77,17 @@ public Field getFieldType() { } } + final class CustomCapabilities extends InMemoryState { + public CustomCapabilities(@Nullable opamp.proto.CustomCapabilities initialValue) { + super(initialValue); + } + + @Override + public Field getFieldType() { + return Field.CUSTOM_CAPABILITIES; + } + } + final class Health extends InMemoryState { public Health(@Nullable ComponentHealth initialValue) { super(initialValue); diff --git a/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/OpampClientImplTest.java b/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/OpampClientImplTest.java index 500b4bb919..13b17bb43b 100644 --- a/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/OpampClientImplTest.java +++ b/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/OpampClientImplTest.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.time.Duration; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -48,6 +49,8 @@ import opamp.proto.AgentToServerFlags; import opamp.proto.AnyValue; import opamp.proto.ComponentHealth; +import opamp.proto.CustomCapabilities; +import opamp.proto.CustomMessage; import opamp.proto.EffectiveConfig; import opamp.proto.KeyValue; import opamp.proto.RemoteConfigStatus; @@ -70,6 +73,7 @@ class OpampClientImplTest { private OpampClientImpl client; private TestEffectiveConfig effectiveConfig; private ComponentHealth initialHealth; + private CustomCapabilities customCapabilities; private TestCallbacks callbacks; @StartStop private final MockWebServer server = new MockWebServer(); @@ -87,6 +91,7 @@ void setUp() { .status("running") .status_time_unix_nano(456L) .build(); + customCapabilities = null; requestService = createHttpService(); } @@ -185,6 +190,52 @@ void verifyFieldsSent() { assertThat(fullRequestedMessage.remote_config_status).isEqualTo(state.remoteConfigStatus.get()); } + @Test + void verifyCustomCapabilitiesSent() { + customCapabilities = createCustomCapabilities("com.example.pause", "com.example.discovery"); + + RecordedRequest firstRequest = initializeClient(); + + assertThat(getAgentToServerMessage(firstRequest).custom_capabilities) + .isEqualTo(customCapabilities); + + enqueueServerToAgentResponse(new ServerToAgent.Builder().build()); + requestService.sendRequest(); + + assertThat(getAgentToServerMessage(takeRequest()).custom_capabilities).isNull(); + + ServerToAgent reportFullState = + new ServerToAgent.Builder() + .flags(ServerToAgentFlags.ServerToAgentFlags_ReportFullState.getValue()) + .build(); + enqueueServerToAgentResponse(reportFullState); + requestService.sendRequest(); + takeRequest(); + + enqueueServerToAgentResponse(new ServerToAgent.Builder().build()); + requestService.sendRequest(); + + assertThat(getAgentToServerMessage(takeRequest()).custom_capabilities) + .isEqualTo(customCapabilities); + } + + @Test + void builderReportsCustomCapabilities() { + enqueueServerToAgentResponse(new ServerToAgent.Builder().build()); + callbacks = spy(new TestCallbacks()); + + client = + (OpampClientImpl) + OpampClient.builder() + .setRequestService(requestService) + .addCustomCapability("com.example.pause") + .addCustomCapability("com.example.discovery") + .build(callbacks); + + assertThat(getAgentToServerMessage(takeRequest()).custom_capabilities.capabilities) + .containsExactlyInAnyOrder("com.example.pause", "com.example.discovery"); + } + @Test void verifyStop() { initializeClient(); @@ -216,6 +267,28 @@ void onSuccess_withChangesToReport_notifyCallbackOnMessage() { .onMessage(client, MessageData.builder().setRemoteConfig(remoteConfig).build()); } + @Test + void onSuccess_withCustomMessage_notifyCallbackOnMessage() { + initializeClient(); + CustomMessage customMessage = + new CustomMessage.Builder() + .capability("com.example.pause") + .type("pause") + .data(ByteString.encodeUtf8("payload")) + .build(); + ServerToAgent serverToAgent = new ServerToAgent.Builder().custom_message(customMessage).build(); + enqueueServerToAgentResponse(serverToAgent); + + // Force request + requestService.sendRequest(); + + // Await for onMessage call + await().atMost(Duration.ofSeconds(5)).until(() -> callbacks.onMessageCalls.get() > 0); + + verify(callbacks) + .onMessage(client, MessageData.builder().setCustomMessage(customMessage).build()); + } + @Test void onSuccess_withNoChangesToReport_doNotNotifyCallbackOnMessage() { initializeClient(); @@ -410,6 +483,7 @@ private OpampClientState createState(AgentCapabilities... enabledCapabilities) { new State.SequenceNum(1L), new State.AgentDescription(new AgentDescription.Builder().build()), new State.Capabilities(capabilities), + new State.CustomCapabilities(customCapabilities), new State.Health(initialHealth), new State.InstanceUid(new byte[] {1, 2, 3}), new State.Flags((long) AgentToServerFlags.AgentToServerFlags_Unspecified.getValue()), @@ -453,6 +527,10 @@ private static AgentConfigMap createAgentConfigMap(String key, String content) { return new AgentConfigMap.Builder().config_map(keyToFile).build(); } + private static CustomCapabilities createCustomCapabilities(String... capabilities) { + return new CustomCapabilities.Builder().capabilities(Arrays.asList(capabilities)).build(); + } + private static AgentDescription getAgentDescriptionWithOneIdentifyingValue( String key, String value) { KeyValue keyValue = diff --git a/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/OpampClientStateTest.java b/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/OpampClientStateTest.java index fe272b12d5..fa6c4f1043 100644 --- a/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/OpampClientStateTest.java +++ b/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/OpampClientStateTest.java @@ -24,6 +24,7 @@ class OpampClientStateTest { @Mock private State.SequenceNum sequenceNum; @Mock private State.AgentDescription agentDescription; @Mock private State.Capabilities capabilities; + @Mock private State.CustomCapabilities customCapabilities; @Mock private State.Health health; @Mock private State.InstanceUid instanceUid; @Mock private State.Flags flags; diff --git a/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/recipe/AgentToServerAppendersTest.java b/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/recipe/AgentToServerAppendersTest.java index e9d3b46f19..45351fc75a 100644 --- a/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/recipe/AgentToServerAppendersTest.java +++ b/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/recipe/AgentToServerAppendersTest.java @@ -11,6 +11,7 @@ import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.AgentDisconnectAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.AgentToServerAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.CapabilitiesAppender; +import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.CustomCapabilitiesAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.EffectiveConfigAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.FlagsAppender; import io.opentelemetry.opamp.client.internal.impl.recipe.appenders.HealthAppender; @@ -31,6 +32,7 @@ class AgentToServerAppendersTest { @Mock private RemoteConfigStatusAppender remoteConfigStatusAppender; @Mock private SequenceNumberAppender sequenceNumberAppender; @Mock private CapabilitiesAppender capabilitiesAppender; + @Mock private CustomCapabilitiesAppender customCapabilitiesAppender; @Mock private HealthAppender healthAppender; @Mock private FlagsAppender flagsAppender; @Mock private InstanceUidAppender instanceUidAppender; @@ -44,6 +46,7 @@ void verifyAppenderList() { verifyMapping(Field.REMOTE_CONFIG_STATUS, remoteConfigStatusAppender); verifyMapping(Field.SEQUENCE_NUM, sequenceNumberAppender); verifyMapping(Field.CAPABILITIES, capabilitiesAppender); + verifyMapping(Field.CUSTOM_CAPABILITIES, customCapabilitiesAppender); verifyMapping(Field.HEALTH, healthAppender); verifyMapping(Field.INSTANCE_UID, instanceUidAppender); verifyMapping(Field.FLAGS, flagsAppender); diff --git a/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/recipe/appenders/CustomCapabilitiesAppenderTest.java b/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/recipe/appenders/CustomCapabilitiesAppenderTest.java new file mode 100644 index 0000000000..47caa3f96b --- /dev/null +++ b/opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/recipe/appenders/CustomCapabilitiesAppenderTest.java @@ -0,0 +1,43 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.opamp.client.internal.impl.recipe.appenders; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collections; +import java.util.concurrent.atomic.AtomicReference; +import opamp.proto.AgentToServer; +import opamp.proto.CustomCapabilities; +import org.junit.jupiter.api.Test; + +class CustomCapabilitiesAppenderTest { + + @Test + void shouldAppendProvidedCustomCapabilities() { + CustomCapabilities customCapabilities = + new CustomCapabilities.Builder() + .capabilities(Collections.singletonList("com.example.pause")) + .build(); + CustomCapabilitiesAppender appender = + CustomCapabilitiesAppender.create(() -> customCapabilities); + + AgentToServer.Builder builder = new AgentToServer.Builder(); + appender.appendTo(builder); + + assertThat(builder.build().custom_capabilities).isEqualTo(customCapabilities); + } + + @Test + void shouldNotAppendCustomCapabilitiesIfNotProvided() { + CustomCapabilitiesAppender appender = + CustomCapabilitiesAppender.create(new AtomicReference()::get); + + AgentToServer.Builder builder = new AgentToServer.Builder(); + appender.appendTo(builder); + + assertThat(builder.build().custom_capabilities).isNull(); + } +}