@@ -4,8 +4,10 @@ import com.fasterxml.jackson.annotation.JsonInclude
44import com.fasterxml.jackson.databind.ObjectMapper
55import com.fasterxml.jackson.databind.PropertyNamingStrategies
66import com.squareup.moshi.Moshi
7+ import datadog.common.version.VersionInfo
78import datadog.trace.api.Config
89import datadog.trace.api.aiguard.AIGuard
10+ import datadog.trace.api.telemetry.WafMetricCollector
911import datadog.trace.bootstrap.instrumentation.api.AgentSpan
1012import datadog.trace.bootstrap.instrumentation.api.AgentTracer
1113import datadog.trace.test.util.DDSpecification
@@ -35,7 +37,11 @@ class AIGuardInternalTests extends DDSpecification {
3537 protected static final URL = HttpUrl . parse(' https://app.datadoghq.com/api/v2/ai-guard/evaluate' )
3638
3739 @Shared
38- protected static final HEADERS = [' DD-API-KEY' : ' api' , ' DD-APPLICATION-KEY' : ' app' ]
40+ protected static final HEADERS = [' DD-API-KEY' : ' api' ,
41+ ' DD-APPLICATION-KEY' : ' app' ,
42+ ' DD-AI-GUARD-VERSION' : VersionInfo . VERSION ,
43+ ' DD-AI-GUARD-SOURCE' : ' SDK' ,
44+ ' DD-AI-GUARD-LANGUAGE' : ' jvm' ]
3945
4046 @Shared
4147 protected static final ORIGINAL_TRACER = AgentTracer . get()
@@ -79,6 +85,11 @@ class AIGuardInternalTests extends DDSpecification {
7985 buildSpan(_ as String , _ as String ) >> builder
8086 }
8187 AgentTracer . forceRegister(tracer)
88+
89+ WafMetricCollector . get(). tap {
90+ prepareMetrics()
91+ drain()
92+ }
8293 }
8394
8495 void cleanup () {
@@ -193,6 +204,7 @@ class AIGuardInternalTests extends DDSpecification {
193204 eval. action == suite. action
194205 eval. reason == suite. reason
195206 }
207+ assertTelemetry(" ai_guard.requests" , " action:$suite . action " , " block:$throwAbortError " )
196208
197209 where :
198210 suite << TestSuite . build()
@@ -222,6 +234,7 @@ class AIGuardInternalTests extends DDSpecification {
222234 final exception = thrown(AIGuard.AIGuardClientError )
223235 exception. errors == errors
224236 1 * span. addThrowable(_ as AIGuard.AIGuardClientError )
237+ assertTelemetry(" ai_guard.requests" , " error:true" )
225238 }
226239
227240 void ' test evaluate with invalid JSON' () {
@@ -246,6 +259,7 @@ class AIGuardInternalTests extends DDSpecification {
246259 then :
247260 thrown(AIGuard.AIGuardClientError )
248261 1 * span. addThrowable(_ as AIGuard.AIGuardClientError )
262+ assertTelemetry(" ai_guard.requests" , " error:true" )
249263 }
250264
251265 void ' test evaluate with missing action' () {
@@ -270,6 +284,7 @@ class AIGuardInternalTests extends DDSpecification {
270284 then :
271285 thrown(AIGuard.AIGuardClientError )
272286 1 * span. addThrowable(_ as AIGuard.AIGuardClientError )
287+ assertTelemetry(" ai_guard.requests" , " error:true" )
273288 }
274289
275290 void ' test evaluate with non JSON response' () {
@@ -294,6 +309,7 @@ class AIGuardInternalTests extends DDSpecification {
294309 then :
295310 thrown(AIGuard.AIGuardClientError )
296311 1 * span. addThrowable(_ as AIGuard.AIGuardClientError )
312+ assertTelemetry(" ai_guard.requests" , " error:true" )
297313 }
298314
299315 void ' test evaluate with empty response' () {
@@ -318,6 +334,7 @@ class AIGuardInternalTests extends DDSpecification {
318334 then :
319335 thrown(AIGuard.AIGuardClientError )
320336 1 * span. addThrowable(_ as AIGuard.AIGuardClientError )
337+ assertTelemetry(" ai_guard.requests" , " error:true" )
321338 }
322339
323340 void ' test message length truncation' () {
@@ -349,6 +366,7 @@ class AIGuardInternalTests extends DDSpecification {
349366 assert received. size() == maxMessages
350367 assert received. size() < messages. size()
351368 }
369+ assertTelemetry(" ai_guard.truncated" , " type:messages" )
352370 }
353371
354372 void ' test message content truncation' () {
@@ -380,6 +398,7 @@ class AIGuardInternalTests extends DDSpecification {
380398 assert it. content. length() < message. content. length()
381399 }
382400 }
401+ assertTelemetry(" ai_guard.truncated" , " type:content" )
383402 }
384403
385404 void ' test no messages' () {
@@ -425,6 +444,21 @@ class AIGuardInternalTests extends DDSpecification {
425444 0 * span. setTag(AIGuardInternal . TOOL_TAG , _)
426445 }
427446
447+ private static assertTelemetry (final String metric , final String ...tags ) {
448+ final metrics = WafMetricCollector . get(). with {
449+ prepareMetrics()
450+ drain()
451+ }
452+ final filtered = metrics. findAll {
453+ it. namespace == ' appsec'
454+ && it. metricName == metric
455+ && it. tags == tags. toList()
456+ }
457+ assert filtered. size() == 1 : metrics
458+ assert filtered* . value. sum() == 1
459+ return true
460+ }
461+
428462 private static assertRequest (final Request request , final List<AIGuard.Message > messages ) {
429463 assert request. url() == URL
430464 assert request. method() == ' POST'
@@ -452,12 +486,12 @@ class AIGuardInternalTests extends DDSpecification {
452486
453487 private static Response mockResponse (final Request request , final int status , final Object body ) {
454488 return new Response.Builder ()
455- .protocol(Protocol . HTTP_1_1 )
456- .message(' ok' )
457- .request(request)
458- .code(status)
459- .body(body == null ? null : ResponseBody . create(MediaType . parse(' application/json' ), MOSHI . adapter(Object ). toJson(body)))
460- .build()
489+ .protocol(Protocol . HTTP_1_1 )
490+ .message(' ok' )
491+ .request(request)
492+ .code(status)
493+ .body(body == null ? null : ResponseBody . create(MediaType . parse(' application/json' ), MOSHI . adapter(Object ). toJson(body)))
494+ .build()
461495 }
462496
463497 private static class TestSuite {
@@ -495,13 +529,13 @@ class AIGuardInternalTests extends DDSpecification {
495529 @Override
496530 String toString () {
497531 return " TestSuite{" +
498- " description='" + description + ' \' ' +
499- " , action=" + action +
500- " , reason='" + reason + ' \' ' +
501- " , blocking=" + blocking +
502- " , target='" + target + ' \' ' +
503- " , messages=" + messages +
504- ' }'
532+ " description='" + description + ' \' ' +
533+ " , action=" + action +
534+ " , reason='" + reason + ' \' ' +
535+ " , blocking=" + blocking +
536+ " , target='" + target + ' \' ' +
537+ " , messages=" + messages +
538+ ' }'
505539 }
506540 }
507541}
0 commit comments