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
131 changes: 131 additions & 0 deletions cmd/flux/create_source_git_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// +build unit

package main

import (
"context"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"testing"
"time"
)

var pollInterval = 50 * time.Millisecond
var testTimeout = 10 * time.Second

// Update the GitRepository once created to exercise test specific behavior
type reconcileFunc func(repo *sourcev1.GitRepository)

// reconciler waits for an object to be created, then invokes a test supplied
// function to mutate that object, simulating a controller.
// Test should invoke run() to run the background reconciler task which
// polls to wait for the object to exist before applying the update function.
// Any errors from the reconciler are asserted on test completion.
type reconciler struct {
client client.Client
name types.NamespacedName
reconcile reconcileFunc
}

// Start the background task that waits for the object to exist then applies
// the update function.
func (r *reconciler) run(t *testing.T) {
result := make(chan error)
go func() {
defer close(result)
err := wait.PollImmediate(
pollInterval,
testTimeout,
r.conditionFunc)
result <- err
}()
t.Cleanup(func() {
if err := <-result; err != nil {
t.Errorf("Failure from test reconciler: '%v':", err.Error())
}
})
}

// A ConditionFunction that waits for the named GitRepository to be created,
// then sets the ready condition to true.
func (r *reconciler) conditionFunc() (bool, error) {
var repo sourcev1.GitRepository
if err := r.client.Get(context.Background(), r.name, &repo); err != nil {
if errors.IsNotFound(err) {
return false, nil // Keep polling until object is created
}
return true, err
}
r.reconcile(&repo)
err := r.client.Status().Update(context.Background(), &repo)
return true, err
}

func TestCreateSourceGit(t *testing.T) {
// Default command used for multiple tests
var command = "create source git podinfo --url=https://github.com/stefanprodan/podinfo --branch=master --timeout=" + testTimeout.String()

cases := []struct {
name string
args string
assert assertFunc
reconcile reconcileFunc
}{
{
"NoArgs",
"create source git",
assertError("GitRepository source name is required"),
nil,
}, {
"Succeeded",
command,
assertGoldenFile("testdata/create_source_git/success.golden"),
func(repo *sourcev1.GitRepository) {
meta.SetResourceCondition(repo, meta.ReadyCondition, metav1.ConditionTrue, sourcev1.GitOperationSucceedReason, "succeeded message")
repo.Status.Artifact = &sourcev1.Artifact{
Path: "some-path",
Revision: "v1",
}
},
}, {
"Failed",
command,
assertError("failed message"),
func(repo *sourcev1.GitRepository) {
meta.SetResourceCondition(repo, meta.ReadyCondition, metav1.ConditionFalse, sourcev1.URLInvalidReason, "failed message")
},
}, {
"NoArtifact",
command,
assertError("GitRepository source reconciliation completed but no artifact was found"),
func(repo *sourcev1.GitRepository) {
// Updated with no artifact
meta.SetResourceCondition(repo, meta.ReadyCondition, metav1.ConditionTrue, sourcev1.GitOperationSucceedReason, "succeeded message")
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
ns := allocateNamespace("podinfo")
setupTestNamespace(ns, t)
if tc.reconcile != nil {
r := reconciler{
client: testEnv.client,
name: types.NamespacedName{Namespace: ns, Name: "podinfo"},
reconcile: tc.reconcile,
}
r.run(t)
}
cmd := cmdTestCase{
args: tc.args + " -n=" + ns,
assert: tc.assert,
}
cmd.runTestCmd(t)
})
}
}
9 changes: 7 additions & 2 deletions cmd/flux/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"text/template"
"time"

"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"
Expand Down Expand Up @@ -132,7 +133,9 @@ func NewTestEnvKubeManager(testClusterMode TestClusterMode) (*testEnvKubeManager

tmpFilename := filepath.Join("/tmp", "kubeconfig-"+time.Nanosecond.String())
os.WriteFile(tmpFilename, kubeConfig, 0644)
k8sClient, err := client.NewWithWatch(cfg, client.Options{})
k8sClient, err := client.NewWithWatch(cfg, client.Options{
Scheme: utils.NewScheme(),
})
if err != nil {
return nil, err
}
Expand All @@ -158,7 +161,9 @@ func NewTestEnvKubeManager(testClusterMode TestClusterMode) (*testEnvKubeManager
if err != nil {
return nil, err
}
k8sClient, err := client.NewWithWatch(cfg, client.Options{})
k8sClient, err := client.NewWithWatch(cfg, client.Options{
Scheme: utils.NewScheme(),
})
if err != nil {
return nil, err
}
Expand Down
14 changes: 14 additions & 0 deletions cmd/flux/main_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
package main

import (
"context"
"fmt"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"os"
"testing"
)
Expand Down Expand Up @@ -32,3 +35,14 @@ func TestMain(m *testing.M) {

os.Exit(code)
}

func setupTestNamespace(namespace string, t *testing.T) {
ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
err := testEnv.client.Create(context.Background(), ns)
if err != nil {
t.Fatalf("Failed to create namespace: %v", err)
}
t.Cleanup(func() {
_ = testEnv.client.Delete(context.Background(), ns)
})
}
6 changes: 6 additions & 0 deletions cmd/flux/testdata/create_source_git/success.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
✚ generating GitRepository source
► applying GitRepository source
✔ GitRepository source created
◎ waiting for GitRepository source reconciliation
✔ GitRepository source reconciliation completed
✔ fetched revision: v1