Files
Reloader/internal/pkg/webhook/webhook_test.go
T

284 lines
7.2 KiB
Go

package webhook
import (
"context"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/go-logr/logr"
)
func TestNewClient_SetsURL(t *testing.T) {
c := NewClient("http://example.com/webhook", logr.Discard())
if c == nil {
t.Fatal("NewClient should not return nil")
}
if c.url != "http://example.com/webhook" {
t.Errorf("URL = %q, want %q", c.url, "http://example.com/webhook")
}
if c.httpClient == nil {
t.Error("httpClient should not be nil")
}
if c.httpClient.Timeout != 30*time.Second {
t.Errorf("Timeout = %v, want %v", c.httpClient.Timeout, 30*time.Second)
}
}
func TestIsConfigured_NilClient(t *testing.T) {
var c *Client = nil
if c.IsConfigured() {
t.Error("IsConfigured() should return false for nil client")
}
}
func TestIsConfigured_EmptyURL(t *testing.T) {
c := NewClient("", logr.Discard())
if c.IsConfigured() {
t.Error("IsConfigured() should return false for empty URL")
}
}
func TestIsConfigured_ValidURL(t *testing.T) {
c := NewClient("http://example.com/webhook", logr.Discard())
if !c.IsConfigured() {
t.Error("IsConfigured() should return true for valid URL")
}
}
func TestSend_EmptyURL_ReturnsNil(t *testing.T) {
c := NewClient("", logr.Discard())
payload := Payload{
Kind: "ConfigMap",
Namespace: "default",
ResourceName: "my-config",
ResourceType: "configmap",
}
err := c.Send(context.Background(), payload)
if err != nil {
t.Errorf("Send() with empty URL should return nil, got %v", err)
}
}
func TestSend_MarshalPayload(t *testing.T) {
var receivedPayload Payload
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
json.Unmarshal(body, &receivedPayload)
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
c := NewClient(server.URL, logr.Discard())
payload := Payload{
Kind: "ConfigMap",
Namespace: "default",
ResourceName: "my-config",
ResourceType: "configmap",
Hash: "abc123",
Timestamp: time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC),
Workloads: []WorkloadInfo{
{Kind: "Deployment", Name: "my-deploy", Namespace: "default"},
},
}
err := c.Send(context.Background(), payload)
if err != nil {
t.Fatalf("Send() error = %v", err)
}
if receivedPayload.Kind != "ConfigMap" {
t.Errorf("Received Kind = %q, want %q", receivedPayload.Kind, "ConfigMap")
}
if receivedPayload.Namespace != "default" {
t.Errorf("Received Namespace = %q, want %q", receivedPayload.Namespace, "default")
}
if receivedPayload.ResourceName != "my-config" {
t.Errorf("Received ResourceName = %q, want %q", receivedPayload.ResourceName, "my-config")
}
if receivedPayload.Hash != "abc123" {
t.Errorf("Received Hash = %q, want %q", receivedPayload.Hash, "abc123")
}
if len(receivedPayload.Workloads) != 1 {
t.Errorf("Received Workloads count = %d, want 1", len(receivedPayload.Workloads))
}
}
func TestSend_SetsCorrectHeaders(t *testing.T) {
var contentType, userAgent string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
contentType = r.Header.Get("Content-Type")
userAgent = r.Header.Get("User-Agent")
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
c := NewClient(server.URL, logr.Discard())
err := c.Send(context.Background(), Payload{})
if err != nil {
t.Fatalf("Send() error = %v", err)
}
if contentType != "application/json" {
t.Errorf("Content-Type = %q, want %q", contentType, "application/json")
}
if userAgent != "Reloader/2.0" {
t.Errorf("User-Agent = %q, want %q", userAgent, "Reloader/2.0")
}
}
func TestSend_UsesPostMethod(t *testing.T) {
var method string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
method = r.Method
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
c := NewClient(server.URL, logr.Discard())
err := c.Send(context.Background(), Payload{})
if err != nil {
t.Fatalf("Send() error = %v", err)
}
if method != http.MethodPost {
t.Errorf("Method = %q, want %q", method, http.MethodPost)
}
}
func TestSend_Non2xxResponse(t *testing.T) {
tests := []struct {
name string
statusCode int
wantErr bool
}{
{"200 OK", 200, false},
{"201 Created", 201, false},
{"204 No Content", 204, false},
{"299 upper bound", 299, false},
{"300 redirect", 300, true},
{"400 Bad Request", 400, true},
{"404 Not Found", 404, true},
{"500 Internal Error", 500, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(tt.statusCode)
}))
defer server.Close()
c := NewClient(server.URL, logr.Discard())
err := c.Send(context.Background(), Payload{})
if (err != nil) != tt.wantErr {
t.Errorf("Send() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestSend_NetworkError(t *testing.T) {
// Use a URL that won't connect
c := NewClient("http://127.0.0.1:1", logr.Discard())
err := c.Send(context.Background(), Payload{})
if err == nil {
t.Error("Send() should return error for network failure")
}
}
func TestSend_ContextCancellation(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(100 * time.Millisecond)
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
c := NewClient(server.URL, logr.Discard())
ctx, cancel := context.WithCancel(context.Background())
cancel() // Cancel immediately
err := c.Send(ctx, Payload{})
if err == nil {
t.Error("Send() should return error for cancelled context")
}
}
func TestPayload_JSONSerialization(t *testing.T) {
payload := Payload{
Kind: "ConfigMap",
Namespace: "default",
ResourceName: "my-config",
ResourceType: "configmap",
Hash: "abc123",
Timestamp: time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC),
Workloads: []WorkloadInfo{
{Kind: "Deployment", Name: "my-deploy", Namespace: "default"},
{Kind: "StatefulSet", Name: "my-sts", Namespace: "default"},
},
}
data, err := json.Marshal(payload)
if err != nil {
t.Fatalf("Failed to marshal payload: %v", err)
}
var unmarshaled Payload
if err := json.Unmarshal(data, &unmarshaled); err != nil {
t.Fatalf("Failed to unmarshal payload: %v", err)
}
if unmarshaled.Kind != payload.Kind {
t.Errorf("Kind = %q, want %q", unmarshaled.Kind, payload.Kind)
}
if len(unmarshaled.Workloads) != 2 {
t.Errorf("Workloads count = %d, want 2", len(unmarshaled.Workloads))
}
}
func TestWorkloadInfo_JSONSerialization(t *testing.T) {
info := WorkloadInfo{
Kind: "Deployment",
Name: "my-deploy",
Namespace: "production",
}
data, err := json.Marshal(info)
if err != nil {
t.Fatalf("Failed to marshal: %v", err)
}
var unmarshaled WorkloadInfo
if err := json.Unmarshal(data, &unmarshaled); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
if unmarshaled.Kind != "Deployment" {
t.Errorf("Kind = %q, want %q", unmarshaled.Kind, "Deployment")
}
if unmarshaled.Name != "my-deploy" {
t.Errorf("Name = %q, want %q", unmarshaled.Name, "my-deploy")
}
if unmarshaled.Namespace != "production" {
t.Errorf("Namespace = %q, want %q", unmarshaled.Namespace, "production")
}
}