Skip to content

Commit 16a6647

Browse files
committed
Add tests
1 parent 5519a72 commit 16a6647

File tree

7 files changed

+189
-38
lines changed

7 files changed

+189
-38
lines changed

src/bors/comment.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,7 @@ pub fn build_failed_comment(
115115

116116
let mut failed_jobs: Vec<Job> = failed_workflows
117117
.into_iter()
118-
.map(|w| w.failed_jobs)
119-
.flatten()
118+
.flat_map(|w| w.failed_jobs)
120119
.collect();
121120
failed_jobs.sort_by(|l, r| l.name.cmp(&r.name));
122121

src/bors/handlers/trybuild.rs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,9 +417,10 @@ mod tests {
417417
use crate::github::CommitSha;
418418
use crate::tests::BorsTester;
419419
use crate::tests::mocks::{
420-
BorsBuilder, Comment, GitHubState, User, WorkflowEvent, WorkflowRunData, default_pr_number,
421-
default_repo_name, run_test,
420+
BorsBuilder, Comment, GitHubState, User, WorkflowEvent, WorkflowJob, WorkflowRunData,
421+
default_pr_number, default_repo_name, run_test,
422422
};
423+
use octocrab::models::JobId;
423424
use octocrab::params::checks::{CheckRunConclusion, CheckRunStatus};
424425

425426
#[sqlx::test]
@@ -457,6 +458,42 @@ mod tests {
457458
.await;
458459
}
459460

461+
#[sqlx::test]
462+
async fn try_failure_job(pool: sqlx::PgPool) {
463+
run_test(pool, async |tester: &mut BorsTester| {
464+
tester.post_comment("@bors try").await?;
465+
tester.expect_comments(1).await;
466+
467+
let mut workflow = WorkflowRunData::from(tester.try_branch());
468+
workflow.jobs.push(WorkflowJob {
469+
id: JobId(42),
470+
status: WorkflowStatus::Failure,
471+
});
472+
workflow.jobs.push(WorkflowJob {
473+
id: JobId(50),
474+
status: WorkflowStatus::Failure,
475+
});
476+
workflow.jobs.push(WorkflowJob {
477+
id: JobId(51),
478+
status: WorkflowStatus::Success,
479+
});
480+
481+
tester.default_repo().lock().update_workflow_run(workflow.clone(), WorkflowStatus::Pending);
482+
tester.workflow_full_failure(workflow).await?;
483+
insta::assert_snapshot!(
484+
tester.get_comment().await?,
485+
@r"
486+
:broken_heart: Test failed ([Workflow1](https://github.com/workflows/Workflow1/1)). Failed jobs:
487+
488+
- `Job 42` ([web logs](https://github.com/job-logs/42), [extended logs](https://triage.rust-lang.org/gha-logs/rust-lang/borstest/42))
489+
- `Job 50` ([web logs](https://github.com/job-logs/50), [extended logs](https://triage.rust-lang.org/gha-logs/rust-lang/borstest/50))
490+
"
491+
);
492+
Ok(())
493+
})
494+
.await;
495+
}
496+
460497
#[sqlx::test]
461498
async fn try_no_permissions(pool: sqlx::PgPool) {
462499
run_test(pool, async |tester| {

src/bors/handlers/workflow.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ async fn maybe_complete_build(
219219
// Download failed jobs
220220
let mut workflow_runs: Vec<FailedWorkflowRun> = vec![];
221221
for workflow_run in db_workflow_runs {
222-
let failed_jobs = match get_failed_jobs(&repo, &workflow_run).await {
222+
let failed_jobs = match get_failed_jobs(repo, &workflow_run).await {
223223
Ok(jobs) => jobs,
224224
Err(error) => {
225225
tracing::error!(

src/github/api/client.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,7 @@ impl GithubRepositoryClient {
286286

287287
// Cancel all workflows in parallel
288288
futures::future::join_all(run_ids.iter().map(|run_id| {
289-
actions.cancel_workflow_run(
290-
self.repo_name.owner(),
291-
self.repo_name.name(),
292-
(*run_id).into(),
293-
)
289+
actions.cancel_workflow_run(self.repo_name.owner(), self.repo_name.name(), *run_id)
294290
}))
295291
.await
296292
.into_iter()

src/tests/mocks/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub use repository::default_branch_name;
2525
pub use repository::default_repo_name;
2626
pub use user::User;
2727
pub use workflow::WorkflowEvent;
28+
pub use workflow::WorkflowJob;
2829
pub use workflow::WorkflowRunData;
2930

3031
mod app;

src/tests/mocks/repository.rs

Lines changed: 133 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
use std::sync::Arc;
22
use std::{collections::HashMap, time::SystemTime};
33

4+
use super::user::{GitHubUser, User};
45
use crate::bors::PullRequestStatus;
5-
use base64::Engine;
6-
use chrono::{DateTime, Utc};
7-
use octocrab::models::CheckSuiteId;
8-
use octocrab::models::pulls::MergeableState;
9-
use octocrab::models::repos::Object;
10-
use octocrab::models::repos::Object::Commit;
11-
126
use crate::database::WorkflowStatus;
137
use crate::github::{GithubRepoName, PullRequestNumber};
148
use crate::permissions::PermissionType;
159
use crate::tests::mocks::comment::Comment;
1610
use crate::tests::mocks::permissions::Permissions;
1711
use crate::tests::mocks::pull_request::mock_pull_requests;
12+
use crate::tests::mocks::workflow::WorkflowJob;
1813
use crate::tests::mocks::{GitHubState, WorkflowRunData, default_pr_number, dynamic_mock_req};
14+
use base64::Engine;
15+
use chrono::{DateTime, Utc};
16+
use octocrab::models::pulls::MergeableState;
17+
use octocrab::models::repos::Object;
18+
use octocrab::models::repos::Object::Commit;
19+
use octocrab::models::workflows::{Conclusion, Status, Step};
20+
use octocrab::models::{CheckSuiteId, JobId, RunId};
1921
use parking_lot::Mutex;
2022
use serde::Serialize;
2123
use tokio::sync::mpsc::Sender;
@@ -25,8 +27,6 @@ use wiremock::{
2527
matchers::{method, path},
2628
};
2729

28-
use super::user::{GitHubUser, User};
29-
3030
#[derive(Clone, Debug)]
3131
pub struct CheckRunData {
3232
pub name: String,
@@ -386,6 +386,7 @@ pub async fn mock_repo(
386386
mock_cancel_workflow(repo.clone(), mock_server).await;
387387
mock_check_runs(repo.clone(), mock_server).await;
388388
mock_workflow_runs(repo.clone(), mock_server).await;
389+
mock_workflow_jobs(repo.clone(), mock_server).await;
389390
mock_config(repo, mock_server).await;
390391
}
391392

@@ -580,8 +581,8 @@ async fn mock_workflow_runs(repo: Arc<Mutex<Repo>>, mock_server: &MockServer) {
580581
#[derive(serde::Serialize, Debug)]
581582
struct WorkflowRunResponse {
582583
id: octocrab::models::RunId,
583-
status: String,
584-
conclusion: Option<String>,
584+
status: Status,
585+
conclusion: Option<Conclusion>,
585586
}
586587

587588
#[derive(serde::Serialize, Debug)]
@@ -607,25 +608,64 @@ async fn mock_workflow_runs(repo: Arc<Mutex<Repo>>, mock_server: &MockServer) {
607608
let response = WorkflowRunsResponse {
608609
workflow_runs: workflow_runs
609610
.into_iter()
610-
.map(|run| WorkflowRunResponse {
611-
id: run.workflow_run.run_id,
612-
status: match run.status {
613-
WorkflowStatus::Pending => "pending",
614-
WorkflowStatus::Success | WorkflowStatus::Failure => "completed",
611+
.map(|run| {
612+
let (status, conclusion) = status_to_gh(run.status);
613+
WorkflowRunResponse {
614+
id: run.workflow_run.run_id,
615+
status,
616+
conclusion,
615617
}
616-
.to_string(),
617-
conclusion: match run.status {
618-
WorkflowStatus::Success => Some("success".to_string()),
619-
WorkflowStatus::Failure => Some("failure".to_string()),
620-
_ => None,
621-
},
622618
})
623619
.collect(),
624620
};
625621
ResponseTemplate::new(200).set_body_json(response)
626622
},
627623
"GET",
628-
format!("^/repos/{repo_name}/actions/runs"),
624+
format!("^/repos/{repo_name}/actions/runs$"),
625+
)
626+
.mount(mock_server)
627+
.await;
628+
}
629+
630+
/// Returns (status, conclusion).
631+
fn status_to_gh(status: WorkflowStatus) -> (Status, Option<Conclusion>) {
632+
let conclusion = match status {
633+
WorkflowStatus::Success => Some(Conclusion::Success),
634+
WorkflowStatus::Failure => Some(Conclusion::Failure),
635+
_ => None,
636+
};
637+
let status = match status {
638+
WorkflowStatus::Pending => Status::Pending,
639+
WorkflowStatus::Success | WorkflowStatus::Failure => Status::Completed,
640+
};
641+
(status, conclusion)
642+
}
643+
644+
async fn mock_workflow_jobs(repo: Arc<Mutex<Repo>>, mock_server: &MockServer) {
645+
let repo_name = repo.lock().name.clone();
646+
dynamic_mock_req(
647+
move |_req: &Request, [run_id]: [&str; 1]| {
648+
let repo = repo.lock();
649+
let run_id: RunId = run_id.parse::<u64>().expect("Non-integer run id").into();
650+
let workflow_run = repo
651+
.workflow_runs
652+
.iter()
653+
.find(|w| w.workflow_run.run_id == run_id)
654+
.unwrap_or_else(|| panic!("Workflow run with ID {run_id} not found"));
655+
656+
let response = GitHubWorkflowJobs {
657+
total_count: workflow_run.workflow_run.jobs.len() as u64,
658+
jobs: workflow_run
659+
.workflow_run
660+
.jobs
661+
.iter()
662+
.map(|job| workflow_job_to_gh(job, run_id))
663+
.collect(),
664+
};
665+
ResponseTemplate::new(200).set_body_json(response)
666+
},
667+
"GET",
668+
format!("^/repos/{repo_name}/actions/runs/(.*)/jobs"),
629669
)
630670
.mount(mock_server)
631671
.await;
@@ -923,3 +963,73 @@ struct GitHubRef {
923963
url: Url,
924964
object: Object,
925965
}
966+
967+
#[derive(Serialize)]
968+
struct GitHubWorkflowJobs {
969+
total_count: u64,
970+
jobs: Vec<GitHubWorkflowJob>,
971+
}
972+
973+
#[derive(Serialize)]
974+
struct GitHubWorkflowJob {
975+
id: JobId,
976+
run_id: RunId,
977+
workflow_name: String,
978+
head_branch: String,
979+
run_url: Url,
980+
run_attempt: u32,
981+
982+
node_id: String,
983+
head_sha: String,
984+
url: Url,
985+
html_url: Url,
986+
status: Status,
987+
#[serde(skip_serializing_if = "Option::is_none")]
988+
conclusion: Option<Conclusion>,
989+
created_at: DateTime<Utc>,
990+
started_at: DateTime<Utc>,
991+
#[serde(skip_serializing_if = "Option::is_none")]
992+
completed_at: Option<DateTime<Utc>>,
993+
name: String,
994+
steps: Vec<Step>,
995+
check_run_url: String,
996+
labels: Vec<String>,
997+
#[serde(skip_serializing_if = "Option::is_none")]
998+
runner_id: Option<u32>,
999+
#[serde(skip_serializing_if = "Option::is_none")]
1000+
runner_name: Option<String>,
1001+
#[serde(skip_serializing_if = "Option::is_none")]
1002+
runner_group_id: Option<u32>,
1003+
#[serde(skip_serializing_if = "Option::is_none")]
1004+
runner_group_name: Option<String>,
1005+
}
1006+
1007+
fn workflow_job_to_gh(job: &WorkflowJob, run_id: RunId) -> GitHubWorkflowJob {
1008+
let WorkflowJob { id, status } = job;
1009+
let (status, conclusion) = status_to_gh(*status);
1010+
GitHubWorkflowJob {
1011+
id: *id,
1012+
run_id,
1013+
workflow_name: "".to_string(),
1014+
head_branch: "".to_string(),
1015+
run_url: "https://test.com".parse().unwrap(),
1016+
run_attempt: 0,
1017+
node_id: "".to_string(),
1018+
head_sha: "".to_string(),
1019+
url: "https://test.com".parse().unwrap(),
1020+
html_url: format!("https://github.com/job-logs/{id}").parse().unwrap(),
1021+
status,
1022+
conclusion,
1023+
created_at: Default::default(),
1024+
started_at: Default::default(),
1025+
completed_at: None,
1026+
name: format!("Job {id}"),
1027+
steps: vec![],
1028+
check_run_url: "".to_string(),
1029+
labels: vec![],
1030+
runner_id: None,
1031+
runner_name: None,
1032+
runner_group_id: None,
1033+
runner_group_name: None,
1034+
}
1035+
}

src/tests/mocks/workflow.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use chrono::{DateTime, Utc};
2-
use octocrab::models::{CheckSuiteId, RunId, WorkflowId};
3-
use serde::Serialize;
4-
use url::Url;
5-
1+
use crate::database::WorkflowStatus;
62
use crate::github::GithubRepoName;
73
use crate::tests::mocks::default_repo_name;
84
use crate::tests::mocks::repository::{Branch, GitHubRepository};
5+
use chrono::{DateTime, Utc};
6+
use octocrab::models::{CheckSuiteId, JobId, RunId, WorkflowId};
7+
use serde::Serialize;
8+
use url::Url;
99

1010
#[derive(Clone)]
1111
pub struct WorkflowEvent {
@@ -44,13 +44,20 @@ pub enum WorkflowEventKind {
4444
Completed { status: String },
4545
}
4646

47+
#[derive(Clone)]
48+
pub struct WorkflowJob {
49+
pub id: JobId,
50+
pub status: WorkflowStatus,
51+
}
52+
4753
#[derive(Clone)]
4854
pub struct WorkflowRunData {
4955
pub repository: GithubRepoName,
5056
name: String,
5157
pub run_id: RunId,
5258
pub check_suite_id: CheckSuiteId,
5359
pub head_branch: String,
60+
pub jobs: Vec<WorkflowJob>,
5461
head_sha: String,
5562
}
5663

@@ -62,6 +69,7 @@ impl WorkflowRunData {
6269
run_id: RunId(1),
6370
check_suite_id: CheckSuiteId(1),
6471
head_branch: branch.get_name().to_string(),
72+
jobs: vec![],
6573
head_sha: branch.get_sha().to_string(),
6674
}
6775
}

0 commit comments

Comments
 (0)