Files
kmsvc-manage/internal/operator/temporal_worker_controller_test.go

194 lines
6.2 KiB
Go

package operator
import (
"context"
"testing"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
kmsvcv1 "forgejo.riotpiao.com/rock/kmsvc-manage/apis/kmsvc/v1"
)
func newTemporalWorkerTestScheme(t *testing.T) *runtime.Scheme {
t.Helper()
scheme := runtime.NewScheme()
if err := clientgoscheme.AddToScheme(scheme); err != nil {
t.Fatalf("add client-go scheme: %v", err)
}
if err := kmsvcv1.AddToScheme(scheme); err != nil {
t.Fatalf("add kmsvc scheme: %v", err)
}
if err := appsv1.AddToScheme(scheme); err != nil {
t.Fatalf("add apps scheme: %v", err)
}
return scheme
}
func newTemporalWorkerTestReconciler(t *testing.T, objs ...client.Object) (*TemporalWorkerReconciler, client.Client) {
t.Helper()
scheme := newTemporalWorkerTestScheme(t)
cl := fake.NewClientBuilder().
WithScheme(scheme).
WithStatusSubresource(&kmsvcv1.TemporalWorker{}).
WithObjects(objs...).
Build()
return &TemporalWorkerReconciler{Client: cl}, cl
}
func baseTemporalWorker(name, namespace string) *kmsvcv1.TemporalWorker {
replicas := int32(2)
return &kmsvcv1.TemporalWorker{
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
Spec: kmsvcv1.TemporalWorkerSpec{
Namespace: "default",
Image: "story-crater-backend:v1.0.0",
Replicas: &replicas,
},
}
}
func TestTemporalWorkerReconcileCreatesDeployment(t *testing.T) {
worker := baseTemporalWorker("worker-default", "temporal")
r, cl := newTemporalWorkerTestReconciler(t, worker)
ctx := context.Background()
if err := r.Reconcile(ctx, "temporal", "worker-default"); err != nil {
t.Fatalf("Reconcile: %v", err)
}
var deploy appsv1.Deployment
if err := cl.Get(ctx, client.ObjectKey{Name: "worker-default", Namespace: "temporal"}, &deploy); err != nil {
t.Fatalf("expected Deployment to be created: %v", err)
}
if *deploy.Spec.Replicas != 2 {
t.Errorf("replicas = %d, want 2", *deploy.Spec.Replicas)
}
if deploy.Spec.Template.Spec.Containers[0].Image != "story-crater-backend:v1.0.0" {
t.Errorf("image = %q, want story-crater-backend:v1.0.0", deploy.Spec.Template.Spec.Containers[0].Image)
}
}
func TestTemporalWorkerReconcileInjectsEnvVars(t *testing.T) {
worker := baseTemporalWorker("worker-default", "temporal")
r, cl := newTemporalWorkerTestReconciler(t, worker)
ctx := context.Background()
if err := r.Reconcile(ctx, "temporal", "worker-default"); err != nil {
t.Fatalf("Reconcile: %v", err)
}
var deploy appsv1.Deployment
if err := cl.Get(ctx, client.ObjectKey{Name: "worker-default", Namespace: "temporal"}, &deploy); err != nil {
t.Fatalf("get Deployment: %v", err)
}
envVars := deploy.Spec.Template.Spec.Containers[0].Env
envMap := make(map[string]string)
for _, ev := range envVars {
envMap[ev.Name] = ev.Value
}
if envMap["TEMPORAL_FRONTEND_ADDRESS"] != "temporal-frontend.temporal.svc.cluster.local:7233" {
t.Errorf("TEMPORAL_FRONTEND_ADDRESS = %q, want temporal-frontend.temporal.svc.cluster.local:7233", envMap["TEMPORAL_FRONTEND_ADDRESS"])
}
if envMap["TEMPORAL_NAMESPACE"] != "default" {
t.Errorf("TEMPORAL_NAMESPACE = %q, want default", envMap["TEMPORAL_NAMESPACE"])
}
if envMap["TEMPORAL_TASK_QUEUE"] != "worker-default" {
t.Errorf("TEMPORAL_TASK_QUEUE = %q, want worker-default", envMap["TEMPORAL_TASK_QUEUE"])
}
}
func TestTemporalWorkerReconcileUpdateDeployment(t *testing.T) {
replicas := int32(2)
worker := baseTemporalWorker("worker-default", "temporal")
deploy := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{Name: "worker-default", Namespace: "temporal"},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app.kubernetes.io/name": "temporal-worker",
"app.kubernetes.io/instance": "worker-default",
"app.kubernetes.io/managed-by": "kmsvc-temporal-operator",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app.kubernetes.io/name": "temporal-worker",
"app.kubernetes.io/instance": "worker-default",
"app.kubernetes.io/managed-by": "kmsvc-temporal-operator",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "worker",
Image: "story-crater-backend:v0.9.0",
},
},
},
},
},
}
r, cl := newTemporalWorkerTestReconciler(t, worker, deploy)
ctx := context.Background()
if err := r.Reconcile(ctx, "temporal", "worker-default"); err != nil {
t.Fatalf("Reconcile: %v", err)
}
var updated appsv1.Deployment
if err := cl.Get(ctx, client.ObjectKey{Name: "worker-default", Namespace: "temporal"}, &updated); err != nil {
t.Fatalf("get Deployment: %v", err)
}
if updated.Spec.Template.Spec.Containers[0].Image != "story-crater-backend:v1.0.0" {
t.Errorf("image updated to %q, want story-crater-backend:v1.0.0", updated.Spec.Template.Spec.Containers[0].Image)
}
}
func TestTemporalWorkerReconcileUpdatesStatus(t *testing.T) {
worker := baseTemporalWorker("worker-default", "temporal")
r, cl := newTemporalWorkerTestReconciler(t, worker)
ctx := context.Background()
if err := r.Reconcile(ctx, "temporal", "worker-default"); err != nil {
t.Fatalf("Reconcile: %v", err)
}
var updated kmsvcv1.TemporalWorker
if err := cl.Get(ctx, client.ObjectKey{Name: "worker-default", Namespace: "temporal"}, &updated); err != nil {
t.Fatalf("get TemporalWorker: %v", err)
}
if updated.Status.Phase != kmsvcv1.TemporalWorkerPhasePending {
t.Errorf("phase = %v, want Pending", updated.Status.Phase)
}
if updated.Status.Replicas != 2 {
t.Errorf("status.replicas = %d, want 2", updated.Status.Replicas)
}
}
func TestTemporalWorkerReconcileDeleteHandlesMarkedForDeletion(t *testing.T) {
worker := baseTemporalWorker("worker-default", "temporal")
now := metav1.Now()
worker.ObjectMeta.DeletionTimestamp = &now
worker.ObjectMeta.Finalizers = []string{temporalWorkerFinalizerName}
r, _ := newTemporalWorkerTestReconciler(t, worker)
ctx := context.Background()
if err := r.Reconcile(ctx, "temporal", "worker-default"); err != nil {
t.Fatalf("Reconcile delete should not error: %v", err)
}
}