Skip to content
Snippets Groups Projects
Commit 86ad19ba authored by Cameron Swords's avatar Cameron Swords :ghost:
Browse files

Merge branch 'add-tests-to-executable-step' into 'main'

Add tests to ExecutableStep

See merge request !105
parents 842fb328 5eb43793
No related branches found
No related tags found
1 merge request!105Add tests to ExecutableStep
Pipeline #1461972854 passed
package runner_test
import (
"context"
"encoding/base64"
"errors"
"os"
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/structpb"
"gitlab.com/gitlab-org/step-runner/pkg/runner"
"gitlab.com/gitlab-org/step-runner/pkg/testutil/bldr"
"gitlab.com/gitlab-org/step-runner/proto"
)
func TestExecutableStep_Run(t *testing.T) {
if _, err := os.Stat("/bin/bash"); errors.Is(err, os.ErrNotExist) {
t.Skip("skipping test because /bin/bash doesn't exist")
}
t.Run("executes command", func(t *testing.T) {
tests := map[string]struct {
outputType proto.ValueType
outputValue string
expected interface{}
extractValueFn func(value *structpb.Value) interface{}
}{
"string output type": {
outputType: proto.ValueType_string,
outputValue: `value="hello world"`,
expected: "hello world",
extractValueFn: func(value *structpb.Value) interface{} { return value.GetStringValue() },
},
"raw string output type": {
outputType: proto.ValueType_raw_string,
outputValue: `value=hello world`,
expected: "hello world",
extractValueFn: func(value *structpb.Value) interface{} { return value.GetStringValue() },
},
"number output type": {
outputType: proto.ValueType_number,
outputValue: "value=56.77",
expected: 56.77,
extractValueFn: func(value *structpb.Value) interface{} { return value.GetNumberValue() },
},
"boolean output type": {
outputType: proto.ValueType_boolean,
outputValue: "value=true",
expected: true,
extractValueFn: func(value *structpb.Value) interface{} { return value.GetBoolValue() },
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
protoSpec := bldr.ProtoSpec().
WithOutputSpec(map[string]*proto.Spec_Content_Output{"value": {Type: test.outputType, Default: nil}}).
Build()
outputValueB64 := base64.StdEncoding.EncodeToString([]byte(test.outputValue))
protoDef := bldr.ProtoDef().
WithExecType("", []string{"/bin/bash", "-c", "echo " + outputValueB64 + " | base64 -d > ${{output_file}}"}).
Build()
specDef := bldr.ProtoSpecDef().WithSpec(protoSpec).WithDefinition(protoDef).Build()
globalCtx := bldr.GlobalContext().WithTempExportFile(t.TempDir()).Build()
stepsCtx := bldr.StepsContext().WithGlobalContext(globalCtx).Build()
step := runner.NewExecutableStep(runner.StepDefinedInGitLabJob, &runner.Params{}, specDef)
execStepResult, err := step.Run(context.Background(), stepsCtx, specDef)
require.NoError(t, err)
require.Equal(t, proto.StepResult_success, execStepResult.Status)
require.Equal(t, test.expected, test.extractValueFn(execStepResult.Outputs["value"]))
})
}
})
t.Run("delegates output", func(t *testing.T) {
stepResult := bldr.StepResult().
WithOutput("name", structpb.NewStringValue("amanda")).
WithSuccessStatus().
Build()
jsonStepResult, err := protojson.Marshal(stepResult)
require.NoError(t, err)
protoSpec := bldr.ProtoSpec().WithOutputMethod(proto.OutputMethod_delegate).Build()
protoDef := bldr.ProtoDef().
WithEnvVar("STEP_RESULT", base64.StdEncoding.EncodeToString(jsonStepResult)).
WithExecType("", []string{"/bin/bash", "-c", `echo ${{env.STEP_RESULT}} | base64 -d >${{output_file}}`}).
Build()
specDef := bldr.ProtoSpecDef().WithSpec(protoSpec).WithDefinition(protoDef).Build()
globalCtx := bldr.GlobalContext().WithTempExportFile(t.TempDir()).Build()
stepsCtx := bldr.StepsContext().WithTempOutputFile(t.TempDir()).WithGlobalContext(globalCtx).Build()
step := runner.NewExecutableStep(runner.StepDefinedInGitLabJob, &runner.Params{}, specDef)
execStepResult, err := step.Run(context.Background(), stepsCtx, specDef)
require.NoError(t, err)
require.Equal(t, proto.StepResult_success, execStepResult.Status)
require.Equal(t, "amanda", execStepResult.Outputs["name"].GetStringValue())
})
}
func TestExecutableStep_Describe(t *testing.T) {
protoDef := bldr.ProtoDef().WithExecType("", []string{"go", "run", "."}).Build()
specDef := bldr.ProtoSpecDef().WithDefinition(protoDef).Build()
......
......@@ -7,6 +7,7 @@ import (
"path/filepath"
"strings"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/structpb"
"gitlab.com/gitlab-org/step-runner/proto"
......@@ -113,7 +114,7 @@ func (f *Files) loadStepResultFromOutputFile() (*proto.StepResult, error) {
}
stepResult := &proto.StepResult{}
if err := json.Unmarshal(data, stepResult); err != nil {
if err := protojson.Unmarshal(data, stepResult); err != nil {
return nil, fmt.Errorf("reading output_file as a step result: %w", err)
}
......
......@@ -2,17 +2,21 @@ package bldr
import (
"bytes"
"os"
"path/filepath"
"gitlab.com/gitlab-org/step-runner/pkg/runner"
)
type GlobalContextBuilder struct {
job map[string]string
job map[string]string
exportFile string
}
func GlobalContext() *GlobalContextBuilder {
return &GlobalContextBuilder{
job: map[string]string{},
job: map[string]string{},
exportFile: "export",
}
}
......@@ -21,11 +25,23 @@ func (bldr *GlobalContextBuilder) WithJob(name, value string) *GlobalContextBuil
return bldr
}
func (bldr *GlobalContextBuilder) WithTempExportFile(tempDir string) *GlobalContextBuilder {
exportFile := filepath.Join(tempDir, "export")
_, err := os.Create(exportFile)
if err != nil {
panic(err)
}
bldr.exportFile = exportFile
return bldr
}
func (bldr *GlobalContextBuilder) Build() *runner.GlobalContext {
return &runner.GlobalContext{
WorkDir: ".",
Job: bldr.job,
ExportFile: "export",
ExportFile: bldr.exportFile,
Env: map[string]string{},
Stdout: &bytes.Buffer{},
Stderr: &bytes.Buffer{},
......
......@@ -4,12 +4,14 @@ import "gitlab.com/gitlab-org/step-runner/proto"
type ProtoDefinitionBuilder struct {
defType proto.DefinitionType
env map[string]string
exec *proto.Definition_Exec
}
func ProtoDef() *ProtoDefinitionBuilder {
return &ProtoDefinitionBuilder{
defType: proto.DefinitionType_exec,
env: map[string]string{},
exec: &proto.Definition_Exec{
Command: []string{"bash", "-c", "echo 'hello world'"},
WorkDir: "",
......@@ -17,6 +19,11 @@ func ProtoDef() *ProtoDefinitionBuilder {
}
}
func (bldr *ProtoDefinitionBuilder) WithEnvVar(name, value string) *ProtoDefinitionBuilder {
bldr.env[name] = value
return bldr
}
func (bldr *ProtoDefinitionBuilder) WithExecType(workDir string, command []string) *ProtoDefinitionBuilder {
bldr.defType = proto.DefinitionType_exec
bldr.exec = &proto.Definition_Exec{Command: command, WorkDir: workDir}
......@@ -29,7 +36,7 @@ func (bldr *ProtoDefinitionBuilder) Build() *proto.Definition {
Exec: bldr.exec,
Steps: nil,
Outputs: nil,
Env: nil,
Env: bldr.env,
Delegate: "",
}
}
......@@ -3,12 +3,14 @@ package bldr
import "gitlab.com/gitlab-org/step-runner/proto"
type ProtoSpecBuilder struct {
outputSpec map[string]*proto.Spec_Content_Output
outputMethod proto.OutputMethod
outputSpec map[string]*proto.Spec_Content_Output
}
func ProtoSpec() *ProtoSpecBuilder {
return &ProtoSpecBuilder{
outputSpec: make(map[string]*proto.Spec_Content_Output),
outputMethod: proto.OutputMethod_outputs,
outputSpec: make(map[string]*proto.Spec_Content_Output),
}
}
......@@ -17,12 +19,17 @@ func (bldr *ProtoSpecBuilder) WithOutputSpec(outputSpec map[string]*proto.Spec_C
return bldr
}
func (bldr *ProtoSpecBuilder) WithOutputMethod(outputMethod proto.OutputMethod) *ProtoSpecBuilder {
bldr.outputMethod = outputMethod
return bldr
}
func (bldr *ProtoSpecBuilder) Build() *proto.Spec {
return &proto.Spec{
Spec: &proto.Spec_Content{
Inputs: map[string]*proto.Spec_Content_Input{},
Outputs: bldr.outputSpec,
OutputMethod: proto.OutputMethod_outputs,
OutputMethod: bldr.outputMethod,
},
}
}
......@@ -14,6 +14,11 @@ func ProtoSpecDef() *ProtoSpecDefinitionBuilder {
}
}
func (bldr *ProtoSpecDefinitionBuilder) WithSpec(spec *proto.Spec) *ProtoSpecDefinitionBuilder {
bldr.spec = spec
return bldr
}
func (bldr *ProtoSpecDefinitionBuilder) WithDefinition(definition *proto.Definition) *ProtoSpecDefinitionBuilder {
bldr.definition = definition
return bldr
......
......@@ -20,6 +20,11 @@ func StepResult() *StepResultBuilder {
}
}
func (bldr *StepResultBuilder) WithOutput(name string, value *structpb.Value) *StepResultBuilder {
bldr.outputs[name] = value
return bldr
}
func (bldr *StepResultBuilder) WithSpecDef(specDef *proto.SpecDefinition) *StepResultBuilder {
bldr.specDef = specDef
return bldr
......
package bldr
import (
"os"
"path/filepath"
"google.golang.org/protobuf/types/known/structpb"
"gitlab.com/gitlab-org/step-runner/pkg/runner"
......@@ -8,16 +11,18 @@ import (
)
type StepsContextBuilder struct {
globalCtx *runner.GlobalContext
env map[string]string
inputs map[string]*structpb.Value
globalCtx *runner.GlobalContext
env map[string]string
inputs map[string]*structpb.Value
outputFile string
}
func StepsContext() *StepsContextBuilder {
return &StepsContextBuilder{
globalCtx: GlobalContext().Build(),
env: map[string]string{},
inputs: map[string]*structpb.Value{},
globalCtx: GlobalContext().Build(),
env: map[string]string{},
inputs: map[string]*structpb.Value{},
outputFile: "output",
}
}
......@@ -36,11 +41,23 @@ func (bldr *StepsContextBuilder) WithInput(name string, value *structpb.Value) *
return bldr
}
func (bldr *StepsContextBuilder) WithTempOutputFile(tempDir string) *StepsContextBuilder {
outputFile := filepath.Join(tempDir, "output")
_, err := os.Create(outputFile)
if err != nil {
panic(err)
}
bldr.outputFile = outputFile
return bldr
}
func (bldr *StepsContextBuilder) Build() *runner.StepsContext {
return &runner.StepsContext{
GlobalContext: bldr.globalCtx,
StepDir: ".",
OutputFile: "output",
OutputFile: bldr.outputFile,
Env: bldr.env,
Inputs: bldr.inputs,
Steps: map[string]*proto.StepResult{},
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment