|
|
|
|
@@ -24,12 +24,13 @@ type codecScore struct {
|
|
|
|
|
type codecNotesAlias codecNotes
|
|
|
|
|
|
|
|
|
|
type testArtifactCodec[T any] struct {
|
|
|
|
|
kind contracts.ArtifactKind
|
|
|
|
|
schema contracts.ArtifactSchema
|
|
|
|
|
mediaType string
|
|
|
|
|
encodeFunc func(T) ([]byte, error)
|
|
|
|
|
candidateFunc func(T) ([]byte, error)
|
|
|
|
|
decodeFunc func([]byte) (T, error)
|
|
|
|
|
kind contracts.ArtifactKind
|
|
|
|
|
schema contracts.ArtifactSchema
|
|
|
|
|
mediaType string
|
|
|
|
|
encodeFunc func(T) ([]byte, error)
|
|
|
|
|
candidateFunc func(T) ([]byte, error)
|
|
|
|
|
decodeFunc func([]byte) (T, error)
|
|
|
|
|
candidateDecodeFunc func([]byte) (T, error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c testArtifactCodec[T]) Kind() contracts.ArtifactKind { return c.kind }
|
|
|
|
|
@@ -43,8 +44,15 @@ func (c testArtifactCodec[T]) EncodeCandidate(value T) ([]byte, error) {
|
|
|
|
|
}
|
|
|
|
|
func (c testArtifactCodec[T]) Encode(value T) ([]byte, error) { return c.encodeFunc(value) }
|
|
|
|
|
func (c testArtifactCodec[T]) Decode(content []byte) (T, error) { return c.decodeFunc(content) }
|
|
|
|
|
func (c testArtifactCodec[T]) DecodeCandidate(content []byte) (T, error) {
|
|
|
|
|
if c.candidateDecodeFunc != nil {
|
|
|
|
|
return c.candidateDecodeFunc(content)
|
|
|
|
|
}
|
|
|
|
|
return c.decodeFunc(content)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var _ contracts.ArtifactCodec[codecNotes] = testArtifactCodec[codecNotes]{}
|
|
|
|
|
var _ contracts.CandidateArtifactCodec[codecNotes] = testArtifactCodec[codecNotes]{}
|
|
|
|
|
|
|
|
|
|
func TestArtifactCodecRegistryStoresHeterogeneousExactTypes(t *testing.T) {
|
|
|
|
|
registry := NewArtifactCodecRegistry()
|
|
|
|
|
@@ -138,6 +146,44 @@ func TestArtifactCodecRegistryKeepsCandidateAndFinalEncodingDistinct(t *testing.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestArtifactCodecRegistryKeepsCandidateAndFinalDecodingDistinct(t *testing.T) {
|
|
|
|
|
candidateCalls, finalCalls := 0, 0
|
|
|
|
|
codec := notesCodec()
|
|
|
|
|
codec.candidateDecodeFunc = func([]byte) (codecNotes, error) {
|
|
|
|
|
candidateCalls++
|
|
|
|
|
return codecNotes{Items: []string{"candidate"}}, nil
|
|
|
|
|
}
|
|
|
|
|
codec.decodeFunc = func([]byte) (codecNotes, error) {
|
|
|
|
|
finalCalls++
|
|
|
|
|
return codecNotes{Items: []string{"final"}}, nil
|
|
|
|
|
}
|
|
|
|
|
registry := NewArtifactCodecRegistry()
|
|
|
|
|
if err := RegisterArtifactCodec(registry, codec); err != nil {
|
|
|
|
|
t.Fatalf("RegisterArtifactCodec() error = %v, want nil", err)
|
|
|
|
|
}
|
|
|
|
|
entry, _, err := registry.entry(codec.kind)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("entry() error = %v, want nil", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
candidate, err := entry.decodeCandidate([]byte(`{"items":["one"]}`))
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("candidate decode error = %v", err)
|
|
|
|
|
}
|
|
|
|
|
candidateWant := codecNotes{Items: []string{"candidate"}}
|
|
|
|
|
if !reflect.DeepEqual(candidate, candidateWant) || candidateCalls != 1 || finalCalls != 0 {
|
|
|
|
|
t.Fatalf("candidate decode = %#v, calls = candidate %d, final %d", candidate, candidateCalls, finalCalls)
|
|
|
|
|
}
|
|
|
|
|
decoded, err := entry.decode([]byte(`{"items":["one"]}`))
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("final decode error = %v", err)
|
|
|
|
|
}
|
|
|
|
|
finalWant := codecNotes{Items: []string{"final"}}
|
|
|
|
|
if !reflect.DeepEqual(decoded, finalWant) || candidateCalls != 1 || finalCalls != 1 {
|
|
|
|
|
t.Fatalf("final decode = %#v, calls = candidate %d, final %d", decoded, candidateCalls, finalCalls)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestArtifactCodecRegistryStoresValidatedSchemaMetadata(t *testing.T) {
|
|
|
|
|
registry := NewArtifactCodecRegistry()
|
|
|
|
|
codec := notesCodec()
|
|
|
|
|
@@ -267,6 +313,28 @@ func TestArtifactCodecRegistryWrapsEncodeFailure(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestArtifactCodecRegistryWrapsCandidateDecodeFailure(t *testing.T) {
|
|
|
|
|
cause := errors.New("cannot decode candidate notes")
|
|
|
|
|
codec := notesCodec()
|
|
|
|
|
codec.candidateDecodeFunc = func([]byte) (codecNotes, error) {
|
|
|
|
|
return codecNotes{}, cause
|
|
|
|
|
}
|
|
|
|
|
registry := NewArtifactCodecRegistry()
|
|
|
|
|
if err := RegisterArtifactCodec(registry, codec); err != nil {
|
|
|
|
|
t.Fatalf("RegisterArtifactCodec() error = %v, want nil", err)
|
|
|
|
|
}
|
|
|
|
|
entry, _, err := registry.entry(codec.kind)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("entry() error = %v, want nil", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = entry.decodeCandidate([]byte(`{"items":["one"]}`))
|
|
|
|
|
var operationErr *ArtifactCodecOperationError
|
|
|
|
|
if !errors.As(err, &operationErr) || operationErr.Operation != "decode candidate" || !errors.Is(err, cause) {
|
|
|
|
|
t.Fatalf("candidate decode error = %T %v, want typed wrapping error", err, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestArtifactCodecRegistryClonesCodecBytes(t *testing.T) {
|
|
|
|
|
shared := []byte(`{"items":["one"]}`)
|
|
|
|
|
codec := notesCodec()
|
|
|
|
|
@@ -275,6 +343,10 @@ func TestArtifactCodecRegistryClonesCodecBytes(t *testing.T) {
|
|
|
|
|
content[0] = '['
|
|
|
|
|
return codecNotes{Items: []string{"one"}}, nil
|
|
|
|
|
}
|
|
|
|
|
codec.candidateDecodeFunc = func(content []byte) (codecNotes, error) {
|
|
|
|
|
content[0] = '['
|
|
|
|
|
return codecNotes{Items: []string{"candidate"}}, nil
|
|
|
|
|
}
|
|
|
|
|
registry := NewArtifactCodecRegistry()
|
|
|
|
|
if err := RegisterArtifactCodec(registry, codec); err != nil {
|
|
|
|
|
t.Fatalf("RegisterArtifactCodec() error = %v, want nil", err)
|
|
|
|
|
@@ -295,6 +367,18 @@ func TestArtifactCodecRegistryClonesCodecBytes(t *testing.T) {
|
|
|
|
|
if !bytes.Equal(artifact.Content, before) {
|
|
|
|
|
t.Fatalf("serialized content changed during decode: %q", artifact.Content)
|
|
|
|
|
}
|
|
|
|
|
candidateContent := []byte(`{"items":["candidate"]}`)
|
|
|
|
|
candidateBefore := append([]byte(nil), candidateContent...)
|
|
|
|
|
entry, _, err := registry.entry("test/notes")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("entry() error = %v, want nil", err)
|
|
|
|
|
}
|
|
|
|
|
if _, err := entry.decodeCandidate(candidateContent); err != nil {
|
|
|
|
|
t.Fatalf("candidate decode error = %v, want nil", err)
|
|
|
|
|
}
|
|
|
|
|
if !bytes.Equal(candidateContent, candidateBefore) {
|
|
|
|
|
t.Fatalf("candidate content changed during decode: %q", candidateContent)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func notesCodec() testArtifactCodec[codecNotes] {
|
|
|
|
|
|