Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 80 additions & 2 deletions home-mixer/candidate_hydrators/in_network_candidate_hydrator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ pub struct InNetworkCandidateHydrator;

#[async_trait]
impl Hydrator<ScoredPostsQuery, PostCandidate> for InNetworkCandidateHydrator {
fn enable(&self, query: &ScoredPostsQuery) -> bool {
!query.has_cached_posts
// Always on. FollowedUserIdsQueryHydrator is request-fresh and this is a
// local HashSet lookup. Skipping cache hits leaves Redis in_network (TTL
// 180s) in place; VF then buckets TimelineHome vs Recs from that stamp.
fn enable(&self, _query: &ScoredPostsQuery) -> bool {
true
}

async fn hydrate(
Expand Down Expand Up @@ -43,3 +46,78 @@ impl Hydrator<ScoredPostsQuery, PostCandidate> for InNetworkCandidateHydrator {
candidate.in_network = hydrated.in_network;
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::models::user_features::UserFeatures;

fn query(viewer_id: u64, followed: Vec<i64>, has_cached_posts: bool) -> ScoredPostsQuery {
ScoredPostsQuery {
user_id: viewer_id,
user_features: UserFeatures {
followed_user_ids: followed,
..Default::default()
},
has_cached_posts,
..Default::default()
}
}

async fn stamp(
query: &ScoredPostsQuery,
author_id: u64,
cached_in_network: Option<bool>,
) -> Option<bool> {
let hydrator = InNetworkCandidateHydrator;
let candidates = vec![PostCandidate {
tweet_id: 1,
author_id,
in_network: cached_in_network,
..Default::default()
}];
let hydrated = hydrator.hydrate(query, &candidates).await;
let mut candidate = candidates.into_iter().next().unwrap();
hydrator.update(&mut candidate, hydrated.into_iter().next().unwrap().unwrap());
candidate.in_network
}

#[test]
fn enable_stays_on_for_cached_posts() {
let hydrator = InNetworkCandidateHydrator;
assert!(hydrator.enable(&query(1, vec![42], true)));
assert!(hydrator.enable(&query(1, vec![42], false)));
}

#[tokio::test]
async fn restamps_stale_cached_in_network_after_unfollow() {
let query = query(1, vec![], true);
assert_eq!(
stamp(&query, 42, Some(true)).await,
Some(false),
"cached Home stamp after unfollow must become OON"
);
}

#[tokio::test]
async fn restamps_stale_cached_oon_after_follow() {
let query = query(1, vec![42], true);
assert_eq!(
stamp(&query, 42, Some(false)).await,
Some(true),
"cached Recs stamp after follow must become Home"
);
}

#[tokio::test]
async fn followed_author_is_in_network() {
let query = query(1, vec![42], false);
assert_eq!(stamp(&query, 42, None).await, Some(true));
}

#[tokio::test]
async fn unfollowed_author_is_oon() {
let query = query(1, vec![42], false);
assert_eq!(stamp(&query, 99, None).await, Some(false));
}
}