diff --git a/src/api/commits.rs b/src/api/commits.rs index c4321173..1d233c08 100644 --- a/src/api/commits.rs +++ b/src/api/commits.rs @@ -1,4 +1,5 @@ //! The commit API. +mod associated_check_runs; mod associated_pull_requests; mod compare_commit; mod create_comment; @@ -6,6 +7,7 @@ mod create_comment; pub use associated_pull_requests::PullRequestTarget; pub use self::create_comment::CreateCommentBuilder; +use crate::params::repos::Reference; use crate::{models, Octocrab, Result}; pub struct CommitHandler<'octo> { @@ -31,6 +33,13 @@ impl<'octo> CommitHandler<'octo> { compare_commit::CompareCommitsBuilder::new(self, base.into(), head.into()) } + pub fn associated_check_runs( + &self, + reference: impl Into, + ) -> associated_check_runs::AssociatedCheckRunsBuilder<'_, '_> { + associated_check_runs::AssociatedCheckRunsBuilder::new(self, reference) + } + pub fn associated_pull_requests( &self, target: PullRequestTarget, diff --git a/src/api/commits/associated_check_runs.rs b/src/api/commits/associated_check_runs.rs new file mode 100644 index 00000000..60c3d117 --- /dev/null +++ b/src/api/commits/associated_check_runs.rs @@ -0,0 +1,51 @@ +use crate::commits::CommitHandler; +use crate::models::checks::ListCheckRuns; +use crate::params::repos::Reference; +use crate::Result; + +#[derive(serde::Serialize)] +pub struct AssociatedCheckRunsBuilder<'octo, 'r> { + #[serde(skip)] + handler: &'r CommitHandler<'octo>, + #[serde(skip)] + reference: Reference, + #[serde(skip_serializing_if = "Option::is_none")] + per_page: Option, + #[serde(skip_serializing_if = "Option::is_none")] + page: Option, +} + +impl<'octo, 'r> AssociatedCheckRunsBuilder<'octo, 'r> { + pub(crate) fn new(handler: &'r CommitHandler<'octo>, reference: impl Into) -> Self { + Self { + handler, + reference: reference.into(), + per_page: None, + page: None, + } + } + + /// Results per page (max 100). + pub fn per_page(mut self, per_page: impl Into) -> Self { + self.per_page = Some(per_page.into()); + self + } + + /// Page number of the results to fetch. + pub fn page(mut self, page: impl Into) -> Self { + self.page = Some(page.into()); + self + } + + /// Send the actual request. + pub async fn send(self) -> Result { + let route = format!( + "/repos/{owner}/{repo}/commits/{reference}/check-runs", + owner = self.handler.owner, + repo = self.handler.repo, + reference = self.reference.full_ref_url() + ); + + self.handler.crab.get(route, Some(&self)).await + } +} diff --git a/tests/commit_associated_check_runs_tests.rs b/tests/commit_associated_check_runs_tests.rs new file mode 100644 index 00000000..2ff6aff3 --- /dev/null +++ b/tests/commit_associated_check_runs_tests.rs @@ -0,0 +1,102 @@ +/// Tests API calls related to check runs of a specific commit. +mod mock_error; + +use mock_error::setup_error_handler; +use octocrab::models::checks::ListCheckRuns; +use octocrab::models::CheckRunId; +use octocrab::params::repos::Reference; +use octocrab::{Error, Octocrab}; +use serde_json::{json, Value}; +use wiremock::{ + matchers::{method, path}, + Mock, MockServer, ResponseTemplate, +}; + +async fn setup_api(template: ResponseTemplate) -> MockServer { + let mock_server = MockServer::start().await; + + let mocked_path = "/repos/some-owner/some-repo/commits/refs/heads/some-branch/check-runs"; + + Mock::given(method("GET")) + .and(path(mocked_path)) + .respond_with(template) + .mount(&mock_server) + .await; + setup_error_handler( + &mock_server, + &format!("GET on {mocked_path} was not received"), + ) + .await; + mock_server +} + +fn setup_octocrab(uri: &str) -> Octocrab { + Octocrab::builder().base_uri(uri).unwrap().build().unwrap() +} + +#[tokio::test] +async fn should_return_page_with_check_runs() { + let mocked_response: ListCheckRuns = + serde_json::from_str(include_str!("resources/commit_check_runs.json")).unwrap(); + let template = ResponseTemplate::new(200).set_body_json(&mocked_response); + let mock_server = setup_api(template).await; + let client = setup_octocrab(&mock_server.uri()); + let result = client + .commits("some-owner", "some-repo") + .associated_check_runs(Reference::Branch("some-branch".into())) + .send() + .await; + + assert!( + result.is_ok(), + "expected successful result, got error: {:#?}", + result + ); + + let response = result.unwrap(); + assert_eq!(response.total_count, 2); + + let items = response.check_runs; + assert_eq!(items.len(), 2); + + { + let item = &items[0]; + + assert_eq!(CheckRunId(16354767716), item.id); + assert_eq!("Cargo test on nix (ubuntu-20.04, stable)", item.name); + assert_eq!(Some("success".into()), item.conclusion); + } + + { + let item = &items[1]; + + assert_eq!(CheckRunId(16354767496), item.id); + assert_eq!("Cargo test on nix (ubuntu-20.04, 1.68)", item.name); + assert_eq!(Some("success".into()), item.conclusion); + } +} + +#[tokio::test] +async fn should_fail_when_not_found() { + let mocked_response = json!({ + "documentation_url": json!("rtm"), + "errors": Value::Null, + "message": json!("Its gone") + }); + + let template = ResponseTemplate::new(404).set_body_json(&mocked_response); + let mock_server = setup_api(template).await; + let client = setup_octocrab(&mock_server.uri()); + let result = client + .commits("some-owner", "some-repo") + .associated_check_runs(Reference::Branch("some-branch".into())) + .send() + .await; + + match result.unwrap_err() { + Error::GitHub { source, .. } => { + assert_eq!("Its gone", source.message) + } + other => panic!("Unexpected error: {:?}", other), + } +} diff --git a/tests/resources/commit_check_runs.json b/tests/resources/commit_check_runs.json new file mode 100644 index 00000000..68dfd539 --- /dev/null +++ b/tests/resources/commit_check_runs.json @@ -0,0 +1,229 @@ +{ + "total_count": 2, + "check_runs": [ + { + "id": 16354767716, + "name": "Cargo test on nix (ubuntu-20.04, stable)", + "node_id": "CR_kwDOIVwnv88AAAADztHzZA", + "head_sha": "c3b93769f009d1738363041806908cb3739c88d9", + "external_id": "d3e3b6ad-fb11-5de5-1243-51ac1bbad548", + "url": "https://api.github.com/repos/iamjpotts/demo_docker_registry_auto_gen_tls/check-runs/16354767716", + "html_url": "https://github.com/iamjpotts/demo_docker_registry_auto_gen_tls/actions/runs/6028130407/job/16354767716", + "details_url": "https://github.com/iamjpotts/demo_docker_registry_auto_gen_tls/actions/runs/6028130407/job/16354767716", + "status": "completed", + "conclusion": "success", + "started_at": "2023-08-30T17:29:22Z", + "completed_at": "2023-08-30T17:31:13Z", + "output": { + "title": null, + "summary": null, + "text": null, + "annotations_count": 0, + "annotations_url": "https://api.github.com/repos/iamjpotts/demo_docker_registry_auto_gen_tls/check-runs/16354767716/annotations" + }, + "check_suite": { + "id": 15656410021 + }, + "app": { + "id": 15368, + "slug": "github-actions", + "node_id": "MDM6QXBwMTUzNjg=", + "owner": { + "login": "github", + "id": 9919, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=", + "avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github", + "html_url": "https://github.com/github", + "followers_url": "https://api.github.com/users/github/followers", + "following_url": "https://api.github.com/users/github/following{/other_user}", + "gists_url": "https://api.github.com/users/github/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github/subscriptions", + "organizations_url": "https://api.github.com/users/github/orgs", + "repos_url": "https://api.github.com/users/github/repos", + "events_url": "https://api.github.com/users/github/events{/privacy}", + "received_events_url": "https://api.github.com/users/github/received_events", + "type": "Organization", + "site_admin": false + }, + "name": "GitHub Actions", + "description": "Automate your workflow from idea to production", + "external_url": "https://help.github.com/en/actions", + "html_url": "https://github.com/apps/github-actions", + "created_at": "2018-07-30T09:30:17Z", + "updated_at": "2019-12-10T19:04:12Z", + "permissions": { + "actions": "write", + "administration": "read", + "checks": "write", + "contents": "write", + "deployments": "write", + "discussions": "write", + "issues": "write", + "merge_queues": "write", + "metadata": "read", + "packages": "write", + "pages": "write", + "pull_requests": "write", + "repository_hooks": "write", + "repository_projects": "write", + "security_events": "write", + "statuses": "write", + "vulnerability_alerts": "read" + }, + "events": [ + "branch_protection_rule", + "check_run", + "check_suite", + "create", + "delete", + "deployment", + "deployment_status", + "discussion", + "discussion_comment", + "fork", + "gollum", + "issues", + "issue_comment", + "label", + "merge_group", + "milestone", + "page_build", + "project", + "project_card", + "project_column", + "public", + "pull_request", + "pull_request_review", + "pull_request_review_comment", + "push", + "registry_package", + "release", + "repository", + "repository_dispatch", + "status", + "watch", + "workflow_dispatch", + "workflow_run" + ] + }, + "pull_requests": [ + + ] + }, + { + "id": 16354767496, + "name": "Cargo test on nix (ubuntu-20.04, 1.68)", + "node_id": "CR_kwDOIVwnv88AAAADztHyiA", + "head_sha": "c3b93769f009d1738363041806908cb3739c88d9", + "external_id": "6be726c3-c902-5ab8-6d02-d6f245c4aab4", + "url": "https://api.github.com/repos/iamjpotts/demo_docker_registry_auto_gen_tls/check-runs/16354767496", + "html_url": "https://github.com/iamjpotts/demo_docker_registry_auto_gen_tls/actions/runs/6028130407/job/16354767496", + "details_url": "https://github.com/iamjpotts/demo_docker_registry_auto_gen_tls/actions/runs/6028130407/job/16354767496", + "status": "completed", + "conclusion": "success", + "started_at": "2023-08-30T17:29:23Z", + "completed_at": "2023-08-30T17:31:30Z", + "output": { + "title": null, + "summary": null, + "text": null, + "annotations_count": 0, + "annotations_url": "https://api.github.com/repos/iamjpotts/demo_docker_registry_auto_gen_tls/check-runs/16354767496/annotations" + }, + "check_suite": { + "id": 15656410021 + }, + "app": { + "id": 15368, + "slug": "github-actions", + "node_id": "MDM6QXBwMTUzNjg=", + "owner": { + "login": "github", + "id": 9919, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=", + "avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github", + "html_url": "https://github.com/github", + "followers_url": "https://api.github.com/users/github/followers", + "following_url": "https://api.github.com/users/github/following{/other_user}", + "gists_url": "https://api.github.com/users/github/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github/subscriptions", + "organizations_url": "https://api.github.com/users/github/orgs", + "repos_url": "https://api.github.com/users/github/repos", + "events_url": "https://api.github.com/users/github/events{/privacy}", + "received_events_url": "https://api.github.com/users/github/received_events", + "type": "Organization", + "site_admin": false + }, + "name": "GitHub Actions", + "description": "Automate your workflow from idea to production", + "external_url": "https://help.github.com/en/actions", + "html_url": "https://github.com/apps/github-actions", + "created_at": "2018-07-30T09:30:17Z", + "updated_at": "2019-12-10T19:04:12Z", + "permissions": { + "actions": "write", + "administration": "read", + "checks": "write", + "contents": "write", + "deployments": "write", + "discussions": "write", + "issues": "write", + "merge_queues": "write", + "metadata": "read", + "packages": "write", + "pages": "write", + "pull_requests": "write", + "repository_hooks": "write", + "repository_projects": "write", + "security_events": "write", + "statuses": "write", + "vulnerability_alerts": "read" + }, + "events": [ + "branch_protection_rule", + "check_run", + "check_suite", + "create", + "delete", + "deployment", + "deployment_status", + "discussion", + "discussion_comment", + "fork", + "gollum", + "issues", + "issue_comment", + "label", + "merge_group", + "milestone", + "page_build", + "project", + "project_card", + "project_column", + "public", + "pull_request", + "pull_request_review", + "pull_request_review_comment", + "push", + "registry_package", + "release", + "repository", + "repository_dispatch", + "status", + "watch", + "workflow_dispatch", + "workflow_run" + ] + }, + "pull_requests": [ + + ] + } + ] +}