mirror of
https://github.com/oneclickvirt/backtrace.git
synced 2026-08-13 20:29:14 +08:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ec85e5163 | ||
|
|
618d53469b | ||
|
|
1be6689d19 | ||
|
|
5c9b5d7c76 | ||
|
|
d6bd866d4b | ||
|
|
28e6e39718 |
@ -114,6 +114,7 @@
|
|||||||
240e:669:b000::/40
|
240e:669:b000::/40
|
||||||
240e:670:d000::/36
|
240e:670:d000::/36
|
||||||
240e:679:1001::/48
|
240e:679:1001::/48
|
||||||
|
240e:67c:2a80::/48
|
||||||
240e:688:ed00::/48
|
240e:688:ed00::/48
|
||||||
240e:699:7a00::/48
|
240e:699:7a00::/48
|
||||||
240e:699:7a01::/48
|
240e:699:7a01::/48
|
||||||
@ -138,11 +139,14 @@
|
|||||||
240e:713:f021::/48
|
240e:713:f021::/48
|
||||||
240e:733:4c0::/48
|
240e:733:4c0::/48
|
||||||
240e:733:4c1::/48
|
240e:733:4c1::/48
|
||||||
|
240e:737:2ee0::/48
|
||||||
240e:767:f000::/48
|
240e:767:f000::/48
|
||||||
240e:787:7000::/48
|
240e:787:7000::/48
|
||||||
240e:787:7001::/48
|
240e:787:7001::/48
|
||||||
240e:790:fe10::/48
|
240e:790:fe10::/48
|
||||||
240e:790:fe20::/48
|
240e:790:fe20::/48
|
||||||
|
240e:790:fe30::/48
|
||||||
|
240e:790:fe70::/48
|
||||||
240e:790:ff00::/40
|
240e:790:ff00::/40
|
||||||
240e:7b6::/31
|
240e:7b6::/31
|
||||||
240e:965:822::/48
|
240e:965:822::/48
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"schema": "backtrace.asn-prefixes/v1",
|
"schema": "backtrace.asn-prefixes/v1",
|
||||||
"file": "as4809.txt",
|
"file": "as4809.txt",
|
||||||
"count": 192,
|
"count": 196,
|
||||||
"sha256": "667e6facb5021b6f5d6c10247089895b97c11d80d0e2e4e0eb7b72f2671e554d",
|
"sha256": "44427b38edfd4cbd6ebefedccc7af0bb2332f923472bc5e67764951fbe20441e",
|
||||||
"generated_at": "2026-08-06T02:45:13Z"
|
"generated_at": "2026-08-12T02:06:59Z"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,21 +13,75 @@
|
|||||||
2408:8000:10fe::/48
|
2408:8000:10fe::/48
|
||||||
2408:8000:10ff::/48
|
2408:8000:10ff::/48
|
||||||
2408:8000:2000::/36
|
2408:8000:2000::/36
|
||||||
|
2408:8000:2009::/48
|
||||||
|
2408:8000:200e::/48
|
||||||
2408:8000:2::/48
|
2408:8000:2::/48
|
||||||
2408:8000:3000::/36
|
2408:8000:3000::/36
|
||||||
|
2408:8000:3100::/48
|
||||||
|
2408:8000:3210::/48
|
||||||
|
2408:8000:3211::/48
|
||||||
|
2408:8000:3317::/48
|
||||||
|
2408:8000:3410::/48
|
||||||
|
2408:8000:3500::/48
|
||||||
|
2408:8000:3610::/48
|
||||||
|
2408:8000:3700::/48
|
||||||
|
2408:8000:3810::/48
|
||||||
|
2408:8000:3811::/48
|
||||||
|
2408:8000:3910::/48
|
||||||
2408:8000:3::/48
|
2408:8000:3::/48
|
||||||
|
2408:8000:3a13::/48
|
||||||
|
2408:8000:3b00::/48
|
||||||
|
2408:8000:3c10::/48
|
||||||
|
2408:8000:3c11::/48
|
||||||
2408:8000:4000::/36
|
2408:8000:4000::/36
|
||||||
|
2408:8000:4000::/48
|
||||||
|
2408:8000:4002::/48
|
||||||
|
2408:8000:4003::/48
|
||||||
|
2408:8000:4004::/48
|
||||||
|
2408:8000:4005::/48
|
||||||
|
2408:8000:4006::/48
|
||||||
|
2408:8000:4007::/48
|
||||||
|
2408:8000:4008::/48
|
||||||
|
2408:8000:4009::/48
|
||||||
|
2408:8000:400a::/48
|
||||||
|
2408:8000:400b::/48
|
||||||
2408:8000:5000::/36
|
2408:8000:5000::/36
|
||||||
2408:8000:6000::/36
|
2408:8000:6000::/36
|
||||||
|
2408:8000:6001::/48
|
||||||
|
2408:8000:6002::/48
|
||||||
|
2408:8000:6003::/48
|
||||||
|
2408:8000:6004::/48
|
||||||
|
2408:8000:6005::/48
|
||||||
|
2408:8000:6006::/48
|
||||||
|
2408:8000:6007::/48
|
||||||
|
2408:8000:6008::/48
|
||||||
|
2408:8000:6009::/48
|
||||||
|
2408:8000:600a::/48
|
||||||
|
2408:8000:600b::/48
|
||||||
|
2408:8000:600c::/48
|
||||||
|
2408:8000:600d::/48
|
||||||
|
2408:8000:600e::/48
|
||||||
2408:8000:7000::/36
|
2408:8000:7000::/36
|
||||||
2408:8000:8000::/36
|
2408:8000:8000::/36
|
||||||
2408:8000:8010::/47
|
2408:8000:8010::/47
|
||||||
2408:8000:8010::/48
|
2408:8000:8010::/48
|
||||||
|
2408:8000:8040::/48
|
||||||
|
2408:8000:8090::/48
|
||||||
|
2408:8000:80c0::/48
|
||||||
2408:8000::/48
|
2408:8000::/48
|
||||||
2408:8000:a000::/36
|
2408:8000:a000::/36
|
||||||
|
2408:8000:a002::/48
|
||||||
|
2408:8000:a00d::/48
|
||||||
2408:8000:b000::/36
|
2408:8000:b000::/36
|
||||||
2408:8000:c000::/36
|
2408:8000:c000::/36
|
||||||
|
2408:8000:c038::/48
|
||||||
|
2408:8000:c03b::/48
|
||||||
|
2408:8000:c03c::/48
|
||||||
2408:8000:d000::/36
|
2408:8000:d000::/36
|
||||||
|
2408:8000:d101::/48
|
||||||
|
2408:8000:d201::/48
|
||||||
|
2408:8000:d501::/48
|
||||||
|
2408:8000:da01::/48
|
||||||
2408:8000:e000::/36
|
2408:8000:e000::/36
|
||||||
2408:8000:eeee::/48
|
2408:8000:eeee::/48
|
||||||
2408:8000:f000::/36
|
2408:8000:f000::/36
|
||||||
@ -59,7 +113,11 @@
|
|||||||
2408:8001:3142::/48
|
2408:8001:3142::/48
|
||||||
2408:8001:3152::/48
|
2408:8001:3152::/48
|
||||||
2408:8001:4000::/36
|
2408:8001:4000::/36
|
||||||
|
2408:8001:4000::/48
|
||||||
|
2408:8001:4010::/48
|
||||||
|
2408:8001:4020::/48
|
||||||
2408:8001:5000::/36
|
2408:8001:5000::/36
|
||||||
|
2408:8001:5000::/48
|
||||||
2408:8001:6000::/36
|
2408:8001:6000::/36
|
||||||
2408:8001:7000::/36
|
2408:8001:7000::/36
|
||||||
2408:8001:7030::/48
|
2408:8001:7030::/48
|
||||||
@ -68,11 +126,21 @@
|
|||||||
2408:8001::/36
|
2408:8001::/36
|
||||||
2408:8001:a000::/36
|
2408:8001:a000::/36
|
||||||
2408:8001:b000::/36
|
2408:8001:b000::/36
|
||||||
|
2408:8001:b013::/48
|
||||||
|
2408:8001:b033::/48
|
||||||
|
2408:8001:b043::/48
|
||||||
|
2408:8001:b063::/48
|
||||||
|
2408:8001:b083::/48
|
||||||
2408:8001:c000::/36
|
2408:8001:c000::/36
|
||||||
2408:8001:c000::/48
|
2408:8001:c000::/48
|
||||||
|
2408:8001:c001::/48
|
||||||
2408:8001:d000::/36
|
2408:8001:d000::/36
|
||||||
|
2408:8001:d000::/48
|
||||||
2408:8001:e000::/36
|
2408:8001:e000::/36
|
||||||
|
2408:8001:e001::/48
|
||||||
|
2408:8001:e003::/48
|
||||||
2408:8001:f000::/36
|
2408:8001:f000::/36
|
||||||
|
2408:8026:480::/48
|
||||||
2408:802a:8000::/33
|
2408:802a:8000::/33
|
||||||
2408:802c::/32
|
2408:802c::/32
|
||||||
2408:803e::/32
|
2408:803e::/32
|
||||||
@ -330,7 +398,23 @@
|
|||||||
2408:8637::/32
|
2408:8637::/32
|
||||||
2408:8638::/32
|
2408:8638::/32
|
||||||
2408:8639::/32
|
2408:8639::/32
|
||||||
|
2408:863c:2ef0::/48
|
||||||
|
2408:863c:3cf1::/48
|
||||||
|
2408:863c:40f1::/48
|
||||||
|
2408:863c:4cf1::/48
|
||||||
|
2408:863c:6ef1::/48
|
||||||
|
2408:863c:7ee0::/48
|
||||||
|
2408:863c:7ef0::/48
|
||||||
|
2408:863c:88e0::/48
|
||||||
|
2408:863c:9ce0::/48
|
||||||
|
2408:863c:9cf0::/48
|
||||||
2408:863c::/32
|
2408:863c::/32
|
||||||
|
2408:863c:a6e1::/48
|
||||||
|
2408:863c:a6f1::/48
|
||||||
|
2408:863c:b2f0::/48
|
||||||
|
2408:863d:5ae1::/48
|
||||||
|
2408:863d:6ef1::/48
|
||||||
|
2408:863d:88f0::/48
|
||||||
2408:863d::/32
|
2408:863d::/32
|
||||||
2408:8640::/32
|
2408:8640::/32
|
||||||
2408:8642::/32
|
2408:8642::/32
|
||||||
@ -395,7 +479,18 @@
|
|||||||
2408:8738::/32
|
2408:8738::/32
|
||||||
2408:873c::/32
|
2408:873c::/32
|
||||||
2408:873d::/32
|
2408:873d::/32
|
||||||
|
2408:8740:1fc0::/48
|
||||||
|
2408:8740:2fc0::/48
|
||||||
|
2408:8740:3fc0::/48
|
||||||
|
2408:8740:4fc0::/48
|
||||||
|
2408:8740:5fc0::/48
|
||||||
|
2408:8740:6fc0::/48
|
||||||
|
2408:8740:7fc0::/48
|
||||||
|
2408:8740:8fc0::/48
|
||||||
|
2408:8740:9fc0::/48
|
||||||
2408:8740::/32
|
2408:8740::/32
|
||||||
|
2408:8740:afc0::/48
|
||||||
|
2408:8740:bfc0::/48
|
||||||
2408:8742::/32
|
2408:8742::/32
|
||||||
2408:8744::/30
|
2408:8744::/30
|
||||||
2408:8748::/32
|
2408:8748::/32
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"schema": "backtrace.asn-prefixes/v1",
|
"schema": "backtrace.asn-prefixes/v1",
|
||||||
"file": "as4837.txt",
|
"file": "as4837.txt",
|
||||||
"count": 501,
|
"count": 596,
|
||||||
"sha256": "ba29c098dbf8c87ab118cb206b59fa6bd093dbf03ccaf3342959766a8adf6b48",
|
"sha256": "903d9b20db80f5696a3c124cda2c73072cd5d8acb992d9b55f369d8b435a65d0",
|
||||||
"generated_at": "2026-08-06T02:45:13Z"
|
"generated_at": "2026-08-13T02:09:16Z"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -167,6 +167,7 @@
|
|||||||
2409:802e:2905::/48
|
2409:802e:2905::/48
|
||||||
2409:802e:2906::/48
|
2409:802e:2906::/48
|
||||||
2409:802e:2909::/48
|
2409:802e:2909::/48
|
||||||
|
2409:802e:290b::/48
|
||||||
2409:802e:290d::/48
|
2409:802e:290d::/48
|
||||||
2409:802e:2a05::/48
|
2409:802e:2a05::/48
|
||||||
2409:802e:2a06::/48
|
2409:802e:2a06::/48
|
||||||
@ -701,16 +702,21 @@
|
|||||||
2409:8069:290d::/48
|
2409:8069:290d::/48
|
||||||
2409:8069:290e::/48
|
2409:8069:290e::/48
|
||||||
2409:8069:290f::/48
|
2409:8069:290f::/48
|
||||||
|
2409:8069:2910::/48
|
||||||
2409:8069:2a00::/40
|
2409:8069:2a00::/40
|
||||||
2409:8069:2a05::/48
|
2409:8069:2a05::/48
|
||||||
2409:8069:2a07::/48
|
2409:8069:2a07::/48
|
||||||
|
2409:8069:2a0b::/48
|
||||||
2409:8069:2a0e::/48
|
2409:8069:2a0e::/48
|
||||||
|
2409:8069:2a10::/48
|
||||||
2409:8069:2b00::/40
|
2409:8069:2b00::/40
|
||||||
2409:8069:2b05::/48
|
2409:8069:2b05::/48
|
||||||
2409:8069:2b07::/48
|
2409:8069:2b07::/48
|
||||||
|
2409:8069:2b0b::/48
|
||||||
2409:8069:2b0c::/48
|
2409:8069:2b0c::/48
|
||||||
2409:8069:2b0e::/48
|
2409:8069:2b0e::/48
|
||||||
2409:8069:2b0f::/48
|
2409:8069:2b0f::/48
|
||||||
|
2409:8069:2b10::/48
|
||||||
2409:8069:2c00::/40
|
2409:8069:2c00::/40
|
||||||
2409:8069:2c05::/48
|
2409:8069:2c05::/48
|
||||||
2409:8069:2c06::/48
|
2409:8069:2c06::/48
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"schema": "backtrace.asn-prefixes/v1",
|
"schema": "backtrace.asn-prefixes/v1",
|
||||||
"file": "as9808.txt",
|
"file": "as9808.txt",
|
||||||
"count": 7125,
|
"count": 7131,
|
||||||
"sha256": "68784a373ac0450daa984d252954a8231b9118a79c113df971d68a20aa12b8f0",
|
"sha256": "9b66caccde8e09b1e167de1d0e7a05b29b203c0ed7032361002fec052fe9d096",
|
||||||
"generated_at": "2026-08-06T02:45:13Z"
|
"generated_at": "2026-08-08T01:47:24Z"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -111,6 +111,7 @@
|
|||||||
2408:864c:10ff::/48
|
2408:864c:10ff::/48
|
||||||
2408:8651:ff00::/48
|
2408:8651:ff00::/48
|
||||||
2408:8652:ff00::/48
|
2408:8652:ff00::/48
|
||||||
|
2408:8656:22cf::/48
|
||||||
2408:8656:a52::/48
|
2408:8656:a52::/48
|
||||||
2408:865c:f0::/44
|
2408:865c:f0::/44
|
||||||
2408:8660:100::/48
|
2408:8660:100::/48
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"schema": "backtrace.asn-prefixes/v1",
|
"schema": "backtrace.asn-prefixes/v1",
|
||||||
"file": "as9929.txt",
|
"file": "as9929.txt",
|
||||||
"count": 144,
|
"count": 145,
|
||||||
"sha256": "a5f65f08a447982cd2c8611005261deab3400f1232ae594fb98f6bfb3fe09084",
|
"sha256": "0764b222d3f24c65be191b0300eaf9942416dda766bb9b70ec21c5836f9bcac9",
|
||||||
"generated_at": "2026-07-20T19:12:54Z"
|
"generated_at": "2026-08-08T01:47:24Z"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/oneclickvirt/backtrace/model"
|
||||||
. "github.com/oneclickvirt/defaultset"
|
. "github.com/oneclickvirt/defaultset"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,22 +14,12 @@ import (
|
|||||||
func RenderRouteReport(report RouteReport) string {
|
func RenderRouteReport(report RouteReport) string {
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
for _, target := range report.Targets {
|
for _, target := range report.Targets {
|
||||||
label := target.Classification.Label
|
|
||||||
if label == "" {
|
|
||||||
label = "线路证据不足"
|
|
||||||
}
|
|
||||||
var rendered string
|
var rendered string
|
||||||
switch {
|
switch {
|
||||||
case target.Status != RouteProbeAvailable:
|
case target.Status != RouteProbeAvailable:
|
||||||
rendered = Red("检测不到回程路由节点的IP地址")
|
rendered = Red("检测不到回程路由节点的IP地址")
|
||||||
case target.Classification.Confidence == routeConfidenceInconclusive:
|
|
||||||
rendered = Red(label)
|
|
||||||
case target.Classification.Rank >= 4:
|
|
||||||
rendered = DarkGreen(label)
|
|
||||||
case target.Classification.Rank == 3:
|
|
||||||
rendered = Green(label)
|
|
||||||
default:
|
default:
|
||||||
rendered = White(label)
|
rendered = renderLegacyRouteLabels(target)
|
||||||
}
|
}
|
||||||
addressWidth := 15
|
addressWidth := 15
|
||||||
if target.Target.IPVersion == "v6" {
|
if target.Target.IPVersion == "v6" {
|
||||||
@ -38,3 +29,89 @@ func RenderRouteReport(report RouteReport) string {
|
|||||||
}
|
}
|
||||||
return strings.TrimSuffix(builder.String(), "\n")
|
return strings.TrimSuffix(builder.String(), "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// renderLegacyRouteLabels keeps the original terminal contract: every known
|
||||||
|
// carrier ASN observed in the trace remains visible. The conservative
|
||||||
|
// Classification is retained for JSON consumers, but its evidence-oriented
|
||||||
|
// fallback labels must not replace the classic human-readable route result.
|
||||||
|
func renderLegacyRouteLabels(target RouteTargetReport) string {
|
||||||
|
asns := uniqueStrings(target.ObservedASNs)
|
||||||
|
hasAS4134 := containsString(asns, "AS4134")
|
||||||
|
hasAS4809 := containsString(asns, "AS4809")
|
||||||
|
|
||||||
|
ordered := make([]string, 0, len(asns)+1)
|
||||||
|
if hasAS4809 {
|
||||||
|
if hasAS4134 {
|
||||||
|
ordered = append(ordered, "AS4809b")
|
||||||
|
} else {
|
||||||
|
ordered = append(ordered, "AS4809a")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ordered = append(ordered, asns...)
|
||||||
|
|
||||||
|
seenLabels := make(map[string]struct{})
|
||||||
|
labels := make([]string, 0, len(ordered))
|
||||||
|
for _, asn := range ordered {
|
||||||
|
if asn == "AS4809" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
label := model.M[asn]
|
||||||
|
if label == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, exists := seenLabels[label]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seenLabels[label] = struct{}{}
|
||||||
|
switch asn {
|
||||||
|
case "AS9929", "AS4809a", "AS23764":
|
||||||
|
labels = append(labels, DarkGreen(label))
|
||||||
|
case "AS4809b", "AS58807":
|
||||||
|
labels = append(labels, Green(label))
|
||||||
|
default:
|
||||||
|
labels = append(labels, White(label))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(labels) > 0 {
|
||||||
|
return strings.Join(labels, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reports produced by older API clients may contain only Classification.
|
||||||
|
if target.Classification.Confidence != routeConfidenceInconclusive && target.Classification.Label != "" {
|
||||||
|
switch {
|
||||||
|
case target.Classification.Rank >= 4:
|
||||||
|
return DarkGreen(target.Classification.Label)
|
||||||
|
case target.Classification.Rank == 3:
|
||||||
|
return Green(target.Classification.Label)
|
||||||
|
default:
|
||||||
|
return White(target.Classification.Label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Red("检测不到已知线路的ASN")
|
||||||
|
}
|
||||||
|
|
||||||
|
func uniqueStrings(values []string) []string {
|
||||||
|
seen := make(map[string]struct{}, len(values))
|
||||||
|
result := make([]string, 0, len(values))
|
||||||
|
for _, value := range values {
|
||||||
|
value = strings.ToUpper(strings.TrimSpace(value))
|
||||||
|
if value == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, exists := seen[value]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[value] = struct{}{}
|
||||||
|
result = append(result, value)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsString(values []string, target string) bool {
|
||||||
|
for _, value := range values {
|
||||||
|
if value == target {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@ -39,7 +39,8 @@ func TestRunRouteReportOfflineFixture(t *testing.T) {
|
|||||||
if target.Classification.Code != "ct_cn2_gia" || target.Latency.Samples != 6 || target.Latency.P95MS != 30 {
|
if target.Classification.Code != "ct_cn2_gia" || target.Latency.Samples != 6 || target.Latency.P95MS != 30 {
|
||||||
t.Fatalf("unexpected route classification or latency: %+v", target)
|
t.Fatalf("unexpected route classification or latency: %+v", target)
|
||||||
}
|
}
|
||||||
if strings.Contains(RenderRouteReport(report), "P95") || !strings.Contains(RenderRouteReport(report), "电信CN2GIA") {
|
rendered := RenderRouteReport(report)
|
||||||
|
if strings.Contains(rendered, "P95") || !strings.Contains(rendered, "电信CN2GT") || !strings.Contains(rendered, "电信163") {
|
||||||
t.Fatalf("legacy rendering is not compact: %q", RenderRouteReport(report))
|
t.Fatalf("legacy rendering is not compact: %q", RenderRouteReport(report))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,6 +89,55 @@ func TestRenderRouteReportPreservesLegacyAddressWidths(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRenderRouteReportUsesClassicASNLabels(t *testing.T) {
|
||||||
|
report := RouteReport{Targets: []RouteTargetReport{
|
||||||
|
{
|
||||||
|
Target: RouteTarget{Name: "北京电信v4", Address: "219.141.140.10", IPVersion: "v4", Carrier: "CT"},
|
||||||
|
Status: RouteProbeAvailable,
|
||||||
|
ObservedASNs: []string{"AS6453", "AS4134"},
|
||||||
|
Classification: inconclusiveClassification("ct_destination_only", "仅见电信目的网", "only one AS4134 hop"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Target: RouteTarget{Name: "上海电信v4", Address: "202.96.209.133", IPVersion: "v4", Carrier: "CT"},
|
||||||
|
Status: RouteProbeAvailable,
|
||||||
|
ObservedASNs: []string{"AS4809", "AS4134"},
|
||||||
|
Classification: RouteClassification{Code: "ct_cn2_mixed", Label: "电信CN2混合 [优质线路]", Confidence: routeConfidenceMixed, Rank: 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Target: RouteTarget{Name: "广州联通v4", Address: "210.21.196.6", IPVersion: "v4", Carrier: "CU"},
|
||||||
|
Status: RouteProbeAvailable,
|
||||||
|
ObservedASNs: []string{"AS4837"},
|
||||||
|
Classification: inconclusiveClassification("cu_destination_only", "仅见联通目的网", "only one AS4837 hop"),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
rendered := regexp.MustCompile(`\x1b\[[0-9;]*m`).ReplaceAllString(RenderRouteReport(report), "")
|
||||||
|
for _, want := range []string{
|
||||||
|
"北京电信v4 219.141.140.10 电信163 [普通线路]",
|
||||||
|
"上海电信v4 202.96.209.133 电信CN2GT [优质线路] 电信163 [普通线路]",
|
||||||
|
"广州联通v4 210.21.196.6 联通4837 [普通线路]",
|
||||||
|
} {
|
||||||
|
if !strings.Contains(rendered, want) {
|
||||||
|
t.Fatalf("classic route label missing %q from %q", want, rendered)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.Contains(rendered, "仅见") || strings.Contains(rendered, "未见") {
|
||||||
|
t.Fatalf("structured fallback label leaked into classic output: %q", rendered)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderRouteReportUsesClassicUnknownMessage(t *testing.T) {
|
||||||
|
report := RouteReport{Targets: []RouteTargetReport{{
|
||||||
|
Target: RouteTarget{Name: "北京电信v4", Address: "219.141.140.10", IPVersion: "v4", Carrier: "CT"},
|
||||||
|
Status: RouteProbeAvailable,
|
||||||
|
ObservedASNs: []string{"AS6453"},
|
||||||
|
Classification: inconclusiveClassification("ct_unknown", "未见电信骨干", "known carrier ASNs are absent"),
|
||||||
|
}}}
|
||||||
|
rendered := regexp.MustCompile(`\x1b\[[0-9;]*m`).ReplaceAllString(RenderRouteReport(report), "")
|
||||||
|
if !strings.Contains(rendered, "检测不到已知线路的ASN") || strings.Contains(rendered, "未见电信骨干") {
|
||||||
|
t.Fatalf("unexpected classic unknown route output: %q", rendered)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunRouteReportHonorsContext(t *testing.T) {
|
func TestRunRouteReportHonorsContext(t *testing.T) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
cancel()
|
cancel()
|
||||||
|
|||||||
@ -2,7 +2,7 @@ package model
|
|||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
const BackTraceVersion = "v0.0.19"
|
const BackTraceVersion = "v0.0.20"
|
||||||
|
|
||||||
var EnableLoger = false
|
var EnableLoger = false
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user