Skip to content

Commit 21da78d

Browse files
committed
Restructure repository and PR mocking
1 parent 12c79f7 commit 21da78d

File tree

4 files changed

+103
-95
lines changed

4 files changed

+103
-95
lines changed

src/tests/mocks/github.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,51 @@
11
use octocrab::Octocrab;
2+
use std::collections::HashMap;
23
use wiremock::MockServer;
34

45
use crate::create_github_client;
6+
use crate::github::GithubRepoName;
57
use crate::tests::mocks::app::{default_app_id, AppHandler};
6-
use crate::tests::mocks::repository::RepositoriesHandler;
8+
use crate::tests::mocks::comment::Comment;
9+
use crate::tests::mocks::repository::{mock_repo, mock_repo_list};
710
use crate::tests::mocks::World;
811

12+
/// Represents the state of a simulated GH repo.
13+
pub struct GitHubRepoState {
14+
// We store comments from all PRs inside a single queue, because
15+
// we don't necessarily know beforehand which PRs will receive comments.
16+
comments_queue: tokio::sync::mpsc::Receiver<Comment>,
17+
// We need a local queue to avoid skipping comments received out of order.
18+
pending_comments: Vec<Comment>,
19+
}
20+
921
pub struct GitHubMockServer {
1022
mock_server: MockServer,
23+
repos: HashMap<GithubRepoName, GitHubRepoState>,
1124
}
1225

1326
impl GitHubMockServer {
1427
pub async fn start(world: &World) -> Self {
1528
let mock_server = MockServer::start().await;
16-
RepositoriesHandler.mount(world, &mock_server).await;
29+
mock_repo_list(world, &mock_server).await;
30+
31+
// Repositories are mocked separately to make it easier to
32+
// pass comm. channels to them.
33+
let mut repos = HashMap::default();
34+
for (name, repo) in &world.repos {
35+
let (comments_tx, comments_rx) = tokio::sync::mpsc::channel(1024);
36+
mock_repo(repo, comments_tx, &mock_server).await;
37+
repos.insert(
38+
name.clone(),
39+
GitHubRepoState {
40+
comments_queue: comments_rx,
41+
pending_comments: vec![],
42+
},
43+
);
44+
}
45+
1746
AppHandler::default().mount(&mock_server).await;
18-
Self { mock_server }
47+
48+
Self { mock_server, repos }
1949
}
2050

2151
pub fn client(&self) -> Octocrab {

src/tests/mocks/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use crate::tests::mocks::permissions::TeamApiMockServer;
99
use crate::TeamApiClient;
1010

1111
pub use bors::run_test;
12-
pub use bors::BorsBuilder;
1312
pub use repository::Repo;
1413
pub use user::User;
1514

src/tests/mocks/pull_request.rs

Lines changed: 26 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
use std::{
2-
collections::HashMap,
3-
sync::{Arc, Mutex},
4-
};
5-
61
use serde::{Deserialize, Serialize};
72
use wiremock::{
83
matchers::{method, path},
@@ -14,44 +9,26 @@ use super::{
149
Repo,
1510
};
1611

17-
/// Handles all repositories related requests
18-
/// Only handle one PR for now, since bors don't create PRs itself
19-
#[derive(Default)]
20-
pub(super) struct PullRequestsHandler {
21-
repo: Repo,
22-
number: u64,
23-
comments: Arc<Mutex<HashMap<u64, Vec<Comment>>>>,
24-
}
25-
26-
impl PullRequestsHandler {
27-
pub(super) fn new(repo: Repo) -> Self {
28-
PullRequestsHandler {
29-
repo,
30-
number: default_pr_number(),
31-
comments: Arc::new(Mutex::new(HashMap::new())),
32-
}
33-
}
34-
pub(super) async fn mount(&self, mock_server: &MockServer) {
12+
pub async fn mock_pull_requests(repo: &Repo, mock_server: &MockServer) {
13+
for pr_number in &repo.known_prs {
3514
Mock::given(method("GET"))
36-
.and(path(format!(
37-
"/repos/{}/pulls/{}",
38-
self.repo.name, self.number
39-
)))
40-
.respond_with(ResponseTemplate::new(200).set_body_json(GitHubPullRequest::default()))
15+
.and(path(format!("/repos/{}/pulls/{}", repo.name, pr_number)))
16+
.respond_with(
17+
ResponseTemplate::new(200).set_body_json(GitHubPullRequest::new(*pr_number)),
18+
)
4119
.mount(mock_server)
4220
.await;
4321

44-
let comments = self.comments.clone();
4522
Mock::given(method("POST"))
4623
.and(path(format!(
4724
"/repos/{}/issues/{}/comments",
48-
self.repo.name, self.number
25+
repo.name, pr_number
4926
)))
5027
.respond_with(move |req: &Request| {
5128
let comment_payload: CommentCreatePayload = req.body_json().unwrap();
5229
let comment: Comment = comment_payload.into();
53-
let mut comments = comments.lock().unwrap();
54-
comments.entry(1).or_default().push(comment.clone());
30+
// let mut comments = comments.lock().unwrap();
31+
// comments.entry(1).or_default().push(comment.clone());
5532
ResponseTemplate::new(201).set_body_json(GitHubComment::from(comment))
5633
})
5734
.mount(mock_server)
@@ -74,27 +51,33 @@ struct GitHubPullRequest {
7451
base: Box<Base>,
7552
}
7653

77-
impl Default for GitHubPullRequest {
78-
fn default() -> Self {
54+
impl GitHubPullRequest {
55+
fn new(number: u64) -> Self {
7956
GitHubPullRequest {
8057
url: "https://test.com".to_string(),
81-
id: 1,
82-
title: format!("PR #{}", default_pr_number()),
83-
body: "test".to_string(),
84-
number: default_pr_number(),
58+
id: number + 1000,
59+
title: format!("PR #{number}"),
60+
body: format!("Description of PR #{number}"),
61+
number,
8562
head: Box::new(Head {
86-
label: "test".to_string(),
87-
ref_field: "test".to_string(),
88-
sha: "test".to_string(),
63+
label: format!("pr-{number}"),
64+
ref_field: format!("pr-{number}"),
65+
sha: format!("pr-{number}-sha"),
8966
}),
9067
base: Box::new(Base {
91-
ref_field: "test".to_string(),
92-
sha: "test".to_string(),
68+
ref_field: "main".to_string(),
69+
sha: "main-sha".to_string(),
9370
}),
9471
}
9572
}
9673
}
9774

75+
impl Default for GitHubPullRequest {
76+
fn default() -> Self {
77+
Self::new(default_pr_number())
78+
}
79+
}
80+
9881
#[derive(Serialize)]
9982
struct Head {
10083
label: String,

src/tests/mocks/repository.rs

Lines changed: 44 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,18 @@ use crate::tests::event::default_pr_number;
44
use base64::Engine;
55
use serde::Serialize;
66
use std::collections::HashMap;
7+
use tokio::sync::mpsc::Sender;
78
use url::Url;
89
use wiremock::{
910
matchers::{method, path},
1011
Mock, MockServer, ResponseTemplate,
1112
};
1213

14+
use crate::tests::mocks::comment::Comment;
15+
use crate::tests::mocks::pull_request::mock_pull_requests;
1316
use crate::tests::mocks::{Permissions, World};
1417

15-
use super::{
16-
pull_request::PullRequestsHandler,
17-
user::{GitHubUser, User},
18-
};
18+
use super::user::{GitHubUser, User};
1919

2020
#[derive(Clone)]
2121
pub struct Repo {
@@ -68,50 +68,46 @@ fn default_repo_name() -> GithubRepoName {
6868
GithubRepoName::new("rust-lang", "borstest")
6969
}
7070

71-
/// Handles all repositories related requests
72-
#[derive(Default)]
73-
pub struct RepositoriesHandler;
74-
75-
impl RepositoriesHandler {
76-
pub async fn mount(&self, world: &World, mock_server: &MockServer) {
77-
let repos = GitHubRepositories {
78-
total_count: world.repos.len() as u64,
79-
repositories: world
80-
.repos
81-
.iter()
82-
.enumerate()
83-
.map(|(index, (_, repo))| GitHubRepository {
84-
id: index as u64,
85-
owner: User::new(index as u64, repo.name.owner()).into(),
86-
name: repo.name.name().to_string(),
87-
url: format!("https://{}.foo", repo.name.name()).parse().unwrap(),
88-
})
89-
.collect(),
90-
};
91-
92-
Mock::given(method("GET"))
93-
.and(path("/installation/repositories"))
94-
.respond_with(ResponseTemplate::new(200).set_body_json(repos))
95-
.mount(mock_server)
96-
.await;
97-
98-
for repo in world.repos.values() {
99-
PullRequestsHandler::new(repo.to_owned())
100-
.mount(mock_server)
101-
.await;
102-
Mock::given(method("GET"))
103-
.and(path(format!(
104-
"/repos/{}/contents/rust-bors.toml",
105-
repo.name
106-
)))
107-
.respond_with(
108-
ResponseTemplate::new(200)
109-
.set_body_json(GitHubContent::new("rust-bors.toml", &repo.config)),
110-
)
111-
.mount(mock_server)
112-
.await;
113-
}
114-
}
71+
pub async fn mock_repo_list(world: &World, mock_server: &MockServer) {
72+
let repos = GitHubRepositories {
73+
total_count: world.repos.len() as u64,
74+
repositories: world
75+
.repos
76+
.iter()
77+
.enumerate()
78+
.map(|(index, (_, repo))| GitHubRepository {
79+
id: index as u64,
80+
owner: User::new(index as u64, repo.name.owner()).into(),
81+
name: repo.name.name().to_string(),
82+
url: format!("https://{}.foo", repo.name.name()).parse().unwrap(),
83+
})
84+
.collect(),
85+
};
86+
87+
Mock::given(method("GET"))
88+
.and(path("/installation/repositories"))
89+
.respond_with(ResponseTemplate::new(200).set_body_json(repos))
90+
.mount(mock_server)
91+
.await;
92+
}
93+
94+
pub async fn mock_repo(repo: &Repo, comments_tx: Sender<Comment>, mock_server: &MockServer) {
95+
mock_pull_requests(repo, mock_server).await;
96+
mock_config(repo, mock_server).await;
97+
}
98+
99+
async fn mock_config(repo: &Repo, mock_server: &MockServer) {
100+
Mock::given(method("GET"))
101+
.and(path(format!(
102+
"/repos/{}/contents/rust-bors.toml",
103+
repo.name
104+
)))
105+
.respond_with(
106+
ResponseTemplate::new(200)
107+
.set_body_json(GitHubContent::new("rust-bors.toml", &repo.config)),
108+
)
109+
.mount(mock_server)
110+
.await;
115111
}
116112

117113
/// Represents all repositories for an installation

0 commit comments

Comments
 (0)