blob: da2815b028312cc7666a72a35c11c5e88fffab2e [file]
// 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 (
"strings"
"testing"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/structpb"
commonpb "go.chromium.org/turboci/proto/go/data/common/v1"
orchestratorpb "go.chromium.org/turboci/proto/go/graph/orchestrator/v1"
"go.chromium.org/turboci/proto/go/utils/internal/test/assert"
)
func TestMakeTypeMatcher(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
urls []string
wantErr string
wantN int
matches []string
rejects []string
}{
{
name: "empty",
rejects: []string{"hi", URL[*emptypb.Empty]()},
},
{
name: "static",
urls: []string{
URL[*emptypb.Empty](),
URL[*structpb.Value](),
},
wantN: 2,
matches: []string{
URL[*emptypb.Empty](),
URL[*structpb.Value](),
},
rejects: []string{
"hi",
URL[*structpb.ListValue](),
// This will produce e.g. `google_protobuf_Empty`; if we don't
// correctly escape the metachars in the regex, then our pattern for
// `google.protobuf.Empty` would match accidentally.
strings.ReplaceAll(URL[*emptypb.Empty](), ".", "_"),
},
},
{
name: "wildcard_package",
urls: []string{
URLPatternPackageOf[*structpb.Value](),
},
wantN: 1,
matches: []string{
URL[*structpb.Value](),
URL[*structpb.ListValue](),
URL[*structpb.Struct](),
URL[*emptypb.Empty](),
},
rejects: []string{"hi", URL[*commonpb.DisplayMessage]()},
},
{
name: "prefix_star",
urls: []string{
TypePrefix + "*",
},
wantN: 1,
matches: []string{
URL[*structpb.Value](),
URL[*structpb.ListValue](),
URL[*structpb.Struct](),
URL[*emptypb.Empty](),
URL[*commonpb.DisplayMessage](),
},
rejects: []string{"hi"},
},
{
name: "bare_star",
urls: []string{
"*",
},
wantN: 1,
matches: []string{
URL[*structpb.Value](),
URL[*structpb.ListValue](),
URL[*structpb.Struct](),
URL[*emptypb.Empty](),
URL[*commonpb.DisplayMessage](),
TypePrefix + "very.long.package.Spam",
TypePrefix + "very.Cool",
TypePrefix + "very.Cool",
TypePrefix + "*",
},
},
{
name: "bad_prefix",
urls: []string{
"asdf*",
},
wantErr: "expected prefix",
},
{
name: "bad_star",
urls: []string{
TypePrefix + "hello*",
},
wantErr: "may only be used in a suffix",
},
{
name: "double_star",
urls: []string{
TypePrefix + "*hello.*",
},
wantErr: "multiple *",
},
{
name: "normalized",
urls: []string{
TypePrefix + "very.long.package.*",
TypePrefix + "very.Cool",
TypePrefix + "very.long.*",
TypePrefix + "very.long.package.Spam",
TypePrefix + "very.Cool",
TypePrefix + "very.Cool",
},
wantN: 2,
matches: []string{
TypePrefix + "very.long.Cooltype",
TypePrefix + "very.Cool",
TypePrefix + "very.long.package.OtherType",
},
rejects: []string{
TypePrefix + "very.longpkg.Cooltype",
TypePrefix + "very.Notcool",
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
matcher, err := MakeTypeMatcher(orchestratorpb.TypeSet_builder{
TypeUrls: tc.urls,
}.Build())
if tc.wantErr != "" {
assert.ErrLike(t, err, tc.wantErr)
return
}
assert.Len(t, matcher.patterns, tc.wantN)
assert.NoErr(t, err)
for _, matchCandidate := range tc.matches {
assert.True(t, matcher.Match(matchCandidate))
}
for _, rejectCandidate := range tc.rejects {
assert.False(t, matcher.Match(rejectCandidate))
}
})
}
}
func TestTypeSetBuilder(t *testing.T) {
t.Parallel()
t.Run(`empty`, func(t *testing.T) {
t.Parallel()
tb, err := TypeSetBuilder{}.Build()
assert.NoErr(t, err)
assert.Nil(t, tb)
})
t.Run(`fixed`, func(t *testing.T) {
t.Parallel()
tb, err := TypeSetBuilder{}.WithMessages(&emptypb.Empty{}, &structpb.Struct{}).Build()
assert.NoErr(t, err)
assert.Len(t, tb.GetTypeUrls(), 2)
})
t.Run(`normalized`, func(t *testing.T) {
t.Parallel()
tb, err := (TypeSetBuilder{}.
WithMessages(&emptypb.Empty{}, &structpb.Struct{}).
WithPackagesOf(&structpb.ListValue{}).
Build())
assert.NoErr(t, err)
assert.Len(t, tb.GetTypeUrls(), 1)
assert.Equal(t, TypePrefix+"google.protobuf.*", tb.GetTypeUrls()[0])
})
}