214 lines
5.9 KiB
Go
214 lines
5.9 KiB
Go
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
|
// Use of this source code is governed by the Polyform License
|
|
// that can be found in the LICENSE file.
|
|
|
|
// +build !windows
|
|
|
|
package compiler
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io/ioutil"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/dchest/uniuri"
|
|
"github.com/drone-runners/drone-runner-docker/engine"
|
|
"github.com/drone-runners/drone-runner-docker/engine/resource"
|
|
"github.com/drone/drone-go/drone"
|
|
"github.com/drone/runner-go/environ/provider"
|
|
"github.com/drone/runner-go/manifest"
|
|
"github.com/drone/runner-go/pipeline/runtime"
|
|
"github.com/drone/runner-go/registry"
|
|
"github.com/drone/runner-go/secret"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
)
|
|
|
|
var nocontext = context.Background()
|
|
|
|
// dummy function that returns a non-random string for testing.
|
|
// it is used in place of the random function.
|
|
func notRandom() string {
|
|
return "random"
|
|
}
|
|
|
|
// This test verifies the pipeline dependency graph. When no
|
|
// dependency graph is defined, a default dependency graph is
|
|
// automatically defined to run steps serially.
|
|
func TestCompile_Serial(t *testing.T) {
|
|
testCompile(t, "testdata/serial.yml", "testdata/serial.json")
|
|
}
|
|
|
|
// This test verifies the pipeline dependency graph. It also
|
|
// verifies that pipeline steps with no dependencies depend on
|
|
// the initial clone step.
|
|
func TestCompile_Graph(t *testing.T) {
|
|
testCompile(t, "testdata/graph.yml", "testdata/graph.json")
|
|
}
|
|
|
|
// This test verifies no clone step exists in the pipeline if
|
|
// cloning is disabled.
|
|
func TestCompile_CloneDisabled_Serial(t *testing.T) {
|
|
testCompile(t, "testdata/noclone_serial.yml", "testdata/noclone_serial.json")
|
|
}
|
|
|
|
// This test verifies no clone step exists in the pipeline if
|
|
// cloning is disabled. It also verifies no pipeline steps
|
|
// depend on a clone step.
|
|
func TestCompile_CloneDisabled_Graph(t *testing.T) {
|
|
testCompile(t, "testdata/noclone_graph.yml", "testdata/noclone_graph.json")
|
|
}
|
|
|
|
// This test verifies that steps are disabled if conditions
|
|
// defined in the when block are not satisfied.
|
|
func TestCompile_Match(t *testing.T) {
|
|
ir := testCompile(t, "testdata/match.yml", "testdata/match.json")
|
|
if ir.Steps[0].RunPolicy != runtime.RunOnSuccess {
|
|
t.Errorf("Expect run on success")
|
|
}
|
|
if ir.Steps[1].RunPolicy != runtime.RunNever {
|
|
t.Errorf("Expect run never")
|
|
}
|
|
}
|
|
|
|
// This test verifies that steps configured to run on both
|
|
// success or failure are configured to always run.
|
|
func TestCompile_RunAlways(t *testing.T) {
|
|
ir := testCompile(t, "testdata/run_always.yml", "testdata/run_always.json")
|
|
if ir.Steps[0].RunPolicy != runtime.RunAlways {
|
|
t.Errorf("Expect run always")
|
|
}
|
|
}
|
|
|
|
// This test verifies that steps configured to run on failure
|
|
// are configured to run on failure.
|
|
func TestCompile_RunFailure(t *testing.T) {
|
|
ir := testCompile(t, "testdata/run_failure.yml", "testdata/run_failure.json")
|
|
if ir.Steps[0].RunPolicy != runtime.RunOnFailure {
|
|
t.Errorf("Expect run on failure")
|
|
}
|
|
}
|
|
|
|
// This test verifies that secrets defined in the yaml are
|
|
// requested and stored in the intermediate representation
|
|
// at compile time.
|
|
func TestCompile_Secrets(t *testing.T) {
|
|
manifest, _ := manifest.ParseFile("testdata/secret.yml")
|
|
|
|
compiler := &Compiler{
|
|
Environ: provider.Static(nil),
|
|
Registry: registry.Static(nil),
|
|
Secret: secret.StaticVars(map[string]string{
|
|
"token": "3DA541559918A808C2402BBA5012F6C60B27661C",
|
|
"password": "password",
|
|
"my_username": "octocat",
|
|
}),
|
|
}
|
|
args := Args{
|
|
Repo: &drone.Repo{},
|
|
Build: &drone.Build{},
|
|
Stage: &drone.Stage{},
|
|
System: &drone.System{},
|
|
Netrc: &drone.Netrc{},
|
|
Manifest: manifest,
|
|
Pipeline: manifest.Resources[0].(*resource.Pipeline),
|
|
Secret: secret.Static(nil),
|
|
}
|
|
|
|
ir := compiler.Compile(nocontext, args)
|
|
got := ir.Steps[0].Secrets
|
|
want := []*engine.Secret{
|
|
{
|
|
Name: "my_password",
|
|
Env: "PASSWORD",
|
|
Data: nil, // secret not found, data nil
|
|
Mask: true,
|
|
},
|
|
{
|
|
Name: "my_username",
|
|
Env: "USERNAME",
|
|
Data: []byte("octocat"), // secret found
|
|
Mask: true,
|
|
},
|
|
}
|
|
if diff := cmp.Diff(got, want); len(diff) != 0 {
|
|
// TODO(bradrydzewski) ordering is not guaranteed. this
|
|
// unit tests needs to be adjusted accordingly.
|
|
t.Skipf(diff)
|
|
}
|
|
}
|
|
|
|
// helper function parses and compiles the source file and then
|
|
// compares to a golden json file.
|
|
func testCompile(t *testing.T, source, golden string) *engine.Spec {
|
|
// replace the default random function with one that
|
|
// is deterministic, for testing purposes.
|
|
random = notRandom
|
|
|
|
// restore the default random function and the previously
|
|
// specified temporary directory
|
|
defer func() {
|
|
random = uniuri.New
|
|
}()
|
|
|
|
manifest, err := manifest.ParseFile(source)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return nil
|
|
}
|
|
|
|
compiler := &Compiler{
|
|
Environ: provider.Static(nil),
|
|
Registry: registry.Static(nil),
|
|
Secret: secret.StaticVars(map[string]string{
|
|
"token": "3DA541559918A808C2402BBA5012F6C60B27661C",
|
|
"password": "password",
|
|
"my_username": "octocat",
|
|
}),
|
|
}
|
|
args := Args{
|
|
Repo: &drone.Repo{},
|
|
Build: &drone.Build{Target: "master"},
|
|
Stage: &drone.Stage{},
|
|
System: &drone.System{},
|
|
Netrc: &drone.Netrc{Machine: "github.com", Login: "octocat", Password: "correct-horse-battery-staple"},
|
|
Manifest: manifest,
|
|
Pipeline: manifest.Resources[0].(*resource.Pipeline),
|
|
Secret: secret.Static(nil),
|
|
}
|
|
|
|
got := compiler.Compile(nocontext, args)
|
|
|
|
raw, err := ioutil.ReadFile(golden)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
want := new(engine.Spec)
|
|
err = json.Unmarshal(raw, want)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
opts := cmp.Options{
|
|
cmpopts.IgnoreUnexported(engine.Spec{}),
|
|
cmpopts.IgnoreFields(engine.Step{}, "Envs", "Secrets", "Labels"),
|
|
cmpopts.IgnoreFields(engine.Network{}, "Labels"),
|
|
cmpopts.IgnoreFields(engine.VolumeEmptyDir{}, "Labels"),
|
|
}
|
|
if diff := cmp.Diff(got, want, opts...); len(diff) != 0 {
|
|
t.Errorf(diff)
|
|
}
|
|
|
|
return got
|
|
}
|
|
|
|
func dump(v interface{}) {
|
|
enc := json.NewEncoder(os.Stdout)
|
|
enc.SetIndent("", " ")
|
|
enc.Encode(v)
|
|
}
|