mirror of
https://github.com/telemt/telemt.git
synced 2026-09-05 18:16:06 +03:00
cf0cd08387
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
272 lines
7.7 KiB
Rust
272 lines
7.7 KiB
Rust
use super::*;
|
|
use crate::web::manager::{CarrierCapabilities, CarrierRequest};
|
|
|
|
fn request(hash: u8) -> CarrierRequest {
|
|
CarrierRequest::automatic(
|
|
CarrierClientClass::Bridge,
|
|
CarrierCapabilities::all(),
|
|
1,
|
|
None,
|
|
[hash; 32],
|
|
)
|
|
}
|
|
|
|
fn context(hash: u8) -> CarrierLearningContext {
|
|
CarrierLearningContext {
|
|
profile_key: [1; 32],
|
|
client_ip: IpAddr::V4(std::net::Ipv4Addr::new(192, 0, 2, hash)),
|
|
class: CarrierClientClass::Bridge,
|
|
user_agent_hash: [hash; 32],
|
|
epoch: 1,
|
|
ip_learning_eligible: true,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn policy_epoch_rejects_late_outcomes_and_clears_state() {
|
|
let now = Instant::now();
|
|
let mut learning = CarrierLearning::new(6);
|
|
let epoch = learning
|
|
.apply_policy(
|
|
now,
|
|
true,
|
|
WebCarrierNegotiationAggressiveness::Aggressive,
|
|
Duration::from_secs(10),
|
|
)
|
|
.unwrap();
|
|
learning.record_chain(now, epoch, context(1), &[], WebCarrier::Websocket);
|
|
assert_eq!(learning.entries.len(), 3);
|
|
let next = learning
|
|
.apply_policy(
|
|
now,
|
|
false,
|
|
WebCarrierNegotiationAggressiveness::Aggressive,
|
|
Duration::from_secs(10),
|
|
)
|
|
.unwrap();
|
|
assert_ne!(epoch, next);
|
|
learning.record_chain(now, epoch, context(1), &[], WebCarrier::Https);
|
|
assert!(learning.entries.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn aggressive_policy_ranks_one_atomic_chain_sample() {
|
|
let now = Instant::now();
|
|
let mut learning = CarrierLearning::new(6);
|
|
let epoch = learning
|
|
.apply_policy(
|
|
now,
|
|
true,
|
|
WebCarrierNegotiationAggressiveness::Aggressive,
|
|
Duration::from_secs(10),
|
|
)
|
|
.unwrap();
|
|
learning.record_chain(now, epoch, context(2), &[], WebCarrier::Websocket);
|
|
let (ranked, scores) = learning.rank(
|
|
now,
|
|
&[
|
|
WebCarrier::Https,
|
|
WebCarrier::Websocket,
|
|
WebCarrier::HttpsLanes,
|
|
],
|
|
request(2),
|
|
[1; 32],
|
|
context(2).client_ip,
|
|
true,
|
|
);
|
|
assert_eq!(
|
|
ranked,
|
|
[
|
|
WebCarrier::Websocket,
|
|
WebCarrier::Https,
|
|
WebCarrier::HttpsLanes,
|
|
]
|
|
);
|
|
assert_eq!(scores[WebCarrier::Websocket.index()], 33);
|
|
}
|
|
|
|
#[test]
|
|
fn two_half_windows_expire_without_sliding_updates() {
|
|
let start = Instant::now();
|
|
let mut learning = CarrierLearning::new(6);
|
|
let epoch = learning
|
|
.apply_policy(
|
|
start,
|
|
true,
|
|
WebCarrierNegotiationAggressiveness::Aggressive,
|
|
Duration::from_secs(10),
|
|
)
|
|
.unwrap();
|
|
learning.record_chain(start, epoch, context(3), &[], WebCarrier::Websocket);
|
|
learning.record_chain(
|
|
start + Duration::from_secs(6),
|
|
epoch,
|
|
context(3),
|
|
&[],
|
|
WebCarrier::Websocket,
|
|
);
|
|
learning.prune(start + Duration::from_secs(11));
|
|
assert_eq!(learning.entries.len(), 3);
|
|
learning.prune(start + Duration::from_secs(16));
|
|
assert!(learning.entries.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn exhausted_epoch_and_insertion_identifiers_fail_closed() {
|
|
let now = Instant::now();
|
|
let mut learning = CarrierLearning::new(3);
|
|
learning.epoch = Some(u64::MAX);
|
|
assert_eq!(
|
|
learning.apply_policy(
|
|
now,
|
|
true,
|
|
WebCarrierNegotiationAggressiveness::Aggressive,
|
|
Duration::from_secs(10),
|
|
),
|
|
None
|
|
);
|
|
learning.record_chain(now, u64::MAX, context(4), &[], WebCarrier::Https);
|
|
assert!(learning.entries.is_empty());
|
|
|
|
learning.epoch = Some(1);
|
|
learning.insertion_sequence = u64::MAX;
|
|
learning.record_chain(now, 1, context(4), &[], WebCarrier::Https);
|
|
assert!(learning.entries.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn client_reported_failures_do_not_create_negative_evidence() {
|
|
let now = Instant::now();
|
|
let mut learning = CarrierLearning::new(3);
|
|
let epoch = learning
|
|
.apply_policy(
|
|
now,
|
|
true,
|
|
WebCarrierNegotiationAggressiveness::Aggressive,
|
|
Duration::from_secs(10),
|
|
)
|
|
.unwrap();
|
|
learning.record_chain(
|
|
now,
|
|
epoch,
|
|
context(5),
|
|
&[WebCarrier::Websocket],
|
|
WebCarrier::Https,
|
|
);
|
|
let (_, scores) = learning.rank(
|
|
now,
|
|
&[WebCarrier::Websocket, WebCarrier::HttpsLanes],
|
|
request(5),
|
|
[1; 32],
|
|
context(5).client_ip,
|
|
true,
|
|
);
|
|
assert_eq!(scores[WebCarrier::Websocket.index()], 0);
|
|
}
|
|
|
|
#[test]
|
|
fn old_new_old_policy_rejects_both_stale_epochs() {
|
|
let now = Instant::now();
|
|
let mut learning = CarrierLearning::new(3);
|
|
let old = learning
|
|
.apply_policy(
|
|
now,
|
|
true,
|
|
WebCarrierNegotiationAggressiveness::Aggressive,
|
|
Duration::from_secs(10),
|
|
)
|
|
.unwrap();
|
|
let middle = learning
|
|
.apply_policy(
|
|
now,
|
|
true,
|
|
WebCarrierNegotiationAggressiveness::Balanced,
|
|
Duration::from_secs(10),
|
|
)
|
|
.unwrap();
|
|
let current = learning
|
|
.apply_policy(
|
|
now,
|
|
true,
|
|
WebCarrierNegotiationAggressiveness::Aggressive,
|
|
Duration::from_secs(10),
|
|
)
|
|
.unwrap();
|
|
assert_ne!(old, middle);
|
|
assert_ne!(middle, current);
|
|
assert_ne!(old, current);
|
|
learning.record_chain(now, old, context(6), &[], WebCarrier::Https);
|
|
learning.record_chain(now, middle, context(6), &[], WebCarrier::Https);
|
|
assert!(learning.entries.is_empty());
|
|
learning.record_chain(now, current, context(6), &[], WebCarrier::Https);
|
|
assert_eq!(learning.entries.len(), 3);
|
|
}
|
|
|
|
#[test]
|
|
fn fifo_metadata_stays_within_the_entry_capacity() {
|
|
let now = Instant::now();
|
|
let mut learning = CarrierLearning::new(3);
|
|
let epoch = learning
|
|
.apply_policy(
|
|
now,
|
|
true,
|
|
WebCarrierNegotiationAggressiveness::Aggressive,
|
|
Duration::from_secs(10),
|
|
)
|
|
.unwrap();
|
|
for hash in 1..=32 {
|
|
learning.record_chain(now, epoch, context(hash), &[], WebCarrier::Https);
|
|
assert!(learning.entries.len() <= 3);
|
|
assert!(learning.insertion_order.len() <= 3);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn explicit_reset_preserves_policy_and_rejects_old_epoch_outcomes() {
|
|
let generation = crate::maestro::generation::test_runtime_generation(
|
|
1,
|
|
crate::config::ProxyConfig::default(),
|
|
);
|
|
let runtime = crate::web::manager::WebProcessRuntime::start(std::sync::Arc::new(
|
|
arc_swap::ArcSwap::from(generation.clone()),
|
|
));
|
|
let now = Instant::now();
|
|
let old_epoch = {
|
|
let mut learning = runtime.learning.lock();
|
|
let epoch = learning
|
|
.apply_policy(
|
|
now,
|
|
true,
|
|
WebCarrierNegotiationAggressiveness::Aggressive,
|
|
Duration::from_secs(10),
|
|
)
|
|
.unwrap();
|
|
learning.record_chain(now, epoch, context(7), &[], WebCarrier::Websocket);
|
|
epoch
|
|
};
|
|
|
|
let outcome = runtime.reset_carrier_learning().unwrap();
|
|
{
|
|
let mut learning = runtime.learning.lock();
|
|
learning.record_chain(
|
|
Instant::now(),
|
|
old_epoch,
|
|
context(7),
|
|
&[],
|
|
WebCarrier::Https,
|
|
);
|
|
let status = learning.status(Instant::now());
|
|
assert!(status.enabled);
|
|
assert_eq!(
|
|
status.aggressiveness,
|
|
WebCarrierNegotiationAggressiveness::Aggressive
|
|
);
|
|
assert_eq!(status.entries, 0);
|
|
assert_eq!(status.epoch, Some(outcome.epoch));
|
|
}
|
|
|
|
runtime.shutdown().await;
|
|
generation.stop_sessions().await;
|
|
generation.stop_background_tasks().await;
|
|
}
|