Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions cmd/flux/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package main

import (
"bufio"
"bytes"
"context"
"io"
"io/ioutil"
"os"
"testing"

"github.com/fluxcd/flux2/internal/utils"
"github.com/google/go-cmp/cmp"
"github.com/mattn/go-shellwords"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/controller-runtime/pkg/client"
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func init() {
// Ensure tests print consistent timestamps regardless of timezone
os.Setenv("TZ", "UTC")
}

func readYamlObjects(objectFile string) ([]client.Object, error) {
obj, err := ioutil.ReadFile(objectFile)
if err != nil {
return nil, err
}
objects := []client.Object{}
reader := k8syaml.NewYAMLReader(bufio.NewReader(bytes.NewReader(obj)))
for {
doc, err := reader.Read()
if err != nil {
if err == io.EOF {
break
}
}
unstructuredObj := &unstructured.Unstructured{}
decoder := k8syaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(doc), len(doc))
err = decoder.Decode(unstructuredObj)
if err != nil {
return nil, err
}
objects = append(objects, unstructuredObj)
}
return objects, nil
}

// A KubeManager that can create objects that are subject to a test.
type fakeKubeManager struct {
fakeClient client.Client
}

func (m *fakeKubeManager) NewClient(kubeconfig string, kubecontext string) (client.Client, error) {
return m.fakeClient, nil
}

func (m *fakeKubeManager) CreateObjects(clientObjects []client.Object) error {
for _, obj := range clientObjects {
err := m.fakeClient.Create(context.Background(), obj)
if err != nil {
return err
}
}
return nil
}

func NewFakeKubeManager() *fakeKubeManager {
c := fakeclient.NewClientBuilder().WithScheme(utils.NewScheme()).Build()
return &fakeKubeManager{
fakeClient: c,
}
}

// Run the command and return the captured output.
func executeCommand(cmd string) (string, error) {
args, err := shellwords.Parse(cmd)
if err != nil {
return "", err
}

buf := new(bytes.Buffer)

rootCmd.SetOut(buf)
rootCmd.SetErr(buf)
rootCmd.SetArgs(args)

_, err = rootCmd.ExecuteC()
result := buf.String()

return result, err
}

// Structure used for each test to load objects into kubernetes, run
// commands and assert on the expected output.
type cmdTestCase struct {
// The command line arguments to test.
args string
// When true, the test expects the command to fail.
wantError bool
// String literal that contains the expected test output.
goldenValue string
// Filename that contains the expected test output.
goldenFile string
// Filename that contains yaml objects to load into Kubernetes
objectFile string
}

func (cmd *cmdTestCase) runTestCmd(t *testing.T) {
km := NewFakeKubeManager()
rootCtx.kubeManager = km

if cmd.objectFile != "" {
clientObjects, err := readYamlObjects(cmd.objectFile)
if err != nil {
t.Fatalf("Error loading yaml: '%v'", err)
}
err = km.CreateObjects(clientObjects)
if err != nil {
t.Fatalf("Error creating test objects: '%v'", err)
}
}

actual, err := executeCommand(cmd.args)
if (err != nil) != cmd.wantError {
t.Fatalf("Expected error='%v', Got: %v", cmd.wantError, err)
}
if err != nil {
actual = err.Error()
}

var expected string
if cmd.goldenValue != "" {
expected = cmd.goldenValue
}
if cmd.goldenFile != "" {
expectedOutput, err := ioutil.ReadFile(cmd.goldenFile)
if err != nil {
t.Fatalf("Error reading golden file: '%s'", err)
}
expected = string(expectedOutput)
}

diff := cmp.Diff(expected, actual)
if diff != "" {
t.Errorf("Mismatch from '%s' (-want +got):\n%s", cmd.goldenFile, diff)
}
}

func TestVersion(t *testing.T) {
cmd := cmdTestCase{
args: "--version",
goldenValue: "flux version 0.0.0-dev.0\n",
}
cmd.runTestCmd(t)
}
1 change: 0 additions & 1 deletion cmd/flux/testdata/trace/deployment.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,3 @@ URL: https://stefanprodan.github.io/podinfo
Revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56
Status: Last reconciled at 2021-07-11 00:25:46 +0000 UTC
Message: Fetched revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56

1 change: 0 additions & 1 deletion cmd/flux/testdata/trace/helmrelease-missing-git-ref.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,3 @@ URL: ssh://git@github.com/example/repo
Revision: main/696f056df216eea4f9401adbee0ff744d4df390f
Status: Last reconciled at 2021-07-20 00:48:16 +0000 UTC
Message: Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f

1 change: 0 additions & 1 deletion cmd/flux/testdata/trace/helmrelease.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,3 @@ Branch: main
Revision: main/696f056df216eea4f9401adbee0ff744d4df390f
Status: Last reconciled at 2021-07-20 00:48:16 +0000 UTC
Message: Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f

1 change: 0 additions & 1 deletion cmd/flux/testdata/trace/no-args.txt

This file was deleted.

142 changes: 3 additions & 139 deletions cmd/flux/trace_test.go
Original file line number Diff line number Diff line change
@@ -1,150 +1,14 @@
package main

import (
"bufio"
"bytes"
"context"
"io"
"io/ioutil"
"os"
"strings"
"testing"

"github.com/fluxcd/flux2/internal/utils"
"github.com/google/go-cmp/cmp"
shellwords "github.com/mattn/go-shellwords"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/controller-runtime/pkg/client"
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func init() {
// Ensure tests print consistent timestamps regardless of timezone
os.Setenv("TZ", "UTC")
}

func readYamlObjects(objectFile string) ([]client.Object, error) {
obj, err := ioutil.ReadFile(objectFile)
if err != nil {
return nil, err
}
objects := []client.Object{}
reader := k8syaml.NewYAMLReader(bufio.NewReader(bytes.NewReader(obj)))
for {
doc, err := reader.Read()
if err != nil {
if err == io.EOF {
break
}
}
unstructuredObj := &unstructured.Unstructured{}
decoder := k8syaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(doc), len(doc))
err = decoder.Decode(unstructuredObj)
if err != nil {
return nil, err
}
objects = append(objects, unstructuredObj)
}
return objects, nil
}

// A KubeManager that can create objects that are subject to a test.
type fakeKubeManager struct {
fakeClient client.Client
}

func (m *fakeKubeManager) NewClient(kubeconfig string, kubecontext string) (client.Client, error) {
return m.fakeClient, nil
}

func (m *fakeKubeManager) CreateObjects(clientObjects []client.Object) error {
for _, obj := range clientObjects {
err := m.fakeClient.Create(context.Background(), obj)
if err != nil {
return err
}
}
return nil
}

func NewFakeKubeManager() *fakeKubeManager {
c := fakeclient.NewClientBuilder().WithScheme(utils.NewScheme()).Build()
return &fakeKubeManager{
fakeClient: c,
}
}

// Run the command and return the captured output.
func executeCommand(cmd string) (string, error) {
args, err := shellwords.Parse(cmd)
if err != nil {
return "", err
}

buf := new(bytes.Buffer)

rootCmd.SetOut(buf)
rootCmd.SetErr(buf)
rootCmd.SetArgs(args)

_, err = rootCmd.ExecuteC()
result := buf.String()

return result, err
}

// Structure used for each test to load objects into kubernetes, run
// commands and assert on the expected output.
type cmdTestCase struct {
// The command line arguments to test.
args string
// When true, the test expects the command to fail.
wantError bool
// Filename that contains the expected test output.
goldenFile string
// Filename that contains yaml objects to load into Kubernetes
objectFile string
}

func (cmd *cmdTestCase) runTestCmd(t *testing.T) {
km := NewFakeKubeManager()
rootCtx.kubeManager = km

if cmd.objectFile != "" {
clientObjects, err := readYamlObjects(cmd.objectFile)
if err != nil {
t.Fatalf("Error loading yaml: '%v'", err)
}
err = km.CreateObjects(clientObjects)
if err != nil {
t.Fatalf("Error creating test objects: '%v'", err)
}
}

actual, err := executeCommand(cmd.args)
if (err != nil) != cmd.wantError {
t.Fatalf("Expected error='%v', Got: %v", cmd.wantError, err)
}
if err != nil {
actual = err.Error()
}
contents, err := ioutil.ReadFile(cmd.goldenFile)
if err != nil {
t.Fatalf("Error reading golden file: '%s'", err)
}
expected := strings.TrimSuffix(string(contents), "\n")
diff := cmp.Diff(expected, actual)
if diff != "" {
t.Errorf("Mismatch from '%s' (-want +got):\n%s", cmd.goldenFile, diff)
}
}

func TestTraceNoArgs(t *testing.T) {
cmd := cmdTestCase{
args: "trace",
wantError: true,
goldenFile: "testdata/trace/no-args.txt",
args: "trace",
wantError: true,
goldenValue: "object name is required",
}
cmd.runTestCmd(t)
}
Expand Down