Skip to content

Commit

Permalink
refactor: add test for ApplicationHealthCommentReconciler (#716)
Browse files Browse the repository at this point in the history
* refactor: add test for ApplicationHealthCommentReconciler

* refactor: split Its
  • Loading branch information
int128 authored Sep 19, 2022
1 parent 660797b commit a8afea2
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 47 deletions.
100 changes: 100 additions & 0 deletions controllers/applicationhealthcomment_controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package controllers

import (
"time"

argocdv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/gitops-engine/pkg/health"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)

var _ = Describe("Application health comment controller", func() {
const timeout = time.Second * 3
const interval = time.Millisecond * 250
appKey := types.NamespacedName{Namespace: "default", Name: "app2"}

Context("When an application is healthy", func() {
It("Should notify a comment", func() {
By("By creating an application")
Expect(k8sClient.Create(ctx, &argocdv1alpha1.Application{
TypeMeta: metav1.TypeMeta{
APIVersion: "argoproj.io/v1alpha1",
Kind: "Application",
},
ObjectMeta: metav1.ObjectMeta{
Name: appKey.Name,
Namespace: appKey.Namespace,
},
Spec: argocdv1alpha1.ApplicationSpec{
Project: "default",
Source: argocdv1alpha1.ApplicationSource{
RepoURL: "https://github.com/int128/argocd-commenter.git",
Path: "test",
TargetRevision: "main",
},
Destination: argocdv1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "default",
},
},
})).Should(Succeed())

By("By updating the health status to progressing")
Eventually(func(g Gomega) {
var app argocdv1alpha1.Application
g.Expect(k8sClient.Get(ctx, appKey, &app)).Should(Succeed())
app.Status = argocdv1alpha1.ApplicationStatus{
Health: argocdv1alpha1.HealthStatus{
Status: health.HealthStatusProgressing,
},
OperationState: &argocdv1alpha1.OperationState{
StartedAt: metav1.Now(),
Operation: argocdv1alpha1.Operation{
Sync: &argocdv1alpha1.SyncOperation{
Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
},
},
}
g.Expect(k8sClient.Update(ctx, &app)).Should(Succeed())
}, timeout, interval).Should(Succeed())

By("By updating the health status to healthy")
Eventually(func(g Gomega) {
var app argocdv1alpha1.Application
g.Expect(k8sClient.Get(ctx, appKey, &app)).Should(Succeed())
app.Status.Health.Status = health.HealthStatusHealthy
g.Expect(k8sClient.Update(ctx, &app)).Should(Succeed())
}, timeout, interval).Should(Succeed())

Eventually(func() int {
return notificationMock.Comments.CountBy(appKey)
}, timeout, interval).Should(Equal(1))
})

It("Should not notify a comment after healthy", func() {
By("By updating the health status to progressing")
Eventually(func(g Gomega) {
var app argocdv1alpha1.Application
g.Expect(k8sClient.Get(ctx, appKey, &app)).Should(Succeed())
app.Status.Health.Status = health.HealthStatusHealthy
g.Expect(k8sClient.Update(ctx, &app)).Should(Succeed())
}, timeout, interval).Should(Succeed())

By("By updating the health status to healthy")
Eventually(func(g Gomega) {
var app argocdv1alpha1.Application
g.Expect(k8sClient.Get(ctx, appKey, &app)).Should(Succeed())
app.Status.Health.Status = health.HealthStatusHealthy
g.Expect(k8sClient.Update(ctx, &app)).Should(Succeed())
}, timeout, interval).Should(Succeed())

Consistently(func() int {
return notificationMock.Comments.CountBy(appKey)
}, 1*time.Second, interval).Should(Equal(1))
})
})
})
63 changes: 34 additions & 29 deletions controllers/applicationphase_controller_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package controllers

import (
"context"
"time"

argocdv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
Expand All @@ -13,25 +12,21 @@ import (
)

var _ = Describe("Application phase controller", func() {
const (
timeout = time.Second * 10
interval = time.Millisecond * 250
)
const timeout = time.Second * 3
const interval = time.Millisecond * 250
appKey := types.NamespacedName{Namespace: "default", Name: "app1"}

Context("When an application is synced", func() {
It("Should notify events", func() {
ctx := context.TODO()
app1Key := types.NamespacedName{Namespace: "default", Name: "app1"}

It("Should notify a comment and deployment status", func() {
By("By creating an application")
Expect(k8sClient.Create(ctx, &argocdv1alpha1.Application{
TypeMeta: metav1.TypeMeta{
APIVersion: "argoproj.io/v1alpha1",
Kind: "Application",
},
ObjectMeta: metav1.ObjectMeta{
Name: app1Key.Name,
Namespace: app1Key.Namespace,
Name: appKey.Name,
Namespace: appKey.Namespace,
},
Spec: argocdv1alpha1.ApplicationSpec{
Project: "default",
Expand All @@ -47,29 +42,39 @@ var _ = Describe("Application phase controller", func() {
},
})).Should(Succeed())

By("By getting the application")
var app1 argocdv1alpha1.Application
Eventually(func() error { return k8sClient.Get(ctx, app1Key, &app1) }, timeout, interval).Should(Succeed())

By("By updating the operation state to running")
app1.Status = argocdv1alpha1.ApplicationStatus{
OperationState: &argocdv1alpha1.OperationState{
Phase: synccommon.OperationRunning,
StartedAt: metav1.Now(),
Operation: argocdv1alpha1.Operation{
Sync: &argocdv1alpha1.SyncOperation{
Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
Eventually(func(g Gomega) {
var app argocdv1alpha1.Application
g.Expect(k8sClient.Get(ctx, appKey, &app)).Should(Succeed())
app.Status = argocdv1alpha1.ApplicationStatus{
OperationState: &argocdv1alpha1.OperationState{
Phase: synccommon.OperationRunning,
StartedAt: metav1.Now(),
Operation: argocdv1alpha1.Operation{
Sync: &argocdv1alpha1.SyncOperation{
Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
},
},
},
}
Expect(k8sClient.Update(ctx, &app1)).Should(Succeed())
//TODO: assert mock
}
g.Expect(k8sClient.Update(ctx, &app)).Should(Succeed())
}, timeout, interval).Should(Succeed())

Eventually(func() int {
return notificationMock.Comments.CountBy(appKey)
}, timeout, interval).Should(Equal(1))

By("By updating the operation state to succeeded")
app1.Status.OperationState.Phase = synccommon.OperationSucceeded
Expect(k8sClient.Update(ctx, &app1)).Should(Succeed())
//TODO: assert mock
Eventually(func(g Gomega) {
var app argocdv1alpha1.Application
g.Expect(k8sClient.Get(ctx, appKey, &app)).Should(Succeed())
app.Status.OperationState.Phase = synccommon.OperationSucceeded
g.Expect(k8sClient.Update(ctx, &app)).Should(Succeed())
}, timeout, interval).Should(Succeed())

Eventually(func() int {
return notificationMock.Comments.CountBy(appKey)
}, timeout, interval).Should(Equal(2))
})
})
})
52 changes: 52 additions & 0 deletions controllers/mocks_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package controllers

import (
"context"
"sync"

"github.com/int128/argocd-commenter/pkg/notification"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/log"
)

type EventRecorder struct {
m sync.Mutex
counter map[string]int
}

func (r *EventRecorder) CountBy(key types.NamespacedName) int {
r.m.Lock()
defer r.m.Unlock()
return r.counter[key.String()]
}

func (r *EventRecorder) call(event notification.Event) int {
r.m.Lock()
defer r.m.Unlock()

if r.counter == nil {
r.counter = make(map[string]int)
}
key := types.NamespacedName{Namespace: event.Application.Namespace, Name: event.Application.Name}
r.counter[key.String()]++
return r.counter[key.String()]
}

type NotificationMock struct {
Comments EventRecorder
DeploymentStatuses EventRecorder
}

func (n *NotificationMock) Comment(ctx context.Context, event notification.Event) error {
logger := log.FromContext(ctx)
nth := n.Comments.call(event)
logger.Info("called Comment", "nth", nth)
return nil
}

func (n *NotificationMock) Deployment(ctx context.Context, event notification.Event) error {
logger := log.FromContext(ctx)
nth := n.DeploymentStatuses.call(event)
logger.Info("called Deployment", "nth", nth)
return nil
}
26 changes: 8 additions & 18 deletions controllers/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"testing"

argocdv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/int128/argocd-commenter/pkg/notification"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
Expand All @@ -40,10 +39,11 @@ import (
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.

var (
k8sClient client.Client
testEnv *envtest.Environment
ctx context.Context
cancel context.CancelFunc
k8sClient client.Client
testEnv *envtest.Environment
ctx context.Context
cancel context.CancelFunc
notificationMock NotificationMock
)

func TestAPIs(t *testing.T) {
Expand Down Expand Up @@ -93,21 +93,21 @@ var _ = BeforeSuite(func() {
err = (&ApplicationPhaseReconciler{
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Notification: notificationMock{},
Notification: &notificationMock,
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())

err = (&ApplicationHealthCommentReconciler{
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Notification: notificationMock{},
Notification: &notificationMock,
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())

err = (&ApplicationHealthDeploymentReconciler{
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Notification: notificationMock{},
Notification: &notificationMock,
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())

Expand All @@ -124,13 +124,3 @@ var _ = AfterSuite(func() {
err := testEnv.Stop()
Expect(err).NotTo(HaveOccurred())
})

type notificationMock struct{}

func (m notificationMock) Comment(context.Context, notification.Event) error {
return nil
}

func (m notificationMock) Deployment(context.Context, notification.Event) error {
return nil
}

0 comments on commit a8afea2

Please sign in to comment.