| // Copyright 2026 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package value |
| |
| import ( |
| "cmp" |
| "slices" |
| "testing" |
| |
| "google.golang.org/protobuf/encoding/protojson" |
| "google.golang.org/protobuf/proto" |
| "google.golang.org/protobuf/types/known/emptypb" |
| "google.golang.org/protobuf/types/known/structpb" |
| "google.golang.org/protobuf/types/known/wrapperspb" |
| |
| orchestratorpb "go.chromium.org/turboci/proto/go/graph/orchestrator/v1" |
| |
| "go.chromium.org/turboci/proto/go/utils/internal/test/assert" |
| ) |
| |
| func TestDecode(t *testing.T) { |
| t.Parallel() |
| |
| t.Run(`ok_inline_binary`, func(t *testing.T) { |
| t.Parallel() |
| |
| vref := MustInline(structpb.NewStringValue("hi"), "proj:realm") |
| |
| sval, err := Decode[*structpb.Value](nil, vref) |
| assert.NoErr(t, err) |
| |
| assert.Match(t, structpb.NewStringValue("hi"), sval) |
| }) |
| |
| t.Run(`ok_source`, func(t *testing.T) { |
| t.Parallel() |
| |
| vref := MustInline(structpb.NewStringValue("hi"), "proj:realm") |
| |
| dSrc := SimpleDataSource{} |
| AbsorbInline(dSrc, vref) |
| |
| assert.True(t, vref.HasDigest()) |
| |
| sval, err := Decode[*structpb.Value](dSrc, vref) |
| assert.NoErr(t, err) |
| |
| assert.Match(t, structpb.NewStringValue("hi"), sval) |
| }) |
| |
| t.Run(`ok_source_json`, func(t *testing.T) { |
| t.Parallel() |
| |
| vref := MustInline(structpb.NewStringValue("hi"), "proj:realm") |
| |
| dSrc := SimpleDataSource{} |
| AbsorbAsJSON(dSrc, vref, protojson.MarshalOptions{}) |
| |
| assert.True(t, vref.HasDigest()) |
| assert.True(t, dSrc.Retrieve(Digest(vref.GetDigest())).HasJson()) |
| |
| sval, err := Decode[*structpb.Value](dSrc, vref) |
| assert.NoErr(t, err) |
| |
| assert.Match(t, structpb.NewStringValue("hi"), sval) |
| }) |
| |
| t.Run(`missing`, func(t *testing.T) { |
| t.Parallel() |
| |
| vref := orchestratorpb.ValueRef_builder{ |
| TypeUrl: proto.String(URL[*structpb.Value]()), |
| Digest: proto.String("bogus"), |
| }.Build() |
| |
| dSrc := SimpleDataSource{} |
| |
| sval, err := Decode[*structpb.Value](dSrc, vref) |
| assert.NoErr(t, err) |
| assert.Nil(t, sval) |
| }) |
| |
| t.Run(`mismatch`, func(t *testing.T) { |
| t.Parallel() |
| |
| vref := orchestratorpb.ValueRef_builder{ |
| TypeUrl: proto.String(URL[*emptypb.Empty]()), |
| Digest: proto.String("bogus"), |
| }.Build() |
| |
| dSrc := SimpleDataSource{} |
| |
| _, err := Decode[*structpb.Value](dSrc, vref) |
| assert.ErrLike(t, err, "mismatched types") |
| }) |
| } |
| |
| func TestLookup(t *testing.T) { |
| t.Parallel() |
| |
| var options []*orchestratorpb.ValueRef |
| |
| options, _ = SetByTypeIn(options, MustInline(&emptypb.Empty{}, "proj:realm")) |
| options, _ = SetByTypeIn(options, MustInline(structpb.NewStringValue("hey"), "proj:realm")) |
| options, _ = SetByTypeIn(options, MustInline(wrapperspb.UInt32(100), "proj:realm")) |
| options, _ = SetByTypeIn(options, MustInline(wrapperspb.Bool(true), "proj:realm")) |
| |
| dSrc := SimpleDataSource{} |
| AbsorbInline(dSrc, options[0]) // bool |
| |
| valGot, err := Lookup[*structpb.Value](dSrc, options) |
| assert.NoErr(t, err) |
| assert.Match(t, structpb.NewStringValue("hey"), valGot) |
| |
| boolGot, err := Lookup[*wrapperspb.BoolValue](dSrc, options) |
| assert.NoErr(t, err) |
| assert.Match(t, wrapperspb.Bool(true), boolGot) |
| |
| missing, err := Lookup[*wrapperspb.StringValue](dSrc, options) |
| assert.NoErr(t, err) |
| assert.Nil(t, missing) |
| } |
| |
| func TestFind(t *testing.T) { |
| t.Parallel() |
| |
| options := []*orchestratorpb.ValueRef{ |
| MustInline(&emptypb.Empty{}, "proj:realm"), |
| MustInline(structpb.NewStringValue("hey"), "proj:realm"), |
| MustInline(wrapperspb.UInt32(100), "proj:realm"), |
| MustInline(wrapperspb.Bool(true), "proj:realm"), |
| |
| MustInline(&emptypb.Empty{}, "proj:other_realm"), |
| MustInline(wrapperspb.UInt32(100), "proj:other_realm"), |
| } |
| Omit(options[0], orchestratorpb.OmitReason_OMIT_REASON_NO_ACCESS) |
| Omit(options[len(options)-1], orchestratorpb.OmitReason_OMIT_REASON_NO_ACCESS) |
| |
| slices.SortStableFunc(options, func(a, b *orchestratorpb.ValueRef) int { |
| return cmp.Compare(a.GetTypeUrl(), b.GetTypeUrl()) |
| }) |
| |
| for _, opt := range options { |
| t.Log(opt) |
| } |
| |
| dSrc := SimpleDataSource{} |
| AbsorbInline(dSrc, options[0]) // bool |
| |
| valGot := Find(options, URL[*structpb.Value]()) |
| assert.Match(t, MustInline(structpb.NewStringValue("hey"), "proj:realm"), valGot) |
| |
| boolGot := Find(options, URL[*wrapperspb.BoolValue]()) |
| assert.Match(t, options[0], boolGot) |
| |
| missing := Find(options, URL[*wrapperspb.StringValue]()) |
| assert.Nil(t, missing) |
| |
| emptyGot := Find(options, URL[*emptypb.Empty]()) |
| assert.Match(t, MustInline(&emptypb.Empty{}, "proj:other_realm"), emptyGot) |
| } |
| |
| func TestResults(t *testing.T) { |
| t.Parallel() |
| |
| sortedData := func(refs ...*orchestratorpb.ValueRef) []*orchestratorpb.ValueRef { |
| ret := make([]*orchestratorpb.ValueRef, 0, len(refs)) |
| for _, ref := range refs { |
| ok := false |
| ret, ok = AddByTypeIn(ret, ref) |
| assert.True(t, ok) |
| } |
| return ret |
| } |
| |
| check := orchestratorpb.Check_builder{ |
| Results: []*orchestratorpb.Check_Result{ |
| orchestratorpb.Check_Result_builder{ |
| Data: sortedData( |
| MustInline(&emptypb.Empty{}, ""), |
| MustInline(wrapperspb.Bool(true), ""), |
| MustInline(wrapperspb.String("hey"), ""), |
| ), |
| }.Build(), |
| orchestratorpb.Check_Result_builder{ |
| Data: sortedData( |
| MustInline(&emptypb.Empty{}, ""), |
| MustInline(wrapperspb.String("norp"), ""), |
| ), |
| }.Build(), |
| orchestratorpb.Check_Result_builder{}.Build(), |
| orchestratorpb.Check_Result_builder{ |
| Data: sortedData( |
| MustInline(&emptypb.Empty{}, ""), |
| MustInline(wrapperspb.Bool(false), ""), |
| MustInline(wrapperspb.String("dorp"), ""), |
| ), |
| }.Build(), |
| }, |
| }.Build() |
| |
| emptyRslts, err := Results[*emptypb.Empty](nil, check) |
| assert.NoErr(t, err) |
| assert.Len(t, emptyRslts, 3) |
| |
| boolRslts, err := Results[*wrapperspb.BoolValue](nil, check) |
| assert.NoErr(t, err) |
| assert.Match(t, []*wrapperspb.BoolValue{ |
| wrapperspb.Bool(true), |
| wrapperspb.Bool(false), |
| }, boolRslts) |
| |
| strResults, err := Results[*wrapperspb.StringValue](nil, check) |
| assert.NoErr(t, err) |
| assert.Match(t, []*wrapperspb.StringValue{ |
| wrapperspb.String("hey"), |
| wrapperspb.String("norp"), |
| wrapperspb.String("dorp"), |
| }, strResults) |
| |
| intResults, err := Results[*wrapperspb.Int32Value](nil, check) |
| assert.NoErr(t, err) |
| assert.Empty(t, intResults) |
| } |