package obs
import "testing"
func TestClassify(t *testing.T) {
for _, tc := range []struct {
name string
f Facts
kind string
crawler string
}{
{"a named crawler", Facts{UA: "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"}, KindCrawler, "Googlebot"},
{"an AI crawler", Facts{UA: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.2"}, KindCrawler, "GPTBot"},
{"a crawler that signed in is still a crawler", Facts{UA: "ClaudeBot/1.0", Account: "x"}, KindCrawler, "ClaudeBot"},
{"a verified account", Facts{UA: "curl/8.4.0", Account: "dmestas"}, KindPerson, ""},
{"an anonymous sync", Facts{UA: "zit/0.6.6", Door: DoorSync, Client: "zit"}, KindUnsure, ""},
{"no user agent", Facts{}, KindAutomated, ""},
{"a library", Facts{UA: "python-requests/2.31"}, KindAutomated, ""},
{"an unnamed bot", Facts{UA: "Mozilla/5.0 (compatible; SomeCrawler/1.0)"}, KindAutomated, ""},
{"a browser", Facts{UA: "Mozilla/5.0 (X11; Linux x86_64; rv:131.0) Gecko/20100101 Firefox/131.0"}, KindPerson, ""},
{"something else", Facts{UA: "Lynx/2.9"}, KindUnsure, ""},
} {
got := Classify(tc.f)
if got.Kind != tc.kind || got.Crawler != tc.crawler {
t.Errorf("FAIL: %s: %+v, want kind %s crawler %q", tc.name, got, tc.kind, tc.crawler)
}
}
}
// The client is named the way a reader would name it, and a sync round
// that already named its client is believed over its user agent.
func TestTheClientIsNamed(t *testing.T) {
for ua, want := range map[string]string{
"zit/0.6.6": "zit",
"zig/0.15": "zit",
"Fossil/2.26": "fossil",
"curl/8.4.0": "curl",
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 Chrome/130.0 Safari/537.36 Edg/130.0": "Edge",
"Mozilla/5.0 (Macintosh) AppleWebKit/605.1.15 Version/18.0 Safari/605.1.15": "Safari",
} {
if got := Classify(Facts{UA: ua}).Client; got != want {
t.Errorf("FAIL: %q named %q, want %q", ua, got, want)
}
}
if got := Classify(Facts{UA: "Mozilla/5.0", Door: DoorSync, Client: "fossil"}).Client; got != "fossil" {
t.Errorf("FAIL: the sync round's own client was overridden: %q", got)
}
}