Files
bird-exporter/cmd/bird-exporter/main_test.go
2025-12-31 15:36:54 +01:00

417 lines
12 KiB
Go

package main
import (
"flag"
"os"
"testing"
)
func TestParseBirdOutput(t *testing.T) {
file, err := os.Open("testdata/sample_output.txt")
if err != nil {
t.Fatalf("Failed to open test file: %v", err)
}
defer file.Close()
protocols, err := parseBirdOutput(file)
if err != nil {
t.Fatalf("parseBirdOutput() failed: %v", err)
}
// Check we got the expected number of protocols
expectedProtocols := 8 // device1, direct1, kernel4, static4, ospf4, fogixp_47498_ipv4_3, fogixp_47498_ipv6_1, fogixp_47498_ipv6_2
if len(protocols) != expectedProtocols {
t.Errorf("Expected %d protocols, got %d", expectedProtocols, len(protocols))
}
// Test Device protocol
device := findProtocol(protocols, "device1")
if device == nil {
t.Fatal("device1 protocol not found")
}
if device.Proto != "Device" {
t.Errorf("Expected proto 'Device', got '%s'", device.Proto)
}
if device.State != "up" {
t.Errorf("Expected state 'up', got '%s'", device.State)
}
if device.Table != "---" {
t.Errorf("Expected table '---', got '%s'", device.Table)
}
// Test Direct protocol with channels
direct := findProtocol(protocols, "direct1")
if direct == nil {
t.Fatal("direct1 protocol not found")
}
if direct.Proto != "Direct" {
t.Errorf("Expected proto 'Direct', got '%s'", direct.Proto)
}
if len(direct.Channels) != 2 {
t.Errorf("Expected 2 channels, got %d", len(direct.Channels))
}
// Test direct1 ipv4 channel
ipv4Chan := findChannel(direct.Channels, "ipv4")
if ipv4Chan == nil {
t.Fatal("ipv4 channel not found in direct1")
}
if ipv4Chan.State != "UP" {
t.Errorf("Expected channel state 'UP', got '%s'", ipv4Chan.State)
}
if ipv4Chan.Table != "master4" {
t.Errorf("Expected table 'master4', got '%s'", ipv4Chan.Table)
}
if ipv4Chan.Preference != 240 {
t.Errorf("Expected preference 240, got %d", ipv4Chan.Preference)
}
if ipv4Chan.InputFilter != "ACCEPT" {
t.Errorf("Expected input filter 'ACCEPT', got '%s'", ipv4Chan.InputFilter)
}
if ipv4Chan.OutputFilter != "REJECT" {
t.Errorf("Expected output filter 'REJECT', got '%s'", ipv4Chan.OutputFilter)
}
// Test route stats
if ipv4Chan.Routes.Imported != 10 {
t.Errorf("Expected 10 imported routes, got %d", ipv4Chan.Routes.Imported)
}
if ipv4Chan.Routes.Exported != 0 {
t.Errorf("Expected 0 exported routes, got %d", ipv4Chan.Routes.Exported)
}
if ipv4Chan.Routes.Preferred != 10 {
t.Errorf("Expected 10 preferred routes, got %d", ipv4Chan.Routes.Preferred)
}
// Test import stats
if ipv4Chan.ImportStats.Updates != 155 {
t.Errorf("Expected 155 import updates, got %d", ipv4Chan.ImportStats.Updates)
}
if ipv4Chan.ImportStats.Rejected != 0 {
t.Errorf("Expected 0 import rejected, got %d", ipv4Chan.ImportStats.Rejected)
}
if ipv4Chan.ImportStats.Filtered != 0 {
t.Errorf("Expected 0 import filtered, got %d", ipv4Chan.ImportStats.Filtered)
}
if ipv4Chan.ImportStats.Ignored != 0 {
t.Errorf("Expected 0 import ignored, got %d", ipv4Chan.ImportStats.Ignored)
}
if ipv4Chan.ImportStats.Accepted != 155 {
t.Errorf("Expected 155 import accepted, got %d", ipv4Chan.ImportStats.Accepted)
}
if ipv4Chan.ImportStats.Withdraws != 153 {
t.Errorf("Expected 153 import withdraws, got %d", ipv4Chan.ImportStats.Withdraws)
}
// Test export stats
if ipv4Chan.ExportStats.Updates != 0 {
t.Errorf("Expected 0 export updates, got %d", ipv4Chan.ExportStats.Updates)
}
if ipv4Chan.ExportStats.Withdraws != 0 {
t.Errorf("Expected 0 export withdraws, got %d", ipv4Chan.ExportStats.Withdraws)
}
// Test direct1 ipv6 channel
ipv6Chan := findChannel(direct.Channels, "ipv6")
if ipv6Chan == nil {
t.Fatal("ipv6 channel not found in direct1")
}
if ipv6Chan.Routes.Imported != 5 {
t.Errorf("Expected 5 imported routes, got %d", ipv6Chan.Routes.Imported)
}
}
func TestParseBGPProtocol(t *testing.T) {
file, err := os.Open("testdata/sample_output.txt")
if err != nil {
t.Fatalf("Failed to open test file: %v", err)
}
defer file.Close()
protocols, err := parseBirdOutput(file)
if err != nil {
t.Fatalf("parseBirdOutput() failed: %v", err)
}
// Test BGP protocol (established)
bgp := findProtocol(protocols, "fogixp_47498_ipv4_3")
if bgp == nil {
t.Fatal("fogixp_47498_ipv4_3 protocol not found")
}
if bgp.Proto != "BGP" {
t.Errorf("Expected proto 'BGP', got '%s'", bgp.Proto)
}
if bgp.State != "up" {
t.Errorf("Expected state 'up', got '%s'", bgp.State)
}
if bgp.Info != "Established" {
t.Errorf("Expected info 'Established', got '%s'", bgp.Info)
}
// Test BGP info
if bgp.BGPInfo == nil {
t.Fatal("BGP info is nil")
}
if bgp.BGPInfo.BGPState != "Established" {
t.Errorf("Expected BGP state 'Established', got '%s'", bgp.BGPInfo.BGPState)
}
if bgp.BGPInfo.NeighborAddr != "185.1.147.3" {
t.Errorf("Expected neighbor address '185.1.147.3', got '%s'", bgp.BGPInfo.NeighborAddr)
}
if bgp.BGPInfo.NeighborAS != "47498" {
t.Errorf("Expected neighbor AS '47498', got '%s'", bgp.BGPInfo.NeighborAS)
}
if bgp.BGPInfo.LocalAS != "8298" {
t.Errorf("Expected local AS '8298', got '%s'", bgp.BGPInfo.LocalAS)
}
if bgp.BGPInfo.NeighborID != "185.1.147.3" {
t.Errorf("Expected neighbor ID '185.1.147.3', got '%s'", bgp.BGPInfo.NeighborID)
}
// Test BGP timers
if bgp.BGPInfo.HoldTimer != 174.765 {
t.Errorf("Expected hold timer 174.765, got %f", bgp.BGPInfo.HoldTimer)
}
if bgp.BGPInfo.KeepaliveTimer != 30.587 {
t.Errorf("Expected keepalive timer 30.587, got %f", bgp.BGPInfo.KeepaliveTimer)
}
if bgp.BGPInfo.SendHoldTimer != 425.866 {
t.Errorf("Expected send hold timer 425.866, got %f", bgp.BGPInfo.SendHoldTimer)
}
// Test BGP channel
if len(bgp.Channels) != 1 {
t.Errorf("Expected 1 channel, got %d", len(bgp.Channels))
}
bgpChan := findChannel(bgp.Channels, "ipv4")
if bgpChan == nil {
t.Fatal("ipv4 channel not found in fogixp_47498_ipv4_3")
}
if bgpChan.Routes.Imported != 44589 {
t.Errorf("Expected 44589 imported routes, got %d", bgpChan.Routes.Imported)
}
if bgpChan.Routes.Filtered != 6 {
t.Errorf("Expected 6 filtered routes, got %d", bgpChan.Routes.Filtered)
}
if bgpChan.Routes.Exported != 27 {
t.Errorf("Expected 27 exported routes, got %d", bgpChan.Routes.Exported)
}
if bgpChan.Routes.Preferred != 4173 {
t.Errorf("Expected 4173 preferred routes, got %d", bgpChan.Routes.Preferred)
}
}
func TestParseKernelProtocol(t *testing.T) {
file, err := os.Open("testdata/sample_output.txt")
if err != nil {
t.Fatalf("Failed to open test file: %v", err)
}
defer file.Close()
protocols, err := parseBirdOutput(file)
if err != nil {
t.Fatalf("parseBirdOutput() failed: %v", err)
}
kernel := findProtocol(protocols, "kernel4")
if kernel == nil {
t.Fatal("kernel4 protocol not found")
}
if len(kernel.Channels) != 1 {
t.Fatalf("Expected 1 channel, got %d", len(kernel.Channels))
}
ipv4Chan := kernel.Channels[0]
if ipv4Chan.Routes.Exported != 1044496 {
t.Errorf("Expected 1044496 exported routes, got %d", ipv4Chan.Routes.Exported)
}
if ipv4Chan.ExportStats.Updates != 1684718679 {
t.Errorf("Expected 1684718679 export updates, got %d", ipv4Chan.ExportStats.Updates)
}
if ipv4Chan.ExportStats.Filtered != 143 {
t.Errorf("Expected 143 export filtered, got %d", ipv4Chan.ExportStats.Filtered)
}
if ipv4Chan.ExportStats.Accepted != 1684718536 {
t.Errorf("Expected 1684718536 export accepted, got %d", ipv4Chan.ExportStats.Accepted)
}
if ipv4Chan.ExportStats.Withdraws != 37968736 {
t.Errorf("Expected 37968736 export withdraws, got %d", ipv4Chan.ExportStats.Withdraws)
}
}
func TestParseOSPFProtocol(t *testing.T) {
file, err := os.Open("testdata/sample_output.txt")
if err != nil {
t.Fatalf("Failed to open test file: %v", err)
}
defer file.Close()
protocols, err := parseBirdOutput(file)
if err != nil {
t.Fatalf("parseBirdOutput() failed: %v", err)
}
ospf := findProtocol(protocols, "ospf4")
if ospf == nil {
t.Fatal("ospf4 protocol not found")
}
if ospf.Proto != "OSPF" {
t.Errorf("Expected proto 'OSPF', got '%s'", ospf.Proto)
}
if ospf.Info != "Running" {
t.Errorf("Expected info 'Running', got '%s'", ospf.Info)
}
if len(ospf.Channels) != 1 {
t.Fatalf("Expected 1 channel, got %d", len(ospf.Channels))
}
ipv4Chan := ospf.Channels[0]
if ipv4Chan.InputFilter != "f_ospf" {
t.Errorf("Expected input filter 'f_ospf', got '%s'", ipv4Chan.InputFilter)
}
if ipv4Chan.Routes.Imported != 26 {
t.Errorf("Expected 26 imported routes, got %d", ipv4Chan.Routes.Imported)
}
if ipv4Chan.Routes.Exported != 4 {
t.Errorf("Expected 4 exported routes, got %d", ipv4Chan.Routes.Exported)
}
if ipv4Chan.Routes.Preferred != 25 {
t.Errorf("Expected 25 preferred routes, got %d", ipv4Chan.Routes.Preferred)
}
}
func TestParseStaticProtocol(t *testing.T) {
file, err := os.Open("testdata/sample_output.txt")
if err != nil {
t.Fatalf("Failed to open test file: %v", err)
}
defer file.Close()
protocols, err := parseBirdOutput(file)
if err != nil {
t.Fatalf("parseBirdOutput() failed: %v", err)
}
static := findProtocol(protocols, "static4")
if static == nil {
t.Fatal("static4 protocol not found")
}
if static.Proto != "Static" {
t.Errorf("Expected proto 'Static', got '%s'", static.Proto)
}
if len(static.Channels) != 1 {
t.Fatalf("Expected 1 channel, got %d", len(static.Channels))
}
ipv4Chan := static.Channels[0]
if ipv4Chan.Preference != 200 {
t.Errorf("Expected preference 200, got %d", ipv4Chan.Preference)
}
if ipv4Chan.Routes.Imported != 3 {
t.Errorf("Expected 3 imported routes, got %d", ipv4Chan.Routes.Imported)
}
}
func TestParseBirdOutputWithRealFile(t *testing.T) {
// Test with the actual birdc.show.proto.all file if it exists
if _, err := os.Stat("../../birdc.show.proto.all"); os.IsNotExist(err) {
t.Skip("Real birdc.show.proto.all file not found, skipping")
}
file, err := os.Open("../../birdc.show.proto.all")
if err != nil {
t.Fatalf("Failed to open real file: %v", err)
}
defer file.Close()
protocols, err := parseBirdOutput(file)
if err != nil {
t.Fatalf("parseBirdOutput() failed on real file: %v", err)
}
if len(protocols) == 0 {
t.Error("Expected at least one protocol from real file")
}
// Check that we have various protocol types
hasDevice := false
hasBGP := false
for _, p := range protocols {
if p.Proto == "Device" {
hasDevice = true
}
if p.Proto == "BGP" {
hasBGP = true
}
}
if !hasDevice {
t.Error("Expected to find at least one Device protocol")
}
if !hasBGP {
t.Error("Expected to find at least one BGP protocol")
}
}
func TestParseEmptyFile(t *testing.T) {
// Create a temporary empty file
tmpFile, err := os.CreateTemp("", "empty-*.txt")
if err != nil {
t.Fatalf("Failed to create temp file: %v", err)
}
defer os.Remove(tmpFile.Name())
tmpFile.Close()
file, err := os.Open(tmpFile.Name())
if err != nil {
t.Fatalf("Failed to open temp file: %v", err)
}
defer file.Close()
protocols, err := parseBirdOutput(file)
if err != nil {
t.Fatalf("parseBirdOutput() failed: %v", err)
}
if len(protocols) != 0 {
t.Errorf("Expected 0 protocols from empty file, got %d", len(protocols))
}
}
func TestParseFileNotFound(t *testing.T) {
_, err := os.Open("/tmp/does-not-exist-bird-test-12345.txt")
if err == nil {
t.Error("Expected error when opening non-existent file")
}
}
// Helper functions
func findProtocol(protocols []Protocol, name string) *Protocol {
for i := range protocols {
if protocols[i].Name == name {
return &protocols[i]
}
}
return nil
}
func findChannel(channels []Channel, name string) *Channel {
for i := range channels {
if channels[i].Name == name {
return &channels[i]
}
}
return nil
}
// TestMain to handle flag parsing for tests
func TestMain(m *testing.M) {
flag.Parse()
os.Exit(m.Run())
}