package bridge import ( "os" "os/exec" "path/filepath" "strings" "testing" ) func TestMembershipChangesRepresentRemovalAndAddition(t *testing.T) { before := members(&BridgeVLAN{Bridge: "br0", VlanIDs: "10", Tagged: []string{"p1"}}) after := members(&BridgeVLAN{Bridge: "br0", VlanIDs: "20", Untagged: []string{"p2"}}) if len(before) != 1 || len(after) != 1 || before[vlanMember{"p2", 20, true, false}] || !after[vlanMember{"p2", 20, true, false}] { t.Fatal(before, after) } } func TestVLANEditInNamespace(t *testing.T) { if os.Getenv("DEBTIK_BRIDGE_INTEGRATION") != "1" { t.Skip("isolated namespace only") } run := func(binary string, args ...string) string { t.Helper() out, err := exec.Command(binary, args...).CombinedOutput() if err != nil { t.Fatalf("%s %v: %s: %v", binary, args, out, err) } return string(out) } run("ip", "link", "add", "br0", "type", "bridge", "vlan_filtering", "1") for _, name := range []string{"p1", "p2"} { run("ip", "link", "add", name, "type", "dummy") run("ip", "link", "set", name, "master", "br0") } m := &BridgeManager{storePath: filepath.Join(t.TempDir(), "bridge.json"), bridges: map[string]*BridgeInstance{}, ports: map[string]*BridgePort{}, vlans: map[string]*BridgeVLAN{}} if err := m.SavePort(BridgePort{ID: "p1", Bridge: "br0", Interface: "p1", PVID: 1, Learn: true}); err != nil { t.Fatal(err) } v := BridgeVLAN{ID: "v1", Bridge: "br0", VlanIDs: "10", Tagged: []string{"p1"}} if err := m.SaveVLAN(v); err != nil { t.Fatal(err) } v.VlanIDs = "20" v.Tagged = nil v.Untagged = []string{"p2"} if err := m.SaveVLAN(v); err != nil { t.Fatal(err) } p1 := run("bridge", "vlan", "show", "dev", "p1") p2 := run("bridge", "vlan", "show", "dev", "p2") if strings.Contains(p1, "10") || !strings.Contains(p2, "20") { t.Fatal(p1, p2) } if err := m.SetVLANState("v1", true); err != nil { t.Fatal(err) } if strings.Contains(run("bridge", "vlan", "show", "dev", "p2"), "20") { t.Fatal("disabled VLAN retained") } }