|
| 1 | +plugins { |
| 2 | + id 'java' |
| 3 | +} |
| 4 | + |
| 5 | +dependencies { |
| 6 | + // OTel API on the app classpath so the smoke test can call GlobalOpenTelemetry.get(). |
| 7 | + // At runtime the OTel agent intercepts this and bridges to the agent's shaded SDK. |
| 8 | + implementation "io.opentelemetry:opentelemetry-api:${otelVersion}" |
| 9 | + // Protobuf for parsing OTLP trace export payloads in the mock collector |
| 10 | + implementation 'io.opentelemetry.proto:opentelemetry-proto:1.5.0-alpha' |
| 11 | + runtimeOnly "org.slf4j:slf4j-simple:${slf4jVersion}" |
| 12 | +} |
| 13 | + |
| 14 | +// ── Configuration to resolve the OTel Java agent JAR from Maven Central ── |
| 15 | +configurations { |
| 16 | + otelAgent |
| 17 | +} |
| 18 | +dependencies { |
| 19 | + otelAgent 'io.opentelemetry.javaagent:opentelemetry-javaagent:2.16.0' |
| 20 | +} |
| 21 | + |
| 22 | +def braintrustAgentJar = project(':braintrust-java-agent').tasks.named('jar') |
| 23 | +def braintrustSdkExtJar = project(':braintrust-sdk').tasks.named('otelExtensionJar') |
| 24 | + |
| 25 | +/** |
| 26 | + * Smoke test: Braintrust agent as -javaagent, SDK JAR as OTel extension. |
| 27 | + * |
| 28 | + * - BT agent attaches first (ByteBuddy instrumentation, bootstrap setup) |
| 29 | + * - OTel agent attaches second, discovers braintrust-sdk.jar as an extension via |
| 30 | + * -Dotel.javaagent.extensions. The SDK JAR's OtelAutoConfig SPI provider is picked |
| 31 | + * up by the OTel agent's autoconfigure machinery and wires BT into the SDK. |
| 32 | + * |
| 33 | + * Two mock OTLP collectors capture exports: |
| 34 | + * 18240 — OTel collector (otel.exporter.otlp.endpoint) |
| 35 | + * 18241 — BT backend (BRAINTRUST_API_URL / OtelAutoConfig) |
| 36 | + */ |
| 37 | +task smokeTestBTAgentFirst(type: JavaExec) { |
| 38 | + dependsOn braintrustAgentJar, braintrustSdkExtJar, classes |
| 39 | + |
| 40 | + mainClass = 'dev.braintrust.smoketest.otelagent.OtelAgentSmokeTest' |
| 41 | + classpath = sourceSets.main.runtimeClasspath |
| 42 | + |
| 43 | + def btAgent = braintrustAgentJar.map { it.archiveFile.get().asFile.absolutePath } |
| 44 | + def sdkExtJar = braintrustSdkExtJar.map { it.archiveFile.get().asFile.absolutePath } |
| 45 | + def otelAgentJar = configurations.otelAgent.singleFile |
| 46 | + |
| 47 | + jvmArgs = [ |
| 48 | + "-javaagent:${otelAgentJar.absolutePath}", |
| 49 | + "-Dotel.javaagent.extensions=${sdkExtJar.get()}", |
| 50 | + "-javaagent:${btAgent.get()}", |
| 51 | + // Point OTel OTLP exporter at our mock collector (HTTP/1.1 — mock doesn't support HTTP/2) |
| 52 | + "-Dotel.exporter.otlp.endpoint=http://127.0.0.1:18240", |
| 53 | + "-Dotel.exporter.otlp.protocol=http/protobuf", |
| 54 | + "-Dotel.traces.exporter=otlp", |
| 55 | + "-Dotel.metrics.exporter=none", |
| 56 | + "-Dotel.logs.exporter=none", |
| 57 | + "-Dotel.service.name=bt-otel-smoke-test", |
| 58 | + // Port config for the smoke test process itself |
| 59 | + "-Dbraintrust.smoketest.otel.mock.port=18240", |
| 60 | + "-Dbraintrust.smoketest.bt.mock.port=18241", |
| 61 | + // "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5566", |
| 62 | + ] |
| 63 | + |
| 64 | + // Prevent host environment from leaking into the test process |
| 65 | + environment.remove('OTEL_EXPORTER_OTLP_ENDPOINT') |
| 66 | + environment.remove('OTEL_TRACES_EXPORTER') |
| 67 | + environment.remove('OTEL_METRICS_EXPORTER') |
| 68 | + environment.remove('OTEL_LOGS_EXPORTER') |
| 69 | + |
| 70 | + environment 'BRAINTRUST_API_KEY', 'bogus' |
| 71 | + environment 'BRAINTRUST_API_URL', 'http://127.0.0.1:18241' |
| 72 | + environment 'BRAINTRUST_FILTER_AI_SPANS', 'true' |
| 73 | + |
| 74 | + // ── Caching ── |
| 75 | + inputs.files(braintrustAgentJar) |
| 76 | + inputs.files(braintrustSdkExtJar) |
| 77 | + inputs.files(configurations.otelAgent) |
| 78 | + inputs.files(sourceSets.main.output) |
| 79 | + def markerFile = layout.buildDirectory.file('test-results/otel-agent-first-smoke-test.passed') |
| 80 | + outputs.file(markerFile) |
| 81 | + |
| 82 | + doLast { |
| 83 | + markerFile.get().asFile.tap { |
| 84 | + parentFile.mkdirs() |
| 85 | + text = "passed at ${java.time.Instant.now()}" |
| 86 | + } |
| 87 | + } |
| 88 | +} |
| 89 | + |
| 90 | +task smokeTestOtelAgentOnly(type: JavaExec) { |
| 91 | + dependsOn braintrustAgentJar, braintrustSdkExtJar, classes |
| 92 | + |
| 93 | + mainClass = 'dev.braintrust.smoketest.otelagent.OtelAgentSmokeTest' |
| 94 | + classpath = sourceSets.main.runtimeClasspath |
| 95 | + |
| 96 | + def btAgent = braintrustAgentJar.map { it.archiveFile.get().asFile.absolutePath } |
| 97 | + def sdkExtJar = braintrustSdkExtJar.map { it.archiveFile.get().asFile.absolutePath } |
| 98 | + def otelAgentJar = configurations.otelAgent.singleFile |
| 99 | + |
| 100 | + jvmArgs = [ |
| 101 | + "-javaagent:${otelAgentJar.absolutePath}", |
| 102 | + // Point OTel OTLP exporter at our mock collector (HTTP/1.1 — mock doesn't support HTTP/2) |
| 103 | + "-Dotel.exporter.otlp.endpoint=http://127.0.0.1:18242", |
| 104 | + "-Dotel.exporter.otlp.protocol=http/protobuf", |
| 105 | + "-Dotel.traces.exporter=otlp", |
| 106 | + "-Dotel.metrics.exporter=none", |
| 107 | + "-Dotel.logs.exporter=none", |
| 108 | + "-Dotel.service.name=bt-otel-smoke-test", |
| 109 | + // Port config for the smoke test process itself |
| 110 | + "-Dbraintrust.smoketest.otel.mock.port=18242", |
| 111 | + // "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5566", |
| 112 | + ] |
| 113 | + |
| 114 | + // Prevent host environment from leaking into the test process |
| 115 | + environment.remove('OTEL_EXPORTER_OTLP_ENDPOINT') |
| 116 | + environment.remove('OTEL_TRACES_EXPORTER') |
| 117 | + environment.remove('OTEL_METRICS_EXPORTER') |
| 118 | + environment.remove('OTEL_LOGS_EXPORTER') |
| 119 | + |
| 120 | + // ── Caching ── |
| 121 | + inputs.files(braintrustAgentJar) |
| 122 | + inputs.files(braintrustSdkExtJar) |
| 123 | + inputs.files(configurations.otelAgent) |
| 124 | + inputs.files(sourceSets.main.output) |
| 125 | + def markerFile = layout.buildDirectory.file('test-results/otel-agent-only-smoke-test.passed') |
| 126 | + outputs.file(markerFile) |
| 127 | + |
| 128 | + doLast { |
| 129 | + markerFile.get().asFile.tap { |
| 130 | + parentFile.mkdirs() |
| 131 | + text = "passed at ${java.time.Instant.now()}" |
| 132 | + } |
| 133 | + } |
| 134 | +} |
| 135 | + |
| 136 | +test.dependsOn smokeTestBTAgentFirst, smokeTestOtelAgentOnly |
0 commit comments