///|
test "matcher allows by default when no groups exist" {
let f = empty_robots_file()
assert_true(can_fetch(f, "bot", "/x"))
}
///|
test "matcher allows by default when no rule matches" {
let f = unwrap_file(parse_robots("User-agent: *\nDisallow: /private/\n"))
assert_true(can_fetch(f, "bot", "/public/"))
}
///|
test "matcher denies disallow prefix" {
let f = unwrap_file(parse_robots("User-agent: *\nDisallow: /private/\n"))
assert_false(can_fetch(f, "bot", "/private/page"))
}
///|
test "matcher accepts matching allow" {
let f = unwrap_file(
parse_robots("User-agent: *\nAllow: /public/\nDisallow: /\n"),
)
assert_true(can_fetch(f, "bot", "/public/page"))
}
///|
test "matcher uses longest allow over disallow" {
let f = unwrap_file(
parse_robots(
"User-agent: *\nDisallow: /private/\nAllow: /private/public/\n",
),
)
assert_true(can_fetch(f, "bot", "/private/public/a"))
}
///|
test "matcher uses longest disallow over allow" {
let f = unwrap_file(
parse_robots("User-agent: *\nAllow: /private/\nDisallow: /private/secret\n"),
)
assert_false(can_fetch(f, "bot", "/private/secret/a"))
}
///|
test "matcher allow wins equal length tie" {
let f = unwrap_file(parse_robots("User-agent: *\nDisallow: /x\nAllow: /x\n"))
assert_true(can_fetch(f, "bot", "/x"))
}
///|
test "matcher handles empty disallow as no effect" {
let f = unwrap_file(parse_robots("User-agent: *\nDisallow:\n"))
assert_true(can_fetch(f, "bot", "/anything"))
}
///|
test "matcher handles empty allow as no effect" {
let f = unwrap_file(
parse_robots("User-agent: *\nAllow:\nDisallow: /private/\n"),
)
assert_false(can_fetch(f, "bot", "/private/page"))
}
///|
test "matcher handles wildcard star" {
let f = unwrap_file(parse_robots("User-agent: *\nDisallow: /tmp/*.json\n"))
assert_false(can_fetch(f, "bot", "/tmp/a/report.json"))
assert_true(can_fetch(f, "bot", "/tmp/a/report.txt"))
}
///|
test "matcher wildcard star can match zero chars" {
let f = unwrap_file(parse_robots("User-agent: *\nDisallow: /tmp/*.json\n"))
assert_false(can_fetch(f, "bot", "/tmp/.json"))
}
///|
test "matcher dollar anchors to end" {
let f = unwrap_file(parse_robots("User-agent: *\nDisallow: /download$\n"))
assert_false(can_fetch(f, "bot", "/download"))
assert_true(can_fetch(f, "bot", "/download/file"))
}
///|
test "matcher path is case sensitive" {
let f = unwrap_file(parse_robots("User-agent: *\nDisallow: /Case\n"))
assert_false(can_fetch(f, "bot", "/Case"))
assert_true(can_fetch(f, "bot", "/case"))
}
///|
test "matcher user-agent is case insensitive" {
let f = unwrap_file(parse_robots("User-agent: ExampleBot\nDisallow: /\n"))
assert_false(can_fetch(f, "examplebot", "/x"))
}
///|
test "matcher does not treat product token substring as exact match" {
let f = unwrap_file(
parse_robots(
"User-agent: *\nAllow: /\n\nUser-agent: Example\nDisallow: /\n",
),
)
assert_true(can_fetch(f, "ExampleBot", "/x"))
}
///|
test "matcher product token API does not accept identification string" {
let f = unwrap_file(
parse_robots(
"User-agent: *\nAllow: /\n\nUser-agent: ExampleBot\nDisallow: /\n",
),
)
assert_true(can_fetch(f, "ExampleBot/1.0", "/x"))
}
///|
test "matcher specific group beats wildcard" {
let f = unwrap_file(
parse_robots(
"User-agent: *\nDisallow: /\n\nUser-agent: ExampleBot\nAllow: /\n",
),
)
assert_true(can_fetch(f, "ExampleBot", "/x"))
assert_false(can_fetch(f, "OtherBot", "/x"))
}
///|
test "matcher merges equally specific groups" {
let f = unwrap_file(
parse_robots(
"User-agent: bot\nDisallow: /a\n\nUser-agent: bot\nDisallow: /b\n",
),
)
assert_false(can_fetch(f, "bot", "/a"))
assert_false(can_fetch(f, "bot", "/b"))
}
///|
test "matcher rules_for returns effective merged rules" {
let f = unwrap_file(
parse_robots(
"User-agent: bot\nDisallow: /a\n\nUser-agent: bot\nAllow: /b\n",
),
)
assert_int_eq(rules_for(f, "bot").length(), 2)
}
///|
test "matcher falls back to wildcard" {
let f = unwrap_file(
parse_robots("User-agent: *\nDisallow: /all\nUser-agent: other\nAllow: /\n"),
)
assert_false(can_fetch(f, "bot", "/all"))
}
///|
test "matcher no wildcard means unrestricted" {
let f = unwrap_file(parse_robots("User-agent: other\nDisallow: /\n"))
assert_true(can_fetch(f, "bot", "/all"))
}
///|
test "matcher always allows robots txt path" {
let f = unwrap_file(parse_robots("User-agent: *\nDisallow: /\n"))
assert_true(can_fetch(f, "bot", "/robots.txt"))
}
///|
test "matcher normalizes missing leading slash" {
let f = unwrap_file(parse_robots("User-agent: *\nDisallow: /admin\n"))
assert_false(can_fetch(f, "bot", "admin"))
}
///|
test "matcher returns matched rule details" {
let f = unwrap_file(parse_robots("User-agent: *\nDisallow: /admin\n"))
let d = unwrap_decision(evaluate(f, "bot", "/admin/x"))
assert_false(d.allowed())
assert_int_eq(d.matched_length(), 6)
match d.matched_rule() {
Some(r) => assert_str_eq(r.pattern(), "/admin")
None => fail("missing matched rule")
}
}