@@ -4,10 +4,12 @@ import io.sentry.Attachment
44import io.sentry.CheckIn
55import io.sentry.CheckInStatus
66import io.sentry.DataCategory.Replay
7+ import io.sentry.EnvelopeReader
78import io.sentry.Hint
89import io.sentry.ILogger
910import io.sentry.IScopes
1011import io.sentry.ISerializer
12+ import io.sentry.JsonSerializer
1113import io.sentry.NoOpLogger
1214import io.sentry.ProfileChunk
1315import io.sentry.ProfilingTraceData
@@ -534,6 +536,33 @@ class RateLimiterTest {
534536 verifyNoMoreInteractions(fixture.clientReportRecorder)
535537 }
536538
539+ @Test
540+ fun `drop span items as lost` () {
541+ val rateLimiter = fixture.getSUT()
542+
543+ // There is no span API yet so we'll create the envelope manually using EnvelopeReader
544+ // This mimics how hybrid SDKs would send span v2 envelope items
545+ val spanPayload = """ {"items":[]}"""
546+ val spanItemHeader =
547+ """ {"type":"span","length":${spanPayload.length} ,"content_type":"application/vnd.sentry.items.span.v2+json","item_count":1}"""
548+ val envelopeHeader = """ {}"""
549+ val rawEnvelope = " $envelopeHeader \n $spanItemHeader \n $spanPayload "
550+
551+ val options = SentryOptions ()
552+ val envelopeReader = EnvelopeReader (JsonSerializer (options))
553+ val spanEnvelope = envelopeReader.read(rawEnvelope.byteInputStream())!!
554+ val spanItem = spanEnvelope.items.first()
555+
556+ rateLimiter.updateRetryAfterLimits(" 60:span:key" , null , 1 )
557+ val result = rateLimiter.filter(spanEnvelope, Hint ())
558+
559+ assertNull(result)
560+
561+ verify(fixture.clientReportRecorder, times(1 ))
562+ .recordLostEnvelopeItem(eq(DiscardReason .RATELIMIT_BACKOFF ), same(spanItem))
563+ verifyNoMoreInteractions(fixture.clientReportRecorder)
564+ }
565+
537566 @Test
538567 fun `apply rate limits notifies observers` () {
539568 val rateLimiter = fixture.getSUT()
0 commit comments