Files
Reloader/internal/pkg/controller/namespace_reconciler_test.go
T

296 lines
7.0 KiB
Go

package controller_test
import (
"context"
"testing"
"github.com/go-logr/logr/testr"
"github.com/stakater/Reloader/internal/pkg/config"
"github.com/stakater/Reloader/internal/pkg/controller"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
)
func TestNamespaceCache_Basic(t *testing.T) {
cache := controller.NewNamespaceCache(true)
// Test Add and Contains
cache.Add("namespace-1")
if !cache.Contains("namespace-1") {
t.Error("Cache should contain namespace-1")
}
if cache.Contains("namespace-2") {
t.Error("Cache should not contain namespace-2")
}
// Test Remove
cache.Remove("namespace-1")
if cache.Contains("namespace-1") {
t.Error("Cache should not contain namespace-1 after removal")
}
}
func TestNamespaceCache_Disabled(t *testing.T) {
cache := controller.NewNamespaceCache(false)
// When disabled, Contains should always return true
if !cache.Contains("any-namespace") {
t.Error("Disabled cache should return true for any namespace")
}
if !cache.Contains("other-namespace") {
t.Error("Disabled cache should return true for any namespace")
}
}
func TestNamespaceCache_List(t *testing.T) {
cache := controller.NewNamespaceCache(true)
cache.Add("ns-1")
cache.Add("ns-2")
cache.Add("ns-3")
list := cache.List()
if len(list) != 3 {
t.Errorf("Expected 3 namespaces, got %d", len(list))
}
// Check all namespaces are in the list
found := make(map[string]bool)
for _, ns := range list {
found[ns] = true
}
for _, expected := range []string{"ns-1", "ns-2", "ns-3"} {
if !found[expected] {
t.Errorf("Expected %s in list", expected)
}
}
}
func TestNamespaceCache_IsEnabled(t *testing.T) {
enabledCache := controller.NewNamespaceCache(true)
disabledCache := controller.NewNamespaceCache(false)
if !enabledCache.IsEnabled() {
t.Error("EnabledCache.IsEnabled() should return true")
}
if disabledCache.IsEnabled() {
t.Error("DisabledCache.IsEnabled() should return false")
}
}
func TestNamespaceReconciler_Add(t *testing.T) {
scheme := runtime.NewScheme()
_ = corev1.AddToScheme(scheme)
ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "test-ns",
Labels: map[string]string{"env": "production"},
},
}
fakeClient := fake.NewClientBuilder().
WithScheme(scheme).
WithObjects(ns).
Build()
cfg := config.NewDefault()
selector, _ := labels.Parse("env=production")
cfg.NamespaceSelectors = []labels.Selector{selector}
cache := controller.NewNamespaceCache(true)
reconciler := &controller.NamespaceReconciler{
Client: fakeClient,
Log: testr.New(t),
Config: cfg,
Cache: cache,
}
req := ctrl.Request{
NamespacedName: types.NamespacedName{Name: "test-ns"},
}
_, err := reconciler.Reconcile(context.Background(), req)
if err != nil {
t.Fatalf("Reconcile failed: %v", err)
}
if !cache.Contains("test-ns") {
t.Error("Cache should contain test-ns after reconcile")
}
}
func TestNamespaceReconciler_Remove_LabelChange(t *testing.T) {
scheme := runtime.NewScheme()
_ = corev1.AddToScheme(scheme)
// Namespace with non-matching labels
ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "test-ns",
Labels: map[string]string{"env": "staging"},
},
}
fakeClient := fake.NewClientBuilder().
WithScheme(scheme).
WithObjects(ns).
Build()
cfg := config.NewDefault()
selector, _ := labels.Parse("env=production")
cfg.NamespaceSelectors = []labels.Selector{selector}
cache := controller.NewNamespaceCache(true)
// Pre-populate cache
cache.Add("test-ns")
reconciler := &controller.NamespaceReconciler{
Client: fakeClient,
Log: testr.New(t),
Config: cfg,
Cache: cache,
}
req := ctrl.Request{
NamespacedName: types.NamespacedName{Name: "test-ns"},
}
_, err := reconciler.Reconcile(context.Background(), req)
if err != nil {
t.Fatalf("Reconcile failed: %v", err)
}
if cache.Contains("test-ns") {
t.Error("Cache should not contain test-ns after reconcile (labels no longer match)")
}
}
func TestNamespaceReconciler_Remove_Delete(t *testing.T) {
scheme := runtime.NewScheme()
_ = corev1.AddToScheme(scheme)
// No namespace in cluster (simulates delete)
fakeClient := fake.NewClientBuilder().
WithScheme(scheme).
Build()
cfg := config.NewDefault()
selector, _ := labels.Parse("env=production")
cfg.NamespaceSelectors = []labels.Selector{selector}
cache := controller.NewNamespaceCache(true)
// Pre-populate cache
cache.Add("deleted-ns")
reconciler := &controller.NamespaceReconciler{
Client: fakeClient,
Log: testr.New(t),
Config: cfg,
Cache: cache,
}
req := ctrl.Request{
NamespacedName: types.NamespacedName{Name: "deleted-ns"},
}
_, err := reconciler.Reconcile(context.Background(), req)
if err != nil {
t.Fatalf("Reconcile failed: %v", err)
}
if cache.Contains("deleted-ns") {
t.Error("Cache should not contain deleted-ns after reconcile")
}
}
func TestNamespaceReconciler_MultipleSelectors(t *testing.T) {
scheme := runtime.NewScheme()
_ = corev1.AddToScheme(scheme)
ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "test-ns",
Labels: map[string]string{"team": "platform"},
},
}
fakeClient := fake.NewClientBuilder().
WithScheme(scheme).
WithObjects(ns).
Build()
cfg := config.NewDefault()
selector1, _ := labels.Parse("env=production")
selector2, _ := labels.Parse("team=platform")
cfg.NamespaceSelectors = []labels.Selector{selector1, selector2}
cache := controller.NewNamespaceCache(true)
reconciler := &controller.NamespaceReconciler{
Client: fakeClient,
Log: testr.New(t),
Config: cfg,
Cache: cache,
}
req := ctrl.Request{
NamespacedName: types.NamespacedName{Name: "test-ns"},
}
_, err := reconciler.Reconcile(context.Background(), req)
if err != nil {
t.Fatalf("Reconcile failed: %v", err)
}
// Should be added because it matches second selector (team=platform)
if !cache.Contains("test-ns") {
t.Error("Cache should contain test-ns (matches second selector)")
}
}
func TestNamespaceReconciler_NoLabels(t *testing.T) {
scheme := runtime.NewScheme()
_ = corev1.AddToScheme(scheme)
// Namespace with no labels
ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "test-ns",
},
}
fakeClient := fake.NewClientBuilder().
WithScheme(scheme).
WithObjects(ns).
Build()
cfg := config.NewDefault()
selector, _ := labels.Parse("env=production")
cfg.NamespaceSelectors = []labels.Selector{selector}
cache := controller.NewNamespaceCache(true)
reconciler := &controller.NamespaceReconciler{
Client: fakeClient,
Log: testr.New(t),
Config: cfg,
Cache: cache,
}
req := ctrl.Request{
NamespacedName: types.NamespacedName{Name: "test-ns"},
}
_, err := reconciler.Reconcile(context.Background(), req)
if err != nil {
t.Fatalf("Reconcile failed: %v", err)
}
if cache.Contains("test-ns") {
t.Error("Cache should not contain test-ns (no labels)")
}
}