configvalue_test.go

  1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  2//
  3// SPDX-License-Identifier: LicenseRef-MutuaL-1.2
  4
  5package configvalue
  6
  7import (
  8	"context"
  9	"os"
 10	"runtime"
 11	"strconv"
 12	"strings"
 13	"testing"
 14	"time"
 15)
 16
 17func TestResolveACIDConfigValuesResolution1InterpolatesEnvironment(t *testing.T) {
 18	t.Setenv("COOKED_USERNAME_FROM_MY_SHELL", "ada")
 19	t.Setenv("COOKED_SUFFIX", "lovelace")
 20
 21	tests := []struct {
 22		name   string
 23		config string
 24		want   string
 25	}{
 26		{name: "plain literal", config: "literal", want: "literal"},
 27		{name: "dollar prefix", config: "$COOKED_USERNAME_FROM_MY_SHELL", want: "ada"},
 28		{name: "braced reference", config: "${COOKED_USERNAME_FROM_MY_SHELL}-${COOKED_SUFFIX}", want: "ada-lovelace"},
 29		{name: "escaped dollar and bang", config: "$$$COOKED_USERNAME_FROM_MY_SHELL$!", want: "$ada!"},
 30	}
 31
 32	resolver := NewResolver()
 33	for _, tt := range tests {
 34		t.Run(tt.name, func(t *testing.T) {
 35			got, ok := resolver.Resolve(context.Background(), tt.config)
 36			if !ok {
 37				t.Fatalf("Resolve(%q) ok = false", tt.config)
 38			}
 39
 40			if got != tt.want {
 41				t.Fatalf("Resolve(%q) = %q, want %q", tt.config, got, tt.want)
 42			}
 43		})
 44	}
 45}
 46
 47func TestResolveACIDConfigValuesResolution1RejectsMissingEnvironment(t *testing.T) {
 48	resolver := NewResolver()
 49
 50	got, ok := resolver.Resolve(context.Background(), "$COOKED_MISSING_VALUE")
 51	if ok {
 52		t.Fatalf("Resolve ok = true, got %q", got)
 53	}
 54}
 55
 56func TestResolveACIDConfigValuesCommands3CachesCommandResults(t *testing.T) {
 57	if runtime.GOOS == "windows" {
 58		t.Skip("shell command syntax is POSIX-specific")
 59	}
 60
 61	resolver := NewResolver()
 62	command := "!printf cooked"
 63
 64	first, ok := resolver.Resolve(context.Background(), command)
 65	if !ok {
 66		t.Fatal("first command resolution failed")
 67	}
 68
 69	second, ok := resolver.Resolve(context.Background(), command)
 70	if !ok {
 71		t.Fatal("second command resolution failed")
 72	}
 73
 74	if first != "cooked" || second != "cooked" {
 75		t.Fatalf("resolved command values = %q and %q, want cooked", first, second)
 76	}
 77
 78	resolver.CommandTimeout = time.Nanosecond
 79	third, ok := resolver.Resolve(context.Background(), command)
 80	if !ok || third != "cooked" {
 81		t.Fatalf("cached command value = %q, %v; want cooked, true", third, ok)
 82	}
 83}
 84
 85func TestResolveACIDAuthenticationCredentials17RejectsOversizedCommandStdout(t *testing.T) {
 86	t.Setenv("COOKED_CONFIGVALUE_FAKE_SHELL", "1")
 87	t.Setenv("COOKED_CONFIGVALUE_FAKE_SHELL_STDOUT_BYTES", strconv.Itoa(maxCommandStdoutBytes+1))
 88
 89	resolver := NewResolver()
 90	resolver.shellCommand = func(string) (string, []string) {
 91		return os.Args[0], []string{"-test.run=TestConfigValueFakeShell"}
 92	}
 93
 94	got, ok := resolver.Resolve(context.Background(), "!oversized")
 95	if ok {
 96		t.Fatalf("oversized command stdout resolved with length %d, want unresolved", len(got))
 97	}
 98	if got != "" {
 99		t.Fatalf("oversized command stdout returned non-empty value length %d, want empty", len(got))
100	}
101}
102
103func TestConfigValueFakeShell(t *testing.T) {
104	if os.Getenv("COOKED_CONFIGVALUE_FAKE_SHELL") != "1" {
105		return
106	}
107
108	byteCount, err := strconv.Atoi(os.Getenv("COOKED_CONFIGVALUE_FAKE_SHELL_STDOUT_BYTES"))
109	if err != nil {
110		os.Exit(2)
111	}
112	if _, err := os.Stdout.WriteString(strings.Repeat("x", byteCount)); err != nil {
113		os.Exit(2)
114	}
115
116	os.Exit(0)
117}