package agent import ( "testing" flockv1alpha1 "code.fritzlab.net/fritzlab/flock/pkg/api/v1alpha1" "code.fritzlab.net/fritzlab/flock/pkg/embed" ) // boolPtr returns a pointer to b — convenient for the *bool pointer fields // in FamilyDefaults where nil means "unset". func boolPtr(b bool) *bool { return &b } func TestBuiltinFamilyDefaults(t *testing.T) { d := BuiltinFamilyDefaults() if !d.WantV6 || d.WantV4 { t.Fatalf("built-in defaults wrong: v6=%v v4=%v (want true/false)", d.WantV6, d.WantV4) } } func TestFamilyDefaultsFromNodeConfig_NilNodeConfig(t *testing.T) { d := FamilyDefaultsFromNodeConfig(nil) if d != BuiltinFamilyDefaults() { t.Fatalf("nil NodeConfig should yield built-in defaults; got %+v", d) } } func TestFamilyDefaultsFromNodeConfig_NilDefaults(t *testing.T) { nc := &flockv1alpha1.NodeConfig{} d := FamilyDefaultsFromNodeConfig(nc) if d != BuiltinFamilyDefaults() { t.Fatalf("missing Defaults should yield built-in; got %+v", d) } } func TestFamilyDefaultsFromNodeConfig_PartialOverride(t *testing.T) { nc := &flockv1alpha1.NodeConfig{ Spec: flockv1alpha1.NodeConfigSpec{ Defaults: &flockv1alpha1.FamilyDefaults{ IPv4: boolPtr(true), }, }, } d := FamilyDefaultsFromNodeConfig(nc) // IPv6 was unset → keeps built-in true; IPv4 was set → flipped on. if !d.WantV6 || !d.WantV4 { t.Fatalf("partial override wrong: %+v (want v6=true, v4=true)", d) } } func TestFamilyDefaultsFromNodeConfig_FullOverride(t *testing.T) { nc := &flockv1alpha1.NodeConfig{ Spec: flockv1alpha1.NodeConfigSpec{ Defaults: &flockv1alpha1.FamilyDefaults{ IPv6: boolPtr(false), IPv4: boolPtr(true), }, }, } d := FamilyDefaultsFromNodeConfig(nc) if d.WantV6 || !d.WantV4 { t.Fatalf("full override wrong: %+v (want v6=false, v4=true)", d) } } func TestParseAnnotations_BuiltinDefaults(t *testing.T) { a, err := ParseAnnotations(nil, BuiltinFamilyDefaults()) if err != nil { t.Fatal(err) } if !a.WantV6 || a.WantV4 { t.Fatalf("defaults wrong: v6=%v v4=%v", a.WantV6, a.WantV4) } } func TestParseAnnotations_NodeDefaultsApplied(t *testing.T) { // Node config says "IPv4 is on by default for this node". d := FamilyDefaults{WantV6: true, WantV4: true} a, err := ParseAnnotations(nil, d) if err != nil { t.Fatal(err) } if !a.WantV6 || !a.WantV4 { t.Fatalf("node defaults not applied: %+v", a) } } func TestParseAnnotations_AnnotationOverridesNodeDefault(t *testing.T) { // Node says dual-stack by default; pod opts out of v4 explicitly. d := FamilyDefaults{WantV6: true, WantV4: true} a, err := ParseAnnotations(map[string]string{ annotationPrefix + "ipv4": "false", }, d) if err != nil { t.Fatal(err) } if !a.WantV6 || a.WantV4 { t.Fatalf("annotation override failed: %+v", a) } } func TestParseAnnotations_DualStackViaAnnotation(t *testing.T) { a, err := ParseAnnotations(map[string]string{ annotationPrefix + "ipv4": "true", }, BuiltinFamilyDefaults()) if err != nil { t.Fatal(err) } if !(a.WantV6 && a.WantV4) { t.Fatalf("expected dual stack, got v6=%v v4=%v", a.WantV6, a.WantV4) } } func TestParseAnnotations_NoFamily(t *testing.T) { if _, err := ParseAnnotations(map[string]string{ annotationPrefix + "ipv6": "false", }, BuiltinFamilyDefaults()); err == nil { t.Fatalf("expected error: ipv6=false ipv4=false") } } func TestParseAnnotations_NoFamily_NodeDefaultsAlsoOff(t *testing.T) { // Pathological NodeConfig that disables both families. Even with no pod // annotation we must reject — otherwise a pod gets an empty allocation. d := FamilyDefaults{WantV6: false, WantV4: false} if _, err := ParseAnnotations(nil, d); err == nil { t.Fatalf("expected error when both defaults are false") } } func TestParseAnnotations_BoolStrictness(t *testing.T) { // Common misuses that should be rejected so typos don't silently flip // behaviour to the implicit-false default. bad := []string{"1", "0", "yes", "no", "TrueFalse", " "} for _, v := range bad { _, err := ParseAnnotations(map[string]string{ annotationPrefix + "ipv4": v, }, BuiltinFamilyDefaults()) if err == nil { t.Errorf("expected error for ipv4=%q", v) } } } func TestParseAnnotations_BoolCaseInsensitive(t *testing.T) { for _, v := range []string{"TRUE", "True", " true ", "FALSE", "False"} { _, err := ParseAnnotations(map[string]string{ annotationPrefix + "ipv4": v, }, BuiltinFamilyDefaults()) if err != nil { t.Errorf("expected ipv4=%q to parse cleanly: %v", v, err) } } } func TestParseAnnotations_IPAlgo(t *testing.T) { a, err := ParseAnnotations(map[string]string{ annotationPrefix + "ip-algo": "namespace,pod,image", }, BuiltinFamilyDefaults()) if err != nil { t.Fatal(err) } want := []embed.Field{embed.FieldNamespace, embed.FieldPod, embed.FieldImage} if len(a.IPAlgo) != len(want) { t.Fatalf("ip-algo len=%d, want %d", len(a.IPAlgo), len(want)) } for i := range want { if a.IPAlgo[i] != want[i] { t.Fatalf("ip-algo[%d]=%s, want %s", i, a.IPAlgo[i], want[i]) } } } func TestParseAnnotations_IPAlgo_Unknown(t *testing.T) { if _, err := ParseAnnotations(map[string]string{ annotationPrefix + "ip-algo": "namespace,foo", }, BuiltinFamilyDefaults()); err == nil { t.Fatalf("expected unknown-field error") } } func TestParseAnnotations_CIDR(t *testing.T) { a, err := ParseAnnotations(map[string]string{ annotationPrefix + "cidr6": "2602:817:3000:f001::/64, 2602:817:3000:f002::/64", }, BuiltinFamilyDefaults()) if err != nil { t.Fatal(err) } if len(a.CIDR6) != 2 { t.Fatalf("cidr6 len=%d", len(a.CIDR6)) } } func TestParseAnnotations_CIDR_FamilyMismatch(t *testing.T) { // v4 prefix in a cidr6 annotation must not silently slip through. if _, err := ParseAnnotations(map[string]string{ annotationPrefix + "cidr6": "10.0.0.0/8", }, BuiltinFamilyDefaults()); err == nil { t.Fatalf("expected family mismatch error") } if _, err := ParseAnnotations(map[string]string{ annotationPrefix + "cidr4": "2602:817::/32", }, BuiltinFamilyDefaults()); err == nil { t.Fatalf("expected family mismatch error") } } func TestParseAnnotations_Anycast_Mixed(t *testing.T) { // Anycast accepts both families together — typical for a service that // advertises one v6 and one v4 anycast IP. a, err := ParseAnnotations(map[string]string{ annotationPrefix + "anycast": "2602:817:3000:ac::1, 172.25.255.1", }, BuiltinFamilyDefaults()) if err != nil { t.Fatal(err) } if len(a.Anycast) != 2 { t.Fatalf("anycast len=%d", len(a.Anycast)) } } func TestParseCNIArgs(t *testing.T) { args := ParseCNIArgs("IgnoreUnknown=1;K8S_POD_NAMESPACE=mail;K8S_POD_NAME=stalwart-0;K8S_POD_INFRA_CONTAINER_ID=abc123") if args.PodNamespace != "mail" || args.PodName != "stalwart-0" || args.InfraID != "abc123" { t.Fatalf("ParseCNIArgs got %+v", args) } } func TestParseCNIArgs_EmptyAndMalformed(t *testing.T) { // Permissive: malformed entries are skipped, never crash. a := ParseCNIArgs("") if a.PodName != "" { t.Fatalf("empty input should yield empty CNIArgs, got %+v", a) } a = ParseCNIArgs(";;K8S_POD_NAMESPACE=ns;noequalshere;=novalue;K8S_POD_NAME=p") if a.PodNamespace != "ns" || a.PodName != "p" { t.Fatalf("permissive parse failed: %+v", a) } }