github.com/anthropics/anthropic-sdk-go@v1.70.1/internal/compat/exhaustive_accumulate_test.go (about) 1 package compat 2 3 import ( 4 "reflect" 5 "sort" 6 "testing" 7 8 "github.com/anthropics/anthropic-sdk-go" 9 ) 10 11 // TestAccumulateExhaustive fails when the generated code gains a stream-event or 12 // delta variant that Accumulate does not handle, so no new variant is dropped 13 // silently. On failure, handle the named variant in Accumulate, then add it 14 // to the list below. 15 func TestAccumulateExhaustive(t *testing.T) { 16 events := []string{ 17 "content_block_delta", "content_block_start", "content_block_stop", 18 "message_delta", "message_start", "message_stop", 19 } 20 deltas := []string{ 21 "citations_delta", "input_json_delta", "signature_delta", 22 "text_delta", "thinking_delta", 23 } 24 for _, tt := range []struct { 25 name string 26 union any 27 handled []string 28 }{ 29 {"events", anthropic.MessageStreamEventUnion{}, events}, 30 {"deltas", anthropic.RawContentBlockDeltaUnion{}, deltas}, 31 {"beta events", anthropic.BetaRawMessageStreamEventUnion{}, events}, 32 {"beta deltas", anthropic.BetaRawContentBlockDeltaUnion{}, append([]string{"compaction_delta"}, deltas...)}, 33 } { 34 t.Run(tt.name, func(t *testing.T) { 35 generated := variants(t, tt.union) 36 if extra := diff(generated, tt.handled); len(extra) > 0 { 37 t.Errorf("new %s variants %q: handle them in Accumulate, then list them here", tt.name, extra) 38 } 39 if stale := diff(tt.handled, generated); len(stale) > 0 { 40 t.Errorf("listed %s variants no longer generated %q: remove them", tt.name, stale) 41 } 42 }) 43 } 44 } 45 46 // variants lists the wire discriminant of every variant of a union. 47 func variants(t *testing.T, union any) []string { 48 t.Helper() 49 var out []string 50 for _, v := range unionVariants(reflect.TypeOf(union)) { 51 out = append(out, wireType(t, v)) 52 } 53 return out 54 } 55 56 // diff returns the members of a that b lacks, sorted. 57 func diff(a, b []string) []string { 58 lacks := map[string]bool{} 59 for _, s := range b { 60 lacks[s] = true 61 } 62 var out []string 63 for _, s := range a { 64 if !lacks[s] { 65 out = append(out, s) 66 } 67 } 68 sort.Strings(out) 69 return out 70 } 71 72 // TestAccumulateDeltaFieldsExhaustive fails when a message_delta type gains a 73 // field that Accumulate does not merge into the message. Every value the final 74 // message_delta carries has to reach the accumulated Message: an ignored one 75 // leaves the streamed Message differing from the non-streaming one. On failure, 76 // merge the named field in Accumulate, then add it to the list below. 77 func TestAccumulateDeltaFieldsExhaustive(t *testing.T) { 78 usage := []string{ 79 "CacheCreationInputTokens", "CacheReadInputTokens", "InputTokens", 80 "OutputTokens", "OutputTokensDetails", "ServerToolUse", 81 } 82 delta := []string{"Container", "StopDetails", "StopReason", "StopSequence"} 83 event := []string{"Delta", "Type", "Usage"} 84 for _, tt := range []struct { 85 name string 86 value any 87 handled []string 88 }{ 89 {"delta usage", anthropic.MessageDeltaUsage{}, usage}, 90 {"beta delta usage", anthropic.BetaMessageDeltaUsage{}, append([]string{"FallbackCredit", "Iterations"}, usage...)}, 91 {"delta", anthropic.MessageDeltaEventDelta{}, delta}, 92 {"beta delta", anthropic.BetaRawMessageDeltaEventDelta{}, delta}, 93 {"delta event", anthropic.MessageDeltaEvent{}, event}, 94 {"beta delta event", anthropic.BetaRawMessageDeltaEvent{}, append([]string{"ContextManagement", "InputTransformations"}, event...)}, 95 } { 96 t.Run(tt.name, func(t *testing.T) { 97 generated := fields(tt.value) 98 if extra := diff(generated, tt.handled); len(extra) > 0 { 99 t.Errorf("new %s fields %q: merge them in Accumulate, then list them here", tt.name, extra) 100 } 101 if stale := diff(tt.handled, generated); len(stale) > 0 { 102 t.Errorf("listed %s fields no longer generated %q: remove them", tt.name, stale) 103 } 104 }) 105 } 106 } 107 108 // fields lists the exported fields of a struct, minus the JSON metadata one. 109 func fields(value any) []string { 110 typ := reflect.TypeOf(value) 111 var out []string 112 for i := range typ.NumField() { 113 field := typ.Field(i) 114 if !field.IsExported() || field.Name == "JSON" { 115 continue 116 } 117 out = append(out, field.Name) 118 } 119 return out 120 }