github.com/anthropics/anthropic-sdk-go@v1.70.1/internal/compat/exhaustive_toparam_test.go (about)

     1  package compat
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"maps"
     7  	"reflect"
     8  	"slices"
     9  	"testing"
    10  
    11  	"github.com/anthropics/anthropic-sdk-go"
    12  )
    13  
    14  // TestToParamExhaustive checks that ToParam keeps all the data of every content
    15  // block the API can return. Blocks are synthesized in synthetic_test.go. A
    16  // response field with no request counterpart is skipped; when codegen adds one,
    17  // convert it in ToParam if a request can carry it, else add it to skipped below.
    18  func TestToParamExhaustive(t *testing.T) {
    19  	skipped := []string{
    20  		"CitationCharLocation.file_id", "CitationContentBlockLocation.file_id", "CitationPageLocation.file_id",
    21  	}
    22  	betaSkipped := []string{
    23  		"BetaCitationCharLocation.file_id", "BetaCitationContentBlockLocation.file_id", "BetaCitationPageLocation.file_id",
    24  	}
    25  	for _, tt := range []struct {
    26  		name     string
    27  		response reflect.Type
    28  		request  reflect.Type
    29  		skipped  []string
    30  		toParam  func([]byte) (any, error)
    31  	}{
    32  		{
    33  			"content blocks",
    34  			reflect.TypeFor[anthropic.ContentBlockUnion](),
    35  			reflect.TypeFor[anthropic.ContentBlockParamUnion](),
    36  			skipped,
    37  			func(wire []byte) (any, error) {
    38  				var block anthropic.ContentBlockUnion
    39  				if err := json.Unmarshal(wire, &block); err != nil {
    40  					return nil, err
    41  				}
    42  				return block.ToParam(), nil
    43  			},
    44  		},
    45  		{
    46  			"beta content blocks",
    47  			reflect.TypeFor[anthropic.BetaContentBlockUnion](),
    48  			reflect.TypeFor[anthropic.BetaContentBlockParamUnion](),
    49  			betaSkipped,
    50  			func(wire []byte) (any, error) {
    51  				var block anthropic.BetaContentBlockUnion
    52  				if err := json.Unmarshal(wire, &block); err != nil {
    53  					return nil, err
    54  				}
    55  				return block.ToParam(), nil
    56  			},
    57  		},
    58  	} {
    59  		t.Run(tt.name, func(t *testing.T) {
    60  			synth := newSynthesizer(t, tt.request)
    61  			for _, v := range unionVariants(tt.response) {
    62  				for _, s := range synth.responseShapes(v.Type) {
    63  					t.Run(v.Type.Name()+"/"+s.Name, func(t *testing.T) {
    64  						wire, err := json.Marshal(s.Value)
    65  						if err != nil {
    66  							t.Fatalf("Failed to marshal shape: %v", err)
    67  						}
    68  						param, err := tt.toParam(wire)
    69  						if err != nil {
    70  							t.Fatalf("Failed to unmarshal %s: %v", wire, err)
    71  						}
    72  						want := comparableJSON(t, wire)
    73  						if got := requestJSON(t, param); !bytes.Equal(got, want) {
    74  							t.Errorf("ToParam changed the block\n want: %s\n  got: %s", want, got)
    75  						}
    76  					})
    77  				}
    78  			}
    79  			got := slices.Collect(maps.Keys(synth.skipped))
    80  			if extra := diff(got, tt.skipped); len(extra) > 0 {
    81  				t.Errorf("new response fields with no request counterpart %q: list them here if no request can carry them", extra)
    82  			}
    83  			if stale := diff(tt.skipped, got); len(stale) > 0 {
    84  				t.Errorf("listed fields no longer skipped %q: remove them", stale)
    85  			}
    86  		})
    87  	}
    88  }