mirror of https://github.com/fluxcd/flux2.git
Add test for "flux create source git"
The create source tests are more interesting than the existing tests as they create objects then wit for the flux source reconciler to complete. The tests simulate this with a background goroutine that waits for an object to be created then uses a test specific function to update it. The tests set a timeout so that if there is a failure they timeout somewhat quickly rather than hanging for a longer period of time. Signed-off-by: Allen Porter <allen@thebends.org>pull/1696/head
parent
83c7994266
commit
8cbd4e8172
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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
|
Loading…
Reference in New Issue