Skip to content

Commit 4853445

Browse files
ref(test): Introduce assert_cmd test manager
This should reduce boilerplate when writing `assert_cmd` tests.
1 parent deee91a commit 4853445

File tree

5 files changed

+92
-53
lines changed

5 files changed

+92
-53
lines changed

tests/integration/debug_files/upload.rs

Lines changed: 17 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ use std::sync::atomic::{AtomicBool, Ordering};
22
use std::sync::LazyLock;
33
use std::{fs, str};
44

5-
use assert_cmd::Command;
65
use regex::bytes::Regex;
76

8-
use crate::integration::{test_utils::env, MockEndpointBuilder, TestManager};
7+
use crate::integration::{AssertCommand, MockEndpointBuilder, TestManager};
98

109
/// This regex is used to extract the boundary from the content-type header.
1110
/// We need to match the boundary, since it changes with each request.
@@ -165,7 +164,7 @@ fn command_debug_files_upload_no_upload() {
165164
/// The mock assemble endpoint returns a 200 response simulating the case where all chunks
166165
/// are already uploaded.
167166
fn ensure_correct_assemble_call() {
168-
let manager = TestManager::new()
167+
TestManager::new()
169168
.mock_endpoint(
170169
MockEndpointBuilder::new("GET", "/api/0/organizations/wat-org/chunk-upload/")
171170
.with_response_file("debug_files/get-chunk-upload.json"),
@@ -184,26 +183,13 @@ fn ensure_correct_assemble_call() {
184183
}
185184
}"#,
186185
),
187-
);
188-
189-
let mut command = Command::cargo_bin("sentry-cli").expect("sentry-cli should be available");
190-
191-
command.args(
192-
"debug-files upload --include-sources tests/integration/_fixtures/SrcGenSampleApp.pdb"
193-
.split(' '),
194-
);
195-
196-
env::set_all(manager.server_info(), |k, v| {
197-
command.env(k, v.as_ref());
198-
});
199-
200-
let command_result = command.assert();
201-
202-
// First assert the mock was called as expected, then that the command was successful.
203-
// This is because failure with the mock assertion can cause the command to fail, and
204-
// the mock assertion failure is likely more interesting in this case.
205-
manager.assert_mock_endpoints();
206-
command_result.success();
186+
)
187+
.assert_cmd(
188+
"debug-files upload --include-sources tests/integration/_fixtures/SrcGenSampleApp.pdb"
189+
.split(' '),
190+
)
191+
.with_default_token()
192+
.run_and_assert(AssertCommand::Success);
207193
}
208194

209195
#[test]
@@ -217,7 +203,7 @@ fn ensure_correct_chunk_upload() {
217203
fs::read("tests/integration/_expected_requests/debug_files/upload/chunk_upload.bin")
218204
.expect("expected chunk body file should be present");
219205

220-
let manager = TestManager::new()
206+
TestManager::new()
221207
.mock_endpoint(
222208
MockEndpointBuilder::new("GET", "/api/0/organizations/wat-org/chunk-upload/")
223209
.with_response_file("debug_files/get-chunk-upload.json"),
@@ -291,24 +277,11 @@ fn ensure_correct_chunk_upload() {
291277
.into()
292278
})
293279
.expect(2),
294-
);
295-
296-
let mut command = Command::cargo_bin("sentry-cli").expect("sentry-cli should be available");
297-
298-
command.args(
299-
"debug-files upload --include-sources tests/integration/_fixtures/SrcGenSampleApp.pdb"
300-
.split(' '),
301-
);
302-
303-
env::set_all(manager.server_info(), |k, v| {
304-
command.env(k, v.as_ref());
305-
});
306-
307-
let command_result = command.assert();
308-
309-
// First assert the mock was called as expected, then that the command was successful.
310-
// This is because failure with the mock assertion can cause the command to fail, and
311-
// the mock assertion failure is likely more interesting in this case.
312-
manager.assert_mock_endpoints();
313-
command_result.success();
280+
)
281+
.assert_cmd(
282+
"debug-files upload --include-sources tests/integration/_fixtures/SrcGenSampleApp.pdb"
283+
.split(' '),
284+
)
285+
.with_default_token()
286+
.run_and_assert(AssertCommand::Success);
314287
}

tests/integration/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use std::io;
3131
use std::path::Path;
3232

3333
use test_utils::MockEndpointBuilder;
34-
use test_utils::{env, ChunkOptions, ServerBehavior, TestManager};
34+
use test_utils::{env, AssertCommand, ChunkOptions, ServerBehavior, TestManager};
3535

3636
pub const UTC_DATE_FORMAT: &str = r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{6,9}Z";
3737
const VERSION: &str = env!("CARGO_PKG_VERSION");

tests/integration/test_utils/env.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,3 @@ pub fn set_auth_token(setter: impl FnOnce(&'static str, Cow<'static, str>)) {
4242
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".into(),
4343
);
4444
}
45-
46-
/// Set all environment variables, including the auth token and the environments
47-
/// set by `set`.
48-
pub fn set_all(server_info: MockServerInfo, mut setter: impl FnMut(&str, Cow<str>)) {
49-
set(server_info, &mut setter);
50-
set_auth_token(setter);
51-
}

tests/integration/test_utils/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ mod test_manager;
88

99
pub use mock_common_endpoints::{ChunkOptions, ServerBehavior};
1010
pub use mock_endpoint_builder::MockEndpointBuilder;
11-
pub use test_manager::TestManager;
11+
pub use test_manager::{AssertCommand, TestManager};
1212

1313
use env::MockServerInfo;

tests/integration/test_utils/test_manager.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
use std::ffi::OsStr;
12
use std::fmt::Display;
23

4+
use assert_cmd::Command;
35
use mockito::{Mock, Server, ServerGuard};
46
use thiserror::Error;
57
use trycmd::TestCases;
@@ -67,6 +69,19 @@ impl TestManager {
6769
TrycmdTestManager::new(self, path)
6870
}
6971

72+
/// Define an assert_cmd test.
73+
/// The args contain the command line arguments which will be passed to `sentry-cli`.
74+
/// The test is run when the appropriate function is called on the returned
75+
/// `AssertCmdTestManager`.
76+
/// The test manager handles setting the environment variables for the test.
77+
pub fn assert_cmd<I, S>(self, args: I) -> AssertCmdTestManager
78+
where
79+
I: IntoIterator<Item = S>,
80+
S: AsRef<OsStr>,
81+
{
82+
AssertCmdTestManager::new(self, args)
83+
}
84+
7085
/// Get the URL of the mock server.
7186
pub fn server_url(&self) -> String {
7287
self.server().url()
@@ -158,3 +173,61 @@ impl TrycmdTestManager {
158173
self.manager.server()
159174
}
160175
}
176+
177+
pub struct AssertCmdTestManager {
178+
manager: TestManager,
179+
command: Command,
180+
}
181+
182+
/// The type of assertion to perform on the command result.
183+
// Currently we only assert success, but we may add other assertions
184+
// (e.g. failure or skip) in the future.
185+
pub enum AssertCommand {
186+
/// Assert that the command succeeds (i.e. returns a `0` exit code).
187+
Success,
188+
}
189+
190+
impl AssertCmdTestManager {
191+
/// Set the auth token environment variable to a fake value.
192+
/// This may be needed when running a Sentry CLI command that checks that
193+
/// an auth token is set. No token is set by default.
194+
pub fn with_default_token(mut self) -> Self {
195+
env::set_auth_token(|k, v| {
196+
self.command.env(k, v.as_ref());
197+
});
198+
199+
self
200+
}
201+
202+
/// Run the command and perform assertions.
203+
///
204+
/// This function asserts both the mocks and the command result.
205+
/// The mocks are asserted first, since a failure in the mocks
206+
/// could cause the command to fail. The function consumes the
207+
/// `AssertCmdTestManager`, as it should not be used after this call.
208+
///
209+
/// Panics if any assertions fail.
210+
pub fn run_and_assert(mut self, assert: AssertCommand) {
211+
let command_result = self.command.assert();
212+
self.manager.assert_mock_endpoints();
213+
214+
match assert {
215+
AssertCommand::Success => command_result.success(),
216+
};
217+
}
218+
219+
fn new<I, S>(manager: TestManager, args: I) -> Self
220+
where
221+
I: IntoIterator<Item = S>,
222+
S: AsRef<OsStr>,
223+
{
224+
let mut command = Command::cargo_bin("sentry-cli").expect("sentry-cli should be available");
225+
command.args(args);
226+
227+
env::set(manager.server_info(), |k, v| {
228+
command.env(k, v.as_ref());
229+
});
230+
231+
Self { manager, command }
232+
}
233+
}

0 commit comments

Comments
 (0)