package main
import (
"reflect"
"testing"
)
func TestSplitWithEscapes(t *testing.T) {
testCases := []struct {
name string
input string
delimiter string
expected []string
}{
{
name: "simple comma split",
input: "a,b,c",
delimiter: ",",
expected: []string{"a", "b", "c"},
},
{
name: "escaped comma",
input: "a,b\\,c,d",
delimiter: ",",
expected: []string{"a", "b,c", "d"},
},
{
name: "email with escaped comma",
input: "user@domain.com\\,backup,*@example.org",
delimiter: ",",
expected: []string{"user@domain.com,backup", "*@example.org"},
},
{
name: "glob pattern with escaped comma",
input: "admin.*@company\\,inc.*,*@example.com",
delimiter: ",",
expected: []string{"admin.*@company,inc.*", "*@example.com"},
},
{
name: "empty string",
input: "",
delimiter: ",",
expected: []string{},
},
{
name: "single item",
input: "single",
delimiter: ",",
expected: []string{"single"},
},
{
name: "no escapes needed",
input: "user1@example.com,user2@test.org",
delimiter: ",",
expected: []string{"user1@example.com", "user2@test.org"},
},
{
name: "multiple escaped commas",
input: "a\\,b\\,c,d,e\\,f",
delimiter: ",",
expected: []string{"a,b,c", "d", "e,f"},
},
{
name: "whitespace trimming",
input: "a , b\\,c , d",
delimiter: ",",
expected: []string{"a", "b,c", "d"},
},
{
name: "different delimiter",
input: "a;b\\;c;d",
delimiter: ";",
expected: []string{"a", "b;c", "d"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := splitWithEscapes(tc.input, tc.delimiter)
if !reflect.DeepEqual(result, tc.expected) {
t.Errorf("Expected %v, got %v", tc.expected, result)
}
})
}
}
func TestParseAttributeMap(t *testing.T) {
testCases := []struct {
name string
input string
expected map[string][]string
}{
{
name: "empty string",
input: "",
expected: map[string][]string{},
},
{
name: "single key-value pair",
input: "/groups=admin",
expected: map[string][]string{
"/groups": {"admin"},
},
},
{
name: "multiple values for same key",
input: "/groups=admin,/groups=users",
expected: map[string][]string{
"/groups": {"admin", "users"},
},
},
{
name: "multiple keys",
input: "/groups=admin,/department=engineering",
expected: map[string][]string{
"/groups": {"admin"},
"/department": {"engineering"},
},
},
{
name: "nested key with JSON pointer",
input: "/org/team=platform",
expected: map[string][]string{
"/org/team": {"platform"},
},
},
{
name: "glob pattern value",
input: "/groups=*-admins,/email=*@example.com",
expected: map[string][]string{
"/groups": {"*-admins"},
"/email": {"*@example.com"},
},
},
{
name: "whitespace trimming",
input: " /groups = admin , /role = editor ",
expected: map[string][]string{
"/groups": {"admin"},
"/role": {"editor"},
},
},
{
name: "invalid format - no equals sign",
input: "invalid",
expected: map[string][]string{},
},
{
name: "invalid format - empty key",
input: "=value",
expected: map[string][]string{},
},
{
name: "invalid format - empty value",
input: "/key=",
expected: map[string][]string{},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := parseAttributeMap(tc.input)
if !reflect.DeepEqual(result, tc.expected) {
t.Errorf("Expected %v, got %v", tc.expected, result)
}
})
}
}
func TestParseHeaderMapping(t *testing.T) {
testCases := []struct {
name string
input string
expected map[string]string
}{
{
name: "empty string",
input: "",
expected: map[string]string{},
},
{
name: "single mapping",
input: "/email:X-Forwarded-Email",
expected: map[string]string{
"/email": "X-Forwarded-Email",
},
},
{
name: "multiple mappings",
input: "/email:X-Forwarded-Email,/preferred_username:X-Forwarded-User",
expected: map[string]string{
"/email": "X-Forwarded-Email",
"/preferred_username": "X-Forwarded-User",
},
},
{
name: "nested JSON pointer",
input: "/org/team:X-Forwarded-Team",
expected: map[string]string{
"/org/team": "X-Forwarded-Team",
},
},
{
name: "whitespace trimming",
input: " /email : X-Forwarded-Email , /sub : X-Forwarded-Sub ",
expected: map[string]string{
"/email": "X-Forwarded-Email",
"/sub": "X-Forwarded-Sub",
},
},
{
name: "no colon - skipped",
input: "invalid",
expected: map[string]string{},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := parseHeaderMapping(tc.input)
if !reflect.DeepEqual(result, tc.expected) {
t.Errorf("Expected %v, got %v", tc.expected, result)
}
})
}
}
func TestGetEnvWithDefault(t *testing.T) {
testCases := []struct {
name string
key string
def string
expected string
setEnv bool
envValue string
}{
{
name: "env var not set",
key: "TEST_KEY_NOT_SET",
def: "default_value",
expected: "default_value",
setEnv: false,
},
{
name: "env var set",
key: "TEST_KEY_SET",
def: "default_value",
expected: "env_value",
setEnv: true,
envValue: "env_value",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Setup
if tc.setEnv {
t.Setenv(tc.key, tc.envValue)
}
// Test
result := getEnvWithDefault(tc.key, tc.def)
if result != tc.expected {
t.Errorf("Expected %s, got %s", tc.expected, result)
}
})
}
}
func TestGetEnvBoolWithDefault(t *testing.T) {
testCases := []struct {
name string
key string
def bool
expected bool
setEnv bool
envValue string
}{
{
name: "env var not set - default false",
key: "TEST_BOOL_NOT_SET",
def: false,
expected: false,
setEnv: false,
},
{
name: "env var not set - default true",
key: "TEST_BOOL_NOT_SET2",
def: true,
expected: true,
setEnv: false,
},
{
name: "env var set to 'true'",
key: "TEST_BOOL_TRUE",
def: false,
expected: true,
setEnv: true,
envValue: "true",
},
{
name: "env var set to 'TRUE'",
key: "TEST_BOOL_TRUE_UPPER",
def: false,
expected: true,
setEnv: true,
envValue: "TRUE",
},
{
name: "env var set to '1'",
key: "TEST_BOOL_ONE",
def: false,
expected: true,
setEnv: true,
envValue: "1",
},
{
name: "env var set to 'false'",
key: "TEST_BOOL_FALSE",
def: true,
expected: false,
setEnv: true,
envValue: "false",
},
{
name: "env var set to '0'",
key: "TEST_BOOL_ZERO",
def: true,
expected: false,
setEnv: true,
envValue: "0",
},
{
name: "env var set to other value",
key: "TEST_BOOL_OTHER",
def: true,
expected: false,
setEnv: true,
envValue: "other",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Setup
if tc.setEnv {
t.Setenv(tc.key, tc.envValue)
}
// Test
result := getEnvBoolWithDefault(tc.key, tc.def)
if result != tc.expected {
t.Errorf("Expected %t, got %t", tc.expected, result)
}
})
}
}
func TestSplitCSV(t *testing.T) {
testCases := []struct {
name string
input string
expected []string
}{
{
name: "empty string",
input: "",
expected: nil,
},
{
name: "trims values",
input: " user1@example.com, user2@example.com ",
expected: []string{"user1@example.com", "user2@example.com"},
},
{
name: "keeps empty values",
input: "a,,b",
expected: []string{"a", "", "b"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := splitCSV(tc.input)
if !reflect.DeepEqual(result, tc.expected) {
t.Errorf("Expected %v, got %v", tc.expected, result)
}
})
}
}
func TestNewRootCommand_HTTPStreamingOnlyFlag(t *testing.T) {
t.Setenv("HTTP_STREAMING_ONLY", "")
var streamingOnly bool
var receivedTargets []string
runner := proxyRunnerFunc(func(listen string,
tlsListen string,
autoTLS bool,
tlsHost string,
tlsDirectoryURL string,
tlsAcceptTOS bool,
tlsCertFile string,
tlsKeyFile string,
dataPath string,
repositoryBackend string,
repositoryDSN string,
externalURL string,
googleClientID string,
googleClientSecret string,
googleAllowedUsers []string,
googleAllowedWorkspaces []string,
githubURL string,
githubAPIURL string,
githubClientID string,
githubClientSecret string,
githubAllowedUsers []string,
githubAllowedOrgs []string,
oidcConfigurationURL string,
oidcClientID string,
oidcClientSecret string,
oidcScopes []string,
oidcUserIDField string,
oidcProviderName string,
oidcAllowedUsers []string,
oidcAllowedUsersGlob []string,
oidcAllowedAttributes map[string][]string,
oidcAllowedAttributesGlob map[string][]string,
noProviderAutoSelect bool,
password string,
passwordHash string,
trustedProxy []string,
proxyHeaders []string,
proxyBearerToken string,
forwardAuthorizationHeader bool,
proxyTarget []string,
httpStreamingOnly bool,
headerMapping map[string]string,
headerMappingBase string,
) error {
streamingOnly = httpStreamingOnly
receivedTargets = proxyTarget
return nil
})
cmd := newRootCommand(runner)
cmd.SetArgs([]string{"--http-streaming-only", "http://backend"})
if err := cmd.Execute(); err != nil {
t.Fatalf("expected command to succeed, got error: %v", err)
}
if !streamingOnly {
t.Fatalf("expected httpStreamingOnly to be true when flag is set")
}
if len(receivedTargets) != 1 || receivedTargets[0] != "http://backend" {
t.Fatalf("expected proxyTarget to receive CLI args, got %v", receivedTargets)
}
}
func TestNewRootCommand_HTTPStreamingOnlyFromEnv(t *testing.T) {
t.Setenv("HTTP_STREAMING_ONLY", "true")
var streamingOnly bool
runner := proxyRunnerFunc(func(listen string,
tlsListen string,
autoTLS bool,
tlsHost string,
tlsDirectoryURL string,
tlsAcceptTOS bool,
tlsCertFile string,
tlsKeyFile string,
dataPath string,
repositoryBackend string,
repositoryDSN string,
externalURL string,
googleClientID string,
googleClientSecret string,
googleAllowedUsers []string,
googleAllowedWorkspaces []string,
githubURL string,
githubAPIURL string,
githubClientID string,
githubClientSecret string,
githubAllowedUsers []string,
githubAllowedOrgs []string,
oidcConfigurationURL string,
oidcClientID string,
oidcClientSecret string,
oidcScopes []string,
oidcUserIDField string,
oidcProviderName string,
oidcAllowedUsers []string,
oidcAllowedUsersGlob []string,
oidcAllowedAttributes map[string][]string,
oidcAllowedAttributesGlob map[string][]string,
noProviderAutoSelect bool,
password string,
passwordHash string,
trustedProxy []string,
proxyHeaders []string,
proxyBearerToken string,
forwardAuthorizationHeader bool,
proxyTarget []string,
httpStreamingOnly bool,
headerMapping map[string]string,
headerMappingBase string,
) error {
streamingOnly = httpStreamingOnly
return nil
})
cmd := newRootCommand(runner)
cmd.SetArgs([]string{"http://backend"})
if err := cmd.Execute(); err != nil {
t.Fatalf("expected command to succeed, got error: %v", err)
}
if !streamingOnly {
t.Fatalf("expected httpStreamingOnly to default to true from env var")
}
}
func TestNewRootCommand_ForwardAuthorizationFlag(t *testing.T) {
t.Setenv("PROXY_FORWARD_AUTHORIZATION", "")
var forwardAuthorization bool
runner := proxyRunnerFunc(func(listen string,
tlsListen string,
autoTLS bool,
tlsHost string,
tlsDirectoryURL string,
tlsAcceptTOS bool,
tlsCertFile string,
tlsKeyFile string,
dataPath string,
repositoryBackend string,
repositoryDSN string,
externalURL string,
googleClientID string,
googleClientSecret string,
googleAllowedUsers []string,
googleAllowedWorkspaces []string,
githubURL string,
githubAPIURL string,
githubClientID string,
githubClientSecret string,
githubAllowedUsers []string,
githubAllowedOrgs []string,
oidcConfigurationURL string,
oidcClientID string,
oidcClientSecret string,
oidcScopes []string,
oidcUserIDField string,
oidcProviderName string,
oidcAllowedUsers []string,
oidcAllowedUsersGlob []string,
oidcAllowedAttributes map[string][]string,
oidcAllowedAttributesGlob map[string][]string,
noProviderAutoSelect bool,
password string,
passwordHash string,
trustedProxy []string,
proxyHeaders []string,
proxyBearerToken string,
forwardAuthorizationHeader bool,
proxyTarget []string,
httpStreamingOnly bool,
headerMapping map[string]string,
headerMappingBase string,
) error {
forwardAuthorization = forwardAuthorizationHeader
return nil
})
cmd := newRootCommand(runner)
cmd.SetArgs([]string{"--proxy-forward-authorization", "http://backend"})
if err := cmd.Execute(); err != nil {
t.Fatalf("expected command to succeed, got error: %v", err)
}
if !forwardAuthorization {
t.Fatalf("expected forwardAuthorizationHeader to be true when flag is set")
}
}
func TestNewRootCommand_ForwardAuthorizationFromEnv(t *testing.T) {
t.Setenv("PROXY_FORWARD_AUTHORIZATION", "true")
var forwardAuthorization bool
runner := proxyRunnerFunc(func(listen string,
tlsListen string,
autoTLS bool,
tlsHost string,
tlsDirectoryURL string,
tlsAcceptTOS bool,
tlsCertFile string,
tlsKeyFile string,
dataPath string,
repositoryBackend string,
repositoryDSN string,
externalURL string,
googleClientID string,
googleClientSecret string,
googleAllowedUsers []string,
googleAllowedWorkspaces []string,
githubURL string,
githubAPIURL string,
githubClientID string,
githubClientSecret string,
githubAllowedUsers []string,
githubAllowedOrgs []string,
oidcConfigurationURL string,
oidcClientID string,
oidcClientSecret string,
oidcScopes []string,
oidcUserIDField string,
oidcProviderName string,
oidcAllowedUsers []string,
oidcAllowedUsersGlob []string,
oidcAllowedAttributes map[string][]string,
oidcAllowedAttributesGlob map[string][]string,
noProviderAutoSelect bool,
password string,
passwordHash string,
trustedProxy []string,
proxyHeaders []string,
proxyBearerToken string,
forwardAuthorizationHeader bool,
proxyTarget []string,
httpStreamingOnly bool,
headerMapping map[string]string,
headerMappingBase string,
) error {
forwardAuthorization = forwardAuthorizationHeader
return nil
})
cmd := newRootCommand(runner)
cmd.SetArgs([]string{"http://backend"})
if err := cmd.Execute(); err != nil {
t.Fatalf("expected command to succeed, got error: %v", err)
}
if !forwardAuthorization {
t.Fatalf("expected forwardAuthorizationHeader to default to true from env var")
}
}