Files
dokku/plugins/docker-options/dockeroptions_test.go
Jose Diaz-Gonzalez 55d7487d66 fix: split multi-flag input in docker-options
Multi-flag inputs (e.g. `--build-arg X=Y --link a --link b`) used to be stored as a single line, which bypassed the per-line filter that drops `--link` and similar flags for dockerfile-based builders. Each `--flag [value]` group is now stored as its own entry, and a `--process` typed after the app name is lifted into the subcommand flag instead of being stored as a docker option.
2026-04-29 13:15:51 -04:00

157 lines
3.7 KiB
Go

package dockeroptions
import (
"testing"
)
func TestSplitOptionString(t *testing.T) {
cases := []struct {
name string
input string
wantOptions []string
wantProcesses []string
wantErr bool
}{
{
name: "single flag with value",
input: "--link foo",
wantOptions: []string{"--link foo"},
},
{
name: "single flag with equals value",
input: "--build-arg FOO=bar",
wantOptions: []string{"--build-arg FOO=bar"},
},
{
name: "two link flags split",
input: "--link a --link b",
wantOptions: []string{"--link a", "--link b"},
},
{
name: "build-arg followed by two link flags",
input: "--build-arg X=Y --link a --link b",
wantOptions: []string{"--build-arg X=Y", "--link a", "--link b"},
},
{
name: "boolean flag then key+value flag",
input: "--rm --link foo",
wantOptions: []string{"--rm", "--link foo"},
},
{
name: "short flag with value",
input: "-v /tmp",
wantOptions: []string{"-v /tmp"},
},
{
name: "restart with embedded equals and colon",
input: "--restart=on-failure:5",
wantOptions: []string{"--restart=on-failure:5"},
},
{
name: "empty input",
input: "",
},
{
name: "whitespace only",
input: " \t ",
},
{
name: "value with whitespace gets shell-quoted",
input: `--label "foo bar"`,
wantOptions: []string{"--label 'foo bar'"},
},
{
name: "equals value with whitespace gets shell-quoted",
input: `--label key="hello world"`,
wantOptions: []string{"--label 'key=hello world'"},
},
{
name: "process flag in option content is lifted",
input: "--process web",
wantProcesses: []string{"web"},
},
{
name: "process equals form is lifted",
input: "--process=web",
wantProcesses: []string{"web"},
},
{
name: "process lifted from middle of multi-flag input",
input: "--link foo --process web --link bar",
wantOptions: []string{"--link foo", "--link bar"},
wantProcesses: []string{"web"},
},
{
name: "multiple processes lifted",
input: "--process web --process worker",
wantProcesses: []string{"web", "worker"},
},
{
name: "process without value errors",
input: "--process",
wantErr: true,
},
{
name: "unbalanced quote errors",
input: `--build-arg FOO="bar`,
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
gotOptions, gotProcesses, err := SplitOptionString(tc.input)
if tc.wantErr {
if err == nil {
t.Fatalf("SplitOptionString(%q) succeeded, want error", tc.input)
}
return
}
if err != nil {
t.Fatalf("SplitOptionString(%q): %v", tc.input, err)
}
if !equalStringSlice(gotOptions, tc.wantOptions) {
t.Errorf("options = %q, want %q", gotOptions, tc.wantOptions)
}
if !equalStringSlice(gotProcesses, tc.wantProcesses) {
t.Errorf("processes = %q, want %q", gotProcesses, tc.wantProcesses)
}
})
}
}
func TestQuoteShellArg(t *testing.T) {
cases := []struct {
in string
want string
}{
{"", "''"},
{"plain", "plain"},
{"FOO=bar", "FOO=bar"},
{"hello world", "'hello world'"},
{"it's", `'it'\''s'`},
{"$VAR", "'$VAR'"},
{"a|b", "'a|b'"},
}
for _, tc := range cases {
if got := quoteShellArg(tc.in); got != tc.want {
t.Errorf("quoteShellArg(%q) = %q, want %q", tc.in, got, tc.want)
}
}
}
func equalStringSlice(a, b []string) bool {
if len(a) == 0 && len(b) == 0 {
return true
}
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}