blob: 64f1b30352d4d32fcd77172d57d72c2ffd948a8a [file] [edit]
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"flag"
"testing"
"github.com/google/go-cmp/cmp"
)
// mockLookupEnv creates a lookup function that reads from the provided map.
func mockLookupEnv(env map[string]string) func(key string) (string, bool) {
return func(key string) (string, bool) {
val, ok := env[key]
return val, ok
}
}
// newDefaultConfigForTest creates a mainConfig struct and populates it with default values
// by registering flags on a new FlagSet and then parsing empty arguments.
func newDefaultConfigForTest(env map[string]string) *mainConfig {
fs := flag.NewFlagSet("default", flag.ContinueOnError)
cfg := &mainConfig{}
registerRsproxyFlags(fs, cfg)
// Parse empty arguments to ensure default values are applied to cfg.
fs.Parse([]string{})
// Parse environment variables.
parseFromEnv(fs, mockLookupEnv(env))
return cfg
}
func TestParseUnset(t *testing.T) {
fs := flag.NewFlagSet(t.Name(), flag.ContinueOnError)
cfg := newDefaultConfigForTest(nil)
registerRsproxyFlags(fs, cfg)
if err := FlagsToConfig(fs, cfg, []string{}, mockLookupEnv(nil)); err != nil {
t.Fatalf("FlagsToConfig failed: %v", err)
}
expectedConfig := newDefaultConfigForTest(nil)
if diff := cmp.Diff(expectedConfig, cfg); diff != "" {
t.Errorf("FlagsToConfig mismatch (-want +got):\n%s", diff)
}
}
func TestParseSet(t *testing.T) {
// Test environment variable overriding command-line argument for upload_requests_file.
env := map[string]string{
"RS_upload_requests_file": "env_file.txt",
}
fs := flag.NewFlagSet(t.Name(), flag.ContinueOnError)
cfg := newDefaultConfigForTest(env)
registerRsproxyFlags(fs, cfg)
args := []string{"--upload_requests_file=cmd_file.txt"}
if err := FlagsToConfig(fs, cfg, args, mockLookupEnv(env)); err != nil {
t.Fatalf("FlagsToConfig failed: %v", err)
}
expectedConfig := newDefaultConfigForTest(env)
expectedConfig.UploadRequestsFile = "env_file.txt" // Env should override command line
if diff := cmp.Diff(expectedConfig, cfg); diff != "" {
t.Errorf("FlagsToConfig mismatch (-want +got):\n%s", diff)
}
// Test environment variable overriding command-line argument for client-config flag (RS_rs_service).
env = map[string]string{
"RS_rs_service": "env_rs_service",
}
fs = flag.NewFlagSet(t.Name(), flag.ContinueOnError)
cfg = newDefaultConfigForTest(env)
registerRsproxyFlags(fs, cfg)
args = []string{"--rs_service=cmd_rs_service"}
if err := FlagsToConfig(fs, cfg, args, mockLookupEnv(env)); err != nil {
t.Fatalf("FlagsToConfig failed: %v", err)
}
expectedConfig = newDefaultConfigForTest(env)
expectedConfig.ClientConfig.ResultStoreService = "env_rs_service" // Env should override command line
if diff := cmp.Diff(expectedConfig, cfg); diff != "" {
t.Errorf("FlagsToConfig mismatch (-want +got):\n%s", diff)
}
}
func TestParseDefault(t *testing.T) {
fs := flag.NewFlagSet(t.Name(), flag.ContinueOnError)
cfg := newDefaultConfigForTest(nil)
registerRsproxyFlags(fs, cfg)
if err := FlagsToConfig(fs, cfg, []string{}, mockLookupEnv(nil)); err != nil {
t.Fatalf("FlagsToConfig failed: %v", err)
}
expectedConfig := newDefaultConfigForTest(nil)
if diff := cmp.Diff(expectedConfig, cfg); diff != "" {
t.Errorf("FlagsToConfig mismatch (-want +got):\n%s", diff)
}
}
func TestWrappedCommandWithReadyFileFails(t *testing.T) {
cfg := &mainConfig{
ReadyFile: "some_file",
WrappedCommand: []string{"echo", "hello"},
}
err := verifyFlags(cfg)
if err == nil {
t.Errorf("Expected an error when using --ready_file with a wrapped command, but got none")
}
}
func TestParseSetRSWins(t *testing.T) {
env := map[string]string{
"FLAG_upload_requests_file": "flag_file.txt",
"RS_upload_requests_file": "rs_file.txt",
}
fs := flag.NewFlagSet(t.Name(), flag.ContinueOnError)
cfg := newDefaultConfigForTest(env)
registerRsproxyFlags(fs, cfg)
if err := FlagsToConfig(fs, cfg, []string{}, mockLookupEnv(env)); err != nil {
t.Fatalf("FlagsToConfig failed: %v", err)
}
expectedConfig := newDefaultConfigForTest(env)
expectedConfig.UploadRequestsFile = "rs_file.txt"
if diff := cmp.Diff(expectedConfig, cfg); diff != "" {
t.Errorf("FlagsToConfig mismatch (-want +got):\n%s", diff)
}
// Test client-config flag
env = map[string]string{
"FLAG_rs_service": "flag_rs_service",
"RS_rs_service": "rs_rs_service",
}
fs = flag.NewFlagSet(t.Name(), flag.ContinueOnError)
cfg = newDefaultConfigForTest(env)
registerRsproxyFlags(fs, cfg)
if err := FlagsToConfig(fs, cfg, []string{}, mockLookupEnv(env)); err != nil {
t.Fatalf("FlagsToConfig failed: %v", err)
}
expectedConfig = newDefaultConfigForTest(env)
expectedConfig.ClientConfig.ResultStoreService = "rs_rs_service"
if diff := cmp.Diff(expectedConfig, cfg); diff != "" {
t.Errorf("FlagsToConfig mismatch (-want +got):\n%s", diff)
}
}
func TestFlagsToConfig_CommandLineArgs(t *testing.T) {
fs := flag.NewFlagSet(t.Name(), flag.ContinueOnError)
cfg := newDefaultConfigForTest(nil)
registerRsproxyFlags(fs, cfg)
args := []string{
"--project_id=my-test-project",
"--quiet",
"--upload_requests_file=/tmp/reqs.pb",
}
if err := FlagsToConfig(fs, cfg, args, mockLookupEnv(nil)); err != nil {
t.Fatalf("FlagsToConfig failed: %v", err)
}
expectedConfig := newDefaultConfigForTest(nil)
expectedConfig.UploadRequestsFile = "/tmp/reqs.pb"
expectedConfig.ProjectId = "my-test-project"
expectedConfig.QuietMode = true
// cmp.Diff provides a detailed diff of the two structs.
if diff := cmp.Diff(expectedConfig, cfg); diff != "" {
t.Errorf("FlagsToConfig mismatch (-want +got):\n%s", diff)
}
}
func TestTLSFlags(t *testing.T) {
tests := []struct {
name string
args []string
env map[string]string
verifyFn func(*testing.T, *mainConfig)
}{
{
name: "tls_server_name_flag",
args: []string{"--tls_server_name=global.example.com"},
verifyFn: func(t *testing.T, cfg *mainConfig) {
if cfg.TLSServerName != "global.example.com" {
t.Errorf("TLSServerName = %q, want %q", cfg.TLSServerName, "global.example.com")
}
},
},
{
name: "rs_tls_server_name_flag",
args: []string{"--rs_tls_server_name=rs.example.com"},
verifyFn: func(t *testing.T, cfg *mainConfig) {
if cfg.ResultStoreTLSServerName != "rs.example.com" {
t.Errorf("ResultStoreTLSServerName = %q, want %q", cfg.ResultStoreTLSServerName, "rs.example.com")
}
},
},
{
name: "cas_tls_server_name_flag",
args: []string{"--cas_tls_server_name=cas.example.com"},
verifyFn: func(t *testing.T, cfg *mainConfig) {
if cfg.CASTLSServerName != "cas.example.com" {
t.Errorf("CASTLSServerName = %q, want %q", cfg.CASTLSServerName, "cas.example.com")
}
},
},
{
name: "rs_tls_server_name_env",
env: map[string]string{"RS_rs_tls_server_name": "rs-env.example.com"},
verifyFn: func(t *testing.T, cfg *mainConfig) {
if cfg.ResultStoreTLSServerName != "rs-env.example.com" {
t.Errorf("ResultStoreTLSServerName = %q, want %q", cfg.ResultStoreTLSServerName, "rs-env.example.com")
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fs := flag.NewFlagSet(tt.name, flag.ContinueOnError)
cfg := newDefaultConfigForTest(tt.env)
registerRsproxyFlags(fs, cfg)
if err := FlagsToConfig(fs, cfg, tt.args, mockLookupEnv(tt.env)); err != nil {
t.Fatalf("FlagsToConfig failed: %v", err)
}
tt.verifyFn(t, cfg)
})
}
}
func TestStringSliceDeduplication(t *testing.T) {
tests := []struct {
name string
args []string
expected []string
}{
{
name: "no_duplicates",
args: []string{"--post_build_uploads=a,b", "--post_build_uploads=c"},
expected: []string{"a", "b", "c"},
},
{
name: "with_duplicates_repeatable",
args: []string{"--post_build_uploads=a", "--post_build_uploads=a"},
expected: []string{"a"},
},
{
name: "with_duplicates_comma_separated",
args: []string{"--post_build_uploads=a,a"},
expected: []string{"a"},
},
{
name: "mixed_duplicates",
args: []string{"--post_build_uploads=a,b", "--post_build_uploads=b,c", "--post_build_uploads=a"},
expected: []string{"a", "b", "c"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fs := flag.NewFlagSet(tt.name, flag.ContinueOnError)
cfg := &mainConfig{}
registerRsproxyFlags(fs, cfg)
if err := FlagsToConfig(fs, cfg, tt.args, mockLookupEnv(nil)); err != nil {
t.Fatalf("FlagsToConfig failed: %v", err)
}
if diff := cmp.Diff(tt.expected, []string(cfg.PostBuildUploads)); diff != "" {
t.Errorf("PostBuildUploads deduplication mismatch (-want +got):\n%s", diff)
}
})
}
}