| // Copyright 2024 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 | |
| // | |
| // https://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 run | |
| import ( | |
| "context" | |
| "fmt" | |
| "reflect" | |
| "strings" | |
| "testing" | |
| ) | |
| type command struct { | |
| arguments []string | |
| result string | |
| shouldFail bool | |
| } | |
| // testRunner is a fake runner for testing, since with batch we are not really | |
| // interested in testing the actual command execution but the layer on top of | |
| // that (parsing command, processing template and sequencing execution) we can | |
| // fake all test executions. | |
| type testRunner struct { | |
| commands []*command | |
| } | |
| func (tr *testRunner) findCommand(name string, args ...string) *command { | |
| for _, curr := range tr.commands { | |
| if reflect.DeepEqual(curr.arguments, append([]string{name}, args...)) { | |
| return curr | |
| } | |
| } | |
| return nil | |
| } | |
| func (tr *testRunner) WithContext(ctx context.Context, opts Options) (*Result, error) { | |
| cmd := tr.findCommand(opts.Name, opts.Args...) | |
| if cmd == nil { | |
| return nil, fmt.Errorf("command %s not found", opts.Name) | |
| } | |
| if cmd.shouldFail { | |
| return nil, fmt.Errorf("command %s should fail", opts.Name) | |
| } | |
| return &Result{Output: cmd.result, OutputType: OutputCombined}, nil | |
| } | |
| func prepareTest(t *testing.T) *testRunner { | |
| t.Helper() | |
| result := &testRunner{} | |
| defaultClient := Client | |
| Client = result | |
| t.Cleanup(func() { | |
| Client = defaultClient | |
| }) | |
| return result | |
| } | |
| func TestCommandSpecSuccess(t *testing.T) { | |
| type commandData struct { | |
| Data string | |
| } | |
| tests := []struct { | |
| name string | |
| spec CommandSpec | |
| data commandData | |
| command string | |
| }{ | |
| { | |
| name: "success_echo_data", | |
| spec: CommandSpec{"echo {{.Data}}", "failed to echo {{.Data}}"}, | |
| data: commandData{"foobar"}, | |
| command: "echo foobar", | |
| }, | |
| { | |
| name: "success_cat_data", | |
| spec: CommandSpec{"cat {{.Data}}", "failed to cat file {{.Data}}"}, | |
| data: commandData{"/proc/cpuinfo"}, | |
| command: "cat /proc/cpuinfo", | |
| }, | |
| { | |
| name: "success_echo_foobar_data", | |
| spec: CommandSpec{"echo 'foobar' >> {{.Data}}", "failed to write to file {{.Data}}"}, | |
| data: commandData{"/tmp/file.data"}, | |
| command: "echo 'foobar' >> /tmp/file.data", | |
| }, | |
| } | |
| testRunner := prepareTest(t) | |
| for _, curr := range tests { | |
| testRunner.commands = append(testRunner.commands, &command{ | |
| arguments: strings.Split(curr.command, " "), | |
| shouldFail: false, | |
| }) | |
| } | |
| for _, curr := range tests { | |
| t.Run(curr.name, func(t *testing.T) { | |
| if err := curr.spec.WithContext(context.Background(), curr.data); err != nil { | |
| t.Errorf("RunQuiet(%v) = %v, want: nil", curr.data, err) | |
| } | |
| }) | |
| } | |
| } | |
| func TestCommandSpecFailure(t *testing.T) { | |
| type commandData struct { | |
| Data string | |
| } | |
| tests := []struct { | |
| name string | |
| spec CommandSpec | |
| data commandData | |
| command string | |
| result string | |
| internalError error | |
| }{ | |
| { | |
| name: "empty_command", | |
| spec: CommandSpec{"", "failed to echo {{.Data}}"}, | |
| data: commandData{"foobar"}, | |
| internalError: ErrCommandTemplate, | |
| }, | |
| { | |
| name: "invalid_field", | |
| spec: CommandSpec{"invalid field {{.UnknownField}}", "failed to echo {{.Data}}"}, | |
| data: commandData{"foobar"}, | |
| internalError: ErrCommandTemplate, | |
| }, | |
| { | |
| name: "invalid_template", | |
| spec: CommandSpec{"echo {{.Data}}", "invalid data {{.UnknownField}}"}, | |
| data: commandData{"foobar"}, | |
| command: "echo foobar", | |
| internalError: ErrTemplateError, | |
| }, | |
| { | |
| name: "invalid_data_command", | |
| spec: CommandSpec{"echoxx {{.Data}}", "invalid data {{.Data}}"}, | |
| data: commandData{"foobar"}, | |
| command: "echoxx foobar", | |
| result: "invalid data foobar", | |
| }, | |
| } | |
| for _, curr := range tests { | |
| t.Run(curr.name, func(t *testing.T) { | |
| err := curr.spec.WithContext(context.Background(), curr.data) | |
| if curr.internalError != nil && err != curr.internalError { | |
| t.Errorf("RunQuiet(%v) = %v, want: %v", curr.data, err, curr.internalError) | |
| } else if err == nil { | |
| t.Errorf("RunQuiet(%v) = %v, want: non-nil", curr.data, err) | |
| } | |
| }) | |
| } | |
| } |