blob: 944b95ac7329650db8386cc3bc4bafabb3b6892b [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"context"
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
buildbucketpb "go.chromium.org/luci/buildbucket/proto"
"google.golang.org/protobuf/types/known/structpb"
)
func TestResolveBuildProperties(t *testing.T) {
t.Parallel()
tests := []struct {
name string
inputProperties map[string]interface{}
versionedProperties map[string]interface{}
expectProperties map[string]interface{}
expectErr bool
}{
{
name: "passes through versioned properties",
inputProperties: map[string]interface{}{},
versionedProperties: map[string]interface{}{
"foo": "bar",
},
expectProperties: map[string]interface{}{
"foo": "bar",
},
},
{
name: "request properties override versioned properties",
inputProperties: map[string]interface{}{
"foo": "request_value",
},
versionedProperties: map[string]interface{}{
"foo": "versioned_value",
"bar": "baz",
},
expectProperties: map[string]interface{}{
"foo": "request_value",
"bar": "baz",
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ctx := context.Background()
builder := &buildbucketpb.BuilderID{
Project: "fuchsia", Bucket: "ci", Builder: "bldr",
}
build := &buildbucketpb.Build{
Builder: builder,
Input: &buildbucketpb.Build_Input{},
}
var err error
build.Input.Properties, err = structpb.NewStruct(test.inputProperties)
if err != nil {
t.Fatal(err)
}
integrationDir := t.TempDir()
propertiesPath := filepath.Join(
generatedDirForBuilder(integrationDir, builder), "properties",
builder.Bucket, builder.Builder+".json")
jsonProps, err := json.Marshal(test.versionedProperties)
if err != nil {
t.Fatal(err)
}
writeFile(t, propertiesPath, []byte(jsonProps))
if err := resolveBuildProperties(ctx, integrationDir, build); err != nil {
if !test.expectErr {
t.Fatalf("got unexpected error: %s", err)
}
return
} else if test.expectErr {
t.Fatalf("expected error, got nil")
}
if diff := cmp.Diff(test.expectProperties, build.Input.Properties.AsMap()); diff != "" {
t.Errorf("resolveBuildProperties(): output differs (-want, +got):\n%s", diff)
}
})
}
}
func TestReadVersionedProperties(t *testing.T) {
t.Parallel()
builder := &buildbucketpb.BuilderID{
Project: "fuchsia", Bucket: "ci", Builder: "bldr",
}
tests := []struct {
name string
// Mock files in integration.git (mapping from path to contents), with
// paths relative to the `generated` subdir corresponding to the
// builder's project.
files map[string]string
expectProperties map[string]interface{}
expectErr bool
}{
{
name: "properties json file",
files: map[string]string{
"properties/ci/bldr.json": `{"foo": "bar"}`,
},
expectProperties: map[string]interface{}{
"foo": "bar",
},
},
{
name: "bucket proto file",
files: map[string]string{
"builders/ci/bldr.textproto": `
swarming {
builders {
properties:
'{'
' "nested_map": {'
' "server": "url"'
' },'
' "number": 1234,'
' "bool": true'
'}'
}
}`,
},
expectProperties: map[string]interface{}{
"nested_map": map[string]interface{}{
"server": "url",
},
"number": float64(1234),
"bool": true,
},
},
{
name: "bucket proto file with old-style path",
files: map[string]string{
"for_review_only/buildbucket/ci/bldr.textproto": `
swarming {
builders {
properties:
'{'
' "nested_map": {'
' "server": "url"'
' },'
' "number": 1234,'
' "bool": true'
'}'
}
}`,
},
expectProperties: map[string]interface{}{
"nested_map": map[string]interface{}{
"server": "url",
},
"number": float64(1234),
"bool": true,
},
},
{
name: "bucket proto file with multiple builders",
files: map[string]string{
"builders/ci/bldr.textproto": `
swarming {
builders {
properties: '{"mock_property": ""}'
}
builders {
properties: '{"mock_property": ""}'
}
}`,
},
expectErr: true,
},
{
name: "empty bucket proto file",
files: map[string]string{
"builders/ci/bldr.textproto": ``,
},
expectErr: true,
},
{
name: "no property file present",
files: map[string]string{},
expectErr: true,
},
{
name: "prefers JSON files to other property file formats",
files: map[string]string{
"properties/ci/bldr.json": `{"foo": "json_property"}`,
"builders/ci/bldr.textproto": `
swarming {
builders {
properties: '{"foo": "builders_proto_property"}'
}
}`,
"for_review_only/buildbucket/ci/bldr.textproto": `
swarming {
builders {
properties: '{"foo": "for_review_only_proto_property"}'
}
}`,
},
expectProperties: map[string]interface{}{
"foo": "json_property",
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ctx := context.Background()
integrationDir := t.TempDir()
projectDir := generatedDirForBuilder(integrationDir, builder)
for relPath, contents := range test.files {
absPath := filepath.Join(
append([]string{projectDir}, strings.Split(relPath, "/")...)...)
writeFile(t, absPath, []byte(contents))
}
properties, err := readVersionedProperties(ctx, integrationDir, builder)
if err != nil {
if !test.expectErr {
t.Fatal(err)
}
return
} else if test.expectErr {
t.Fatalf("expected error, got nil")
}
if diff := cmp.Diff(test.expectProperties, properties.AsMap()); diff != "" {
t.Errorf("readVersionedProperties(): output differs (-want, +got):\n%s", diff)
}
})
}
}
func writeFile(t *testing.T, path string, contents []byte) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(path, contents, 0o600); err != nil {
t.Fatal(err)
}
}