package dynamicrouting import ( "strings" "testing" "time" ) func TestBGPConfigGeneration(t *testing.T) { m := &Manager{} m.config.BGPInstance = BGPInstance{ Enabled: true, AS: 65001, RouterID: "192.168.131.56", RedistributeStatic: true, } m.config.BGPNetworks = []BGPNetwork{ { ID: "net-1", Network: "192.168.100.0/24", Disabled: false, }, } m.config.BGPPeers = []BGPPeer{ { ID: "peer-1", Name: "TestPeer", RemoteAddress: "192.168.129.225", RemoteAS: 65555, Passive: true, NexthopSelf: true, RedistStatic: true, OutputNetworks: []string{"192.168.200.0/24"}, }, } // Mock bird and birdc dir := t.TempDir() t.Setenv("PATH", dir+":"+dir) // Test configuration generation sb := &strings.Builder{} // Test the export filter logic directly exportSources := []string{`source = RTS_BGP`} if m.config.BGPPeers[0].RedistStatic || m.config.BGPInstance.RedistributeStatic { exportSources = append(exportSources, `source ~ [ RTS_STATIC, RTS_INHERIT ]`) } exportRule := strings.Join(exportSources, ") || (") if !strings.Contains(exportRule, "RTS_INHERIT") { t.Fatalf("Expected exportRule to contain RTS_INHERIT for kernel static routes, got %s", exportRule) } _ = sb } func TestParseBirdSinceToUptime(t *testing.T) { refTime := time.Date(2026, 11, 2, 12, 0, 0, 0, time.Local) tests := []struct { name string input string expected string }{ { name: "ISO long same day", input: "2026-11-02 10:06:15", expected: "01h 53m 45s", }, { name: "ISO long with milliseconds", input: "2026-11-02 10:06:15.123", expected: "01h 53m 44s", }, { name: "ISO long previous day", input: "2026-10-31 13:15:43", expected: "1d 22h 44m 17s", }, { name: "Date only", input: "2026-10-31", expected: "2d 12h 00m 00s", }, { name: "Time only with ms", input: "10:06:15.631", expected: "01h 53m 45s", }, { name: "Empty", input: "", expected: "", }, { name: "Invalid", input: "not-a-date", expected: "", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got := parseBirdSinceToUptime(tc.input, refTime) if got != tc.expected { t.Errorf("%s: got %q, want %q", tc.name, got, tc.expected) } }) } } func TestTranslateRos7RuleToBird(t *testing.T) { tests := []struct { name string rule RoutingFilterRule contains []string omits []string }{ { name: "Equals conversion in script", rule: RoutingFilterRule{ Rule: "if (dst in 10.0.0.0/8 && dst-len == 24) { accept; }", }, contains: []string{"net ~ [ 10.0.0.0/8+ ]", "net.len = 24", ") then {"}, omits: []string{"==", "dst-len"}, }, { name: "Inequality preserved", rule: RoutingFilterRule{ Rule: "if (dst != 192.168.1.0/24) { accept; }", }, contains: []string{"net != 192.168.1.0/24"}, omits: []string{"dst !="}, }, { name: "Passthrough action in structured form", rule: RoutingFilterRule{ Prefix: "10.0.0.0/8", Action: "passthrough", SetLocalPref: 150, }, contains: []string{"bgp_local_pref = 150;"}, omits: []string{"accept;", "reject;"}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got := translateRos7RuleToBird(&tc.rule) for _, exp := range tc.contains { if !strings.Contains(got, exp) { t.Errorf("%s: expected to contain %q, got: %s", tc.name, exp, got) } } for _, om := range tc.omits { if strings.Contains(got, om) { t.Errorf("%s: expected to NOT contain %q, got: %s", tc.name, om, got) } } }) } }