fn now() -> std::time::SystemTime {
std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1_700_000_000)
}
#[tokio::test]
async fn fast_rule_priority_last_config_rule_wins_for_pin() {
let t = crate::Torrent {
category: "linux".into(),
..crate::Torrent::zero()
};
let c = crate::Config {
rules: vec![
crate::Rule {
match_: crate::Match {
categories: Some(["linux".into()].into()),
..Default::default()
},
pin: Some(true),
..Default::default()
},
crate::Rule {
match_: crate::Match {
categories: Some(["linux".into()].into()),
..Default::default()
},
pin: Some(false),
..Default::default()
},
],
..crate::Config::empty()
};
let g = crate::qbt_mock::global_with_trackers(c, now(), vec![]);
let res = g.rules(&t, false).await.unwrap();
assert!(res.pinned.is_none());
}
#[tokio::test]
async fn fast_rules_compose_across_independent_fields() {
let t = crate::Torrent {
category: "linux".into(),
..crate::Torrent::zero()
};
let c = crate::Config {
rules: vec![
crate::Rule {
match_: crate::Match {
categories: Some(["linux".into()].into()),
..Default::default()
},
include_in_score: Some(false),
..Default::default()
},
crate::Rule {
match_: crate::Match {
categories: Some(["linux".into()].into()),
..Default::default()
},
pin: Some(true),
..Default::default()
},
],
..crate::Config::empty()
};
let g = crate::qbt_mock::global_with_trackers(c, now(), vec![]);
let res = g.rules(&t, false).await.unwrap();
assert!(matches!(res.pinned, Some(crate::PinReason::Explicit)));
assert!(!res.include_in_score);
}
#[tokio::test]
async fn slow_phase2_resolves_when_phase1_has_multiple_possible_pin_values() {
let t = crate::Torrent {
category: "linux".into(),
seeding_time: std::time::Duration::from_secs(1),
..crate::Torrent::zero()
};
let c = crate::Config {
categories_allowed: ["linux".into()].into(),
rules: vec![
crate::Rule {
match_: crate::Match {
tracker_msg: Some(regex::RegexSet::new([r"^ok$"]).unwrap()),
..Default::default()
},
pin: Some(true),
..Default::default()
},
crate::Rule {
match_: crate::Match {
tracker_url: Some(regex::RegexSet::new([r"^http://bad/.*$"]).unwrap()),
..Default::default()
},
pin: Some(false),
..Default::default()
},
],
..crate::Config::empty()
};
let g = crate::qbt_mock::global_with_trackers(c.clone(), now(), vec![
crate::Tracker {
msg: "ok".into(),
url: "http://t/".into(),
},
]);
let res = g.rules(&t, false).await.unwrap();
assert!(matches!(res.pinned, Some(crate::PinReason::Explicit)));
let g = crate::qbt_mock::global_with_trackers(c.clone(), now(), vec![
crate::Tracker {
msg: "nope".into(),
url: "http://bad/x".into(),
},
]);
let res = g.rules(&t, false).await.unwrap();
assert!(res.pinned.is_none());
let g = crate::qbt_mock::global_with_trackers(c.clone(), now(), vec![
crate::Tracker {
msg: "ok".into(),
url: "http://bad/x".into(),
},
]);
let res = g.rules(&t, false).await.unwrap();
assert!(res.pinned.is_none());
let g = crate::qbt_mock::global_with_trackers(c, now(), vec![
crate::Tracker {
msg: "nope".into(),
url: "http://t/".into(),
},
]);
let res = g.rules(&t, false).await.unwrap();
assert!(res.pinned.is_none());
}
#[tokio::test]
async fn mixed_fast_and_slow_rules() {
let t = crate::Torrent {
category: "linux".into(),
seeders: 5,
seeding_time: std::time::Duration::from_secs(1),
..crate::Torrent::zero()
};
let c = crate::Config {
categories_allowed: ["linux".into()].into(),
rules: vec![
crate::Rule {
match_: crate::Match {
categories: Some(["linux".into()].into()),
..Default::default()
},
include_in_score: Some(false),
..Default::default()
},
crate::Rule {
match_: crate::Match {
tracker_msg: Some(regex::RegexSet::new([r"^private$"]).unwrap()),
..Default::default()
},
seeder_count_min: Some(50),
..Default::default()
},
],
..crate::Config::empty()
};
let g = crate::qbt_mock::global_with_trackers(c.clone(), now(), vec![
crate::Tracker {
msg: "private".into(),
url: "http://t/".into(),
},
]);
let res = g.rules(&t, false).await.unwrap();
assert!(matches!(res.pinned, Some(crate::PinReason::Seeders(5))));
assert!(!res.include_in_score);
let g = crate::qbt_mock::global_with_trackers(c, now(), vec![
crate::Tracker {
msg: "public".into(),
url: "http://t/".into(),
},
]);
let res = g.rules(&t, false).await.unwrap();
assert!(res.pinned.is_none());
assert!(!res.include_in_score);
}