1
0
Fork 0
mirror of https://github.com/imjasonh/client-go2 synced 2026-07-08 17:16:47 +00:00
client-go2/generic/e2e_test.go
Jason Hall e5071fc0cf
Add support for label and field selectors in informers
- Add InformOptions struct with ListOptions and ResyncPeriod fields
- Update all client methods to accept options parameters
- Add comprehensive examples in main.go demonstrating:
  - Label selector filtering (test=example)
  - Field selector filtering (status.phase=Running)
  - Custom resync periods
- Add tests for label and field selectors with mock query parameter support
- Fix nil pointer handling in all client methods
- Update mock transport to handle query parameters correctly

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-27 17:13:22 -04:00

291 lines
7.2 KiB
Go

//go:build e2e
// +build e2e
package generic
import (
"context"
"testing"
"time"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
)
// TestInferGVRE2E tests GVR inference against a real Kubernetes cluster
func TestInferGVRE2E(t *testing.T) {
config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
clientcmd.NewDefaultClientConfigLoadingRules(),
&clientcmd.ConfigOverrides{},
).ClientConfig()
if err != nil {
t.Fatalf("failed to load kubeconfig: %v", err)
}
tests := []struct {
name string
inferFunc func() (schema.GroupVersionResource, error)
expectedGVR schema.GroupVersionResource
}{
{
name: "Pod",
inferFunc: func() (schema.GroupVersionResource, error) {
return inferGVR[*corev1.Pod](config)
},
expectedGVR: schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "pods",
},
},
{
name: "ConfigMap",
inferFunc: func() (schema.GroupVersionResource, error) {
return inferGVR[*corev1.ConfigMap](config)
},
expectedGVR: schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "configmaps",
},
},
{
name: "Service",
inferFunc: func() (schema.GroupVersionResource, error) {
return inferGVR[*corev1.Service](config)
},
expectedGVR: schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "services",
},
},
{
name: "Secret",
inferFunc: func() (schema.GroupVersionResource, error) {
return inferGVR[*corev1.Secret](config)
},
expectedGVR: schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "secrets",
},
},
{
name: "Namespace",
inferFunc: func() (schema.GroupVersionResource, error) {
return inferGVR[*corev1.Namespace](config)
},
expectedGVR: schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "namespaces",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gvr, err := tt.inferFunc()
if err != nil {
t.Fatalf("failed to infer GVR: %v", err)
}
if gvr != tt.expectedGVR {
t.Errorf("expected GVR %v, got %v", tt.expectedGVR, gvr)
}
})
}
}
// TestNewClientE2E tests client creation with inferred GVR against a real cluster
func TestNewClientE2E(t *testing.T) {
config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
clientcmd.NewDefaultClientConfigLoadingRules(),
&clientcmd.ConfigOverrides{},
).ClientConfig()
if err != nil {
t.Fatalf("failed to load kubeconfig: %v", err)
}
ctx := context.Background()
t.Run("Pod client operations", func(t *testing.T) {
client, err := NewClient[*corev1.Pod](config)
if err != nil {
t.Fatalf("failed to create pod client: %v", err)
}
// Try to list pods in default namespace
pods, err := client.List(ctx, "default", nil)
if err != nil {
t.Fatalf("failed to list pods: %v", err)
}
t.Logf("Successfully listed %d pods in default namespace", len(pods))
})
t.Run("ConfigMap client operations", func(t *testing.T) {
client, err := NewClient[*corev1.ConfigMap](config)
if err != nil {
t.Fatalf("failed to create configmap client: %v", err)
}
// Try to list configmaps in default namespace
cms, err := client.List(ctx, "default", nil)
if err != nil {
t.Fatalf("failed to list configmaps: %v", err)
}
t.Logf("Successfully listed %d configmaps in default namespace", len(cms))
})
t.Run("Service client operations", func(t *testing.T) {
client, err := NewClient[*corev1.Service](config)
if err != nil {
t.Fatalf("failed to create service client: %v", err)
}
// Try to list services in default namespace
svcs, err := client.List(ctx, "default", nil)
if err != nil {
t.Fatalf("failed to list services: %v", err)
}
t.Logf("Successfully listed %d services in default namespace", len(svcs))
})
}
// TestInferGVRErrorCases tests error cases for GVR inference
func TestInferGVRErrorCases(t *testing.T) {
config := &rest.Config{
Host: "http://localhost:8080",
}
t.Run("Unregistered type", func(t *testing.T) {
// This should fail because no scheme is registered for this type
type UnregisteredType struct {
*corev1.Pod
}
_, err := inferGVR[*UnregisteredType](config)
if err == nil {
t.Error("expected error for unregistered type, got nil")
}
})
}
// TestInformE2E tests informer functionality against a real cluster
func TestInformE2E(t *testing.T) {
config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
clientcmd.NewDefaultClientConfigLoadingRules(),
&clientcmd.ConfigOverrides{},
).ClientConfig()
if err != nil {
t.Fatalf("failed to load kubeconfig: %v", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// Create a ConfigMap client with inferred GVR
client, err := NewClient[*corev1.ConfigMap](config)
if err != nil {
t.Fatalf("failed to create configmap client: %v", err)
}
// Track events
events := make(chan string, 100)
handler := InformerHandler[*corev1.ConfigMap]{
OnAdd: func(key string, obj *corev1.ConfigMap) {
namespace, name, _ := cache.SplitMetaNamespaceKey(key)
if namespace == "default" {
events <- "add:" + name
}
},
OnUpdate: func(key string, oldObj, newObj *corev1.ConfigMap) {
namespace, name, _ := cache.SplitMetaNamespaceKey(key)
if namespace == "default" {
events <- "update:" + name
}
},
OnDelete: func(key string, obj *corev1.ConfigMap) {
namespace, name, _ := cache.SplitMetaNamespaceKey(key)
if namespace == "default" {
events <- "delete:" + name
}
},
OnError: func(obj any, err error) {
t.Logf("Informer error: %v for object %v", err, obj)
},
}
client.Inform(ctx, handler, nil)
// Create a test ConfigMap
testCM := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-inform-" + time.Now().Format("20060102-150405"),
Namespace: "default",
},
Data: map[string]string{
"test": "data",
},
}
created, err := client.Create(ctx, "default", testCM, nil)
if err != nil {
t.Fatalf("failed to create test configmap: %v", err)
}
testCM = created
defer func() {
// Clean up
if err := client.Delete(ctx, "default", testCM.Name, nil); err != nil {
t.Logf("failed to delete test configmap: %v", err)
}
}()
// Wait for add event for our specific ConfigMap
deadline := time.After(10 * time.Second)
foundAdd := false
for !foundAdd {
select {
case event := <-events:
t.Logf("Received event: %s", event)
if event == "add:"+testCM.Name {
foundAdd = true
}
case <-deadline:
t.Fatal("timeout waiting for add event")
}
}
// Update the ConfigMap
testCM.Data["test"] = "updated"
updated, err := client.Update(ctx, "default", testCM, nil)
if err != nil {
t.Fatalf("failed to update test configmap: %v", err)
}
testCM = updated
// Wait for update event
deadline = time.After(10 * time.Second)
foundUpdate := false
for !foundUpdate {
select {
case event := <-events:
t.Logf("Received event: %s", event)
if event == "update:"+testCM.Name {
foundUpdate = true
}
case <-deadline:
t.Fatal("timeout waiting for update event")
}
}
}