package vpn import ( "os" "path/filepath" "testing" ) func TestVXLANManagerBasics(t *testing.T) { tmpDir := t.TempDir() storePath := filepath.Join(tmpDir, "vxlan.json") mgr := NewVXLANManager(storePath) if mgr == nil { t.Fatal("expected non-nil VXLANManager") } // 1. Validation checks err := mgr.Save(&VXLANInstance{ VNI: 0, }) if err == nil { t.Fatal("expected error for VNI <= 0") } err = mgr.Save(&VXLANInstance{ VNI: 16777216, }) if err == nil { t.Fatal("expected error for VNI > 16777215") } // 2. Save a disabled VXLAN instance (does not invoke kernel netlink) inst1 := &VXLANInstance{ ID: "vxlan-test-1", Name: "vxlan-test1", VNI: 100, Disabled: true, Comment: "Test VXLAN 1", } if err := mgr.Save(inst1); err != nil { t.Fatalf("failed to save inst1: %v", err) } // Validate auto-defaults if inst1.Port != 4789 { t.Errorf("expected default port 4789, got %d", inst1.Port) } if inst1.MTU != 1500 { t.Errorf("expected default MTU 1500, got %d", inst1.MTU) } if inst1.MACAddress == "" { t.Error("expected auto-generated MAC address") } // 3. Get and List got, found := mgr.Get("vxlan-test-1") if !found || got == nil { t.Fatal("expected to find vxlan-test-1 by ID") } if got.Name != "vxlan-test1" { t.Errorf("expected name vxlan-test1, got %s", got.Name) } gotByName, found := mgr.Get("vxlan-test1") if !found || gotByName == nil { t.Fatal("expected to find vxlan-test1 by Name") } list := mgr.List() if len(list) != 1 { t.Fatalf("expected 1 tunnel, got %d", len(list)) } // 4. Duplicate checks dupVNI := &VXLANInstance{ ID: "vxlan-test-dup", Name: "vxlan-test-other", VNI: 100, Port: 4789, Disabled: true, } if err := mgr.Save(dupVNI); err == nil { t.Fatal("expected error on duplicate VNI+Port") } dupName := &VXLANInstance{ ID: "vxlan-test-dup-name", Name: "vxlan-test1", VNI: 200, Disabled: true, } if err := mgr.Save(dupName); err == nil { t.Fatal("expected error on duplicate Name") } // 5. VTEP management vtep1 := &VXLANVtep{ RemoteIP: "192.168.1.10", Comment: "Remote Office VTEP", } if err := mgr.AddVtep("vxlan-test-1", vtep1); err != nil { t.Fatalf("failed to add VTEP: %v", err) } // Duplicate VTEP remote IP check if err := mgr.AddVtep("vxlan-test-1", &VXLANVtep{RemoteIP: "192.168.1.10"}); err == nil { t.Fatal("expected error for duplicate remote IP VTEP") } // Invalid remote IP if err := mgr.AddVtep("vxlan-test-1", &VXLANVtep{RemoteIP: "invalid-ip"}); err == nil { t.Fatal("expected error for invalid remote IP") } vteps := mgr.ListVteps("vxlan-test1") if len(vteps) != 1 { t.Fatalf("expected 1 VTEP, got %d", len(vteps)) } if vteps[0].RemoteIP != "192.168.1.10" { t.Errorf("expected 192.168.1.10, got %s", vteps[0].RemoteIP) } // Delete VTEP if err := mgr.DeleteVtep("vxlan-test1", "192.168.1.10"); err != nil { t.Fatalf("failed to delete VTEP: %v", err) } if len(mgr.ListVteps("vxlan-test1")) != 0 { t.Fatal("expected 0 VTEPs after deletion") } // 6. Delete tunnel if err := mgr.Delete("vxlan-test-1"); err != nil { t.Fatalf("failed to delete tunnel: %v", err) } if len(mgr.List()) != 0 { t.Fatal("expected 0 tunnels after deletion") } // Verify persistence file exists and is valid if _, err := os.Stat(storePath); err != nil { t.Fatalf("expected store file to exist: %v", err) } }