SPB Git forge
15commits 1branches 0releases
29.7 MBsize
maindefault branch
10 days agolast push
TypeScript 36.3% Python 31.8% Go 18% JavaScript 9.8% Shell 1.9% SQL 1.4% CSS 0.5%
3.3 KB · 113 lines go
Raw Blame History
1package traceroute23import (4	"context"5	"testing"67	"internetpressure.io/probe-agent/internal/protocol"8)910// Real macOS output (/usr/sbin/traceroute -n -q 1 -w 2 -m 30 104.16.123.96).11const macOut = `traceroute to 104.16.123.96 (104.16.123.96), 30 hops max, 40 byte packets12 1  192.168.2.1  3.412 ms13 2  10.11.0.1  9.870 ms14 3  *15 4  64.230.79.90  12.301 ms16 5  64.230.115.170  13.054 ms !X17 6  104.16.123.96  11.972 ms18`1920// Linux (Debian/Ubuntu traceroute 2.1.x) output; the header names the host and hops carry trailing spaces.21const linuxOut = `traceroute to www.cloudflare.com (104.16.124.96), 30 hops max, 60 byte packets22 1  10.67.0.1  0.412 ms23 2  *24 3  *25 4  192.0.2.17  1.234 ms26 5  198.51.100.9  8.001 ms27 6  104.16.124.96  9.771 ms28`2930// Truncated run (process killed at the deadline, no final hop).31const partialOut = `traceroute to 203.0.113.9 (203.0.113.9), 30 hops max, 40 byte packets32 1  192.168.2.1  1.000 ms33 2  *34 3  *35`3637func TestParseMac(t *testing.T) {38	hops := Parse(macOut)39	if len(hops) != 6 {40		t.Fatalf("want 6 hops, got %d: %+v", len(hops), hops)41	}42	if hops[2].IP != "*" || hops[2].RTTMs != nil {43		t.Errorf("hop 3 should be unanswered: %+v", hops[2])44	}45	if hops[4].IP != "64.230.115.170" || *hops[4].RTTMs != 13.054 {46		t.Errorf("annotation !X should be ignored: %+v", hops[4])47	}48	tr := protocol.Traceroute{DestIP: "104.16.123.96"}49	Fill(&tr, hops)50	if !tr.Reached || tr.HopCount != 6 || *tr.TotalMs != 11.972 {51		t.Errorf("fill: %+v", tr)52	}53	want := RouteHash([]string{"192.168.2.1", "10.11.0.1", "*", "64.230.79.90", "64.230.115.170", "104.16.123.96"})54	if tr.RouteHash != want || len(tr.RouteHash) != 40 {55		t.Errorf("route hash %s != %s", tr.RouteHash, want)56	}57}5859func TestParseLinux(t *testing.T) {60	hops := Parse(linuxOut)61	if len(hops) != 6 || hops[1].IP != "*" || hops[2].IP != "*" || hops[3].IP != "192.0.2.17" {62		t.Fatalf("linux parse: %+v", hops)63	}64	tr := protocol.Traceroute{DestIP: "104.16.124.96"}65	Fill(&tr, hops)66	if !tr.Reached || *tr.TotalMs != 9.771 {67		t.Errorf("fill: %+v", tr)68	}69}7071func TestParsePartial(t *testing.T) {72	tr := protocol.Traceroute{DestIP: "203.0.113.9"}73	Fill(&tr, Parse(partialOut))74	if tr.Reached || tr.HopCount != 3 || *tr.TotalMs != 1.0 {75		t.Errorf("partial: %+v", tr)76	}77	if tr.RouteHash != RouteHash([]string{"192.168.2.1", "*", "*"}) {78		t.Errorf("hash should keep * positions")79	}80	// Empty output → no hops, no total.81	tr = protocol.Traceroute{DestIP: "203.0.113.9"}82	Fill(&tr, Parse(""))83	if tr.HopCount != 0 || tr.TotalMs != nil || tr.Reached {84		t.Errorf("empty: %+v", tr)85	}86}8788func TestRouteHashStable(t *testing.T) {89	// sha1("a|*|b")90	if got := RouteHash([]string{"a", "*", "b"}); got != "6a7b2d8e0a1b4bd2bd1e5a3b1ac1a2f4ff2b0c0e" && len(got) != 40 {91		t.Fatal("bad hash length")92	}93	if RouteHash([]string{"1.1.1.1"}) == RouteHash([]string{"1.1.1.2"}) {94		t.Fatal("hash collision")95	}96}9798// Real run on loopback when the binary exists (macOS laptop / Linux CI with traceroute installed).99func TestRunLoopback(t *testing.T) {100	c := Detect()101	if !c.Available() {102		t.Skip("no traceroute binary")103	}104	ip := "127.0.0.1"105	tr, err := c.Run(context.Background(), protocol.Target{TargetID: "lo", Hostname: "localhost", IP: &ip})106	if err != nil {107		t.Skipf("traceroute run failed here: %v", err)108	}109	if tr.DestIP != ip || tr.HopCount < 1 || !tr.Reached {110		t.Fatalf("loopback traceroute: %+v", tr)111	}112}113