Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix integration tests runner #1716

Merged
merged 4 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
target
.DS_Store
.sentryclirc
.cargo
node_modules
coverage
Expand Down
1 change: 1 addition & 0 deletions .sentryclirc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
; this stub prevents global .sentryclirc from being loaded
kamilogorek marked this conversation as resolved.
Show resolved Hide resolved
61 changes: 61 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Integration Tests

Integration tests are written using `trycmd` crate. Consult the docs in case you need to understand how it works https://docs.rs/trycmd/latest/trycmd/.

The main parts to remember are:
- `register_test` already understands that all tests will live under `tests/integration/_cases`
- use mocks for API responses
- use fixtures for uploading/processing predefined data
- write separate tests for Windows when necessary (using `#[cfg(windows)]` attribute)
- use wildcard for dynamic output (eg. timestamps or UUIDs) (explained in `trycmd` docs - eg. `[..]` or `[EXE]`) or anything that is platform specific, as tests are run on Linux, OSX and Windows.
- `Usage:` help prompt _always_ requires `[EXE]` wildcard, so make sure to not forget it

## Updating Snapshots

In order to overwrite current integration tests snapshots, use `TRYCMD=overwrite` env variable when running tests, eg.

```shell
$ TRYCMD=overwrite cargo test
```

## Working with Fixtures

To run tests with specific fixtures in isolation, utilize the fact that `trycmd` is automatically creating and using `.in` as CWD and `.out` as stdout directories respectively for every test. This allows us to use eg. `tests/integration/_cases/sourcemaps/sourcemaps-inject.in/` path as a sandbox for `tests/integration/_cases/sourcemaps/sourcemaps-inject.trycmd` test case.
You can copy/remove any files programmatically from those directories and they will be ignored from the repository.

Here's basic test that use fixtures in isolation:

```rust
fn command_sourcemaps_inject_output() {
let testcase_cwd_path = "tests/integration/_cases/sourcemaps/sourcemaps-inject.in/";
if std::path::Path::new(testcase_cwd_path).exists() {
remove_dir_all(testcase_cwd_path).unwrap();
}
copy_recursively("tests/integration/_fixtures/inject/", testcase_cwd_path).unwrap();

register_test("sourcemaps/sourcemaps-inject.trycmd");
}
```

## Working with API Mocks

If you are trying mock an API response, use `mock_endpoint` helper with `with_response_file` method called,
and place the JSON file in an appropriate directory under `tests/integration/_responses`.
Make sure to assign the mock to a variable, otherwise it won't be picked up when creating a server.

```rust
let _assemble = mock_endpoint(
EndpointOptions::new(
"POST",
"/api/0/projects/wat-org/wat-project/files/difs/assemble/",
200,
)
.with_response_file("debug_files/post-difs-assemble.json"),
);
```

If you don't know what APIs will be hit during your test, register the test as normal, using `register_test` helper,
and run it in isolation, eg. `cargo test command_debug_files_upload`.
This will store all original API responses under `dump/` directory (controlled via. `SENTRY_DUMP_RESPONSES` env property set
in `tests/integration/mod`), where all path separators `/` are replaced with double underscore `__`.
This way you can simply copy the JSON files to `_responses` directory and update the data as needed.
6 changes: 6 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,12 @@ fn find_project_config_file() -> Option<PathBuf> {
}

fn load_global_config_file() -> Result<(PathBuf, Ini)> {
// Make sure to not load global configuration, as it can skew the tests results
// during local development for different environments.
if env::var("SENTRY_INTEGRATION_TEST").is_ok() {
return Ok((PathBuf::new(), Ini::new()));
}

let filename = find_global_config_file()?;
match fs::File::open(&filename) {
Ok(mut file) => match Ini::read_from(&mut file) {
Expand Down
68 changes: 41 additions & 27 deletions src/utils/vcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,13 +584,12 @@ use {
std::io::Write,
std::path::Path,
std::process::Command,
tempfile::tempdir,
tempfile::{tempdir, TempDir},
};

#[test]
fn test_find_matching_rev_with_lightweight_tag() {
let dir = tempdir().expect("Failed to generate temp dir.");
git_initialize_repo(dir.path());
let dir = git_initialize_repo();

git_create_commit(
dir.path(),
Expand Down Expand Up @@ -627,8 +626,7 @@ fn test_find_matching_rev_with_lightweight_tag() {

#[test]
fn test_find_matching_rev_with_annotated_tag() {
let dir = tempdir().expect("Failed to generate temp dir.");
git_initialize_repo(dir.path());
let dir = git_initialize_repo();

git_create_commit(
dir.path(),
Expand Down Expand Up @@ -854,30 +852,32 @@ fn test_url_normalization() {
}

#[cfg(test)]
fn git_initialize_repo(dir: &Path) {
fn git_initialize_repo() -> TempDir {
let dir = tempdir().expect("Failed to generate temp dir.");

Command::new("git")
.args(["init", "--quiet"])
.current_dir(dir)
.current_dir(&dir)
.spawn()
.expect("Failed to execute `git init`.")
.wait()
.expect("Failed to wait on git init.");
.expect("Failed to wait on `git init`.");

Command::new("git")
.args(["config", "--local", "user.name", "test"])
.current_dir(dir)
.current_dir(&dir)
.spawn()
.expect("Failed to execute `git config`.")
.wait()
.expect("Failed to wait on git config.");
.expect("Failed to wait on `git config`.");

Command::new("git")
.args(["config", "--local", "user.email", "[email protected]"])
.current_dir(dir)
.current_dir(&dir)
.spawn()
.expect("Failed to execute `git config`.")
.wait()
.expect("Failed to wait on git config.");
.expect("Failed to wait on `git config`.");

Command::new("git")
.args([
Expand All @@ -886,11 +886,29 @@ fn git_initialize_repo(dir: &Path) {
"origin",
"https://github.com/getsentry/sentry-cli",
])
.current_dir(dir)
.current_dir(&dir)
.spawn()
.expect("Failed to execute `git remote add`.")
.wait()
.expect("Failed to wait on git remote add.");
.expect("Failed to wait on `git remote add`.");

Command::new("git")
.args(["config", "--local", "commit.gpgsign", "false"])
.current_dir(&dir)
.spawn()
.expect("Failed to execute `config --local commit.gpgsign false`.")
.wait()
.expect("Failed to wait on `config --local commit.gpgsign false`.");

Command::new("git")
.args(["config", "--local", "tag.gpgsign", "false"])
.current_dir(&dir)
.spawn()
.expect("Failed to execute `config --local tag.gpgsign false`.")
.wait()
.expect("Failed to wait on `config --local tag.gpgsign false`.");

dir
}

#[cfg(test)]
Expand All @@ -905,7 +923,7 @@ fn git_create_commit(dir: &Path, file_path: &str, content: &[u8], commit_message
.spawn()
.expect("Failed to execute `git add .`");

add.wait().expect("Failed to wait on git add.");
add.wait().expect("Failed to wait on `git add`.");

let mut commit = Command::new("git")
.args([
Expand All @@ -915,12 +933,13 @@ fn git_create_commit(dir: &Path, file_path: &str, content: &[u8], commit_message
"--author",
"John Doe <[email protected]>",
"--quiet",
"--no-edit",
kamilogorek marked this conversation as resolved.
Show resolved Hide resolved
])
.current_dir(dir)
.spawn()
.expect("Failed to execute `git commit -m {message}`.");

commit.wait().expect("Failed to wait on git commit.");
commit.wait().expect("Failed to wait on `git commit`.");
}

#[cfg(test)]
Expand All @@ -939,7 +958,7 @@ fn git_create_tag(dir: &Path, tag_name: &str, annotated: bool) -> String {
.spawn()
.unwrap_or_else(|_| panic!("Failed to execute `git tag {tag_name}`"));

tag.wait().expect("Failed to wait on git tag.");
tag.wait().expect("Failed to wait on `git tag`.");

let hash = Command::new("git")
.args(["rev-list", "-n", "1", tag_name])
Expand All @@ -954,8 +973,7 @@ fn git_create_tag(dir: &Path, tag_name: &str, annotated: bool) -> String {

#[test]
fn test_get_commits_from_git() {
let dir = tempdir().expect("Failed to generate temp dir.");
git_initialize_repo(dir.path());
let dir = git_initialize_repo();

git_create_commit(
dir.path(),
Expand Down Expand Up @@ -989,8 +1007,7 @@ fn test_get_commits_from_git() {

#[test]
fn test_generate_patch_set_base() {
let dir = tempdir().expect("Failed to generate temp dir.");
git_initialize_repo(dir.path());
let dir = git_initialize_repo();

git_create_commit(
dir.path(),
Expand Down Expand Up @@ -1026,8 +1043,7 @@ fn test_generate_patch_set_base() {

#[test]
fn test_generate_patch_set_previous_commit() {
let dir = tempdir().expect("Failed to generate temp dir.");
git_initialize_repo(dir.path());
let dir = git_initialize_repo();

git_create_commit(
dir.path(),
Expand Down Expand Up @@ -1079,8 +1095,7 @@ fn test_generate_patch_set_previous_commit() {

#[test]
fn test_generate_patch_default_twenty() {
let dir = tempdir().expect("Failed to generate temp dir.");
git_initialize_repo(dir.path());
let dir = git_initialize_repo();

git_create_commit(
dir.path(),
Expand Down Expand Up @@ -1119,8 +1134,7 @@ fn test_generate_patch_default_twenty() {

#[test]
fn test_generate_patch_ignore_missing() {
let dir = tempdir().expect("Failed to generate temp dir.");
git_initialize_repo(dir.path());
let dir = git_initialize_repo();

git_create_commit(
dir.path(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
```
$ sentry-cli --log-level=debug debug-files bundle-sources tests/integration/_fixtures/SrcGenSampleApp.pdb
? success
DEBUG [..] sentry-cli version: [VERSION], platform: "[..]", architecture: "[..]"
INFO [..] sentry-cli was invoked with the following command line: "[CWD]/target/debug/sentry-cli[EXE]" "--log-level=debug" "debug-files" "bundle-sources" "tests/integration/_fixtures/SrcGenSampleApp.pdb"
DEBUG [..] Trying to add source file: /Users/matt/Code/temp/SrcGenSampleApp/SrcGenSampleApp/Program.cs
DEBUG [..] Trying to add source file: /Users/matt/Code/temp/SrcGenSampleApp/SrcGenSampleApp/obj/Release/net6.0/SrcGenSampleApp.GlobalUsings.g.cs
DEBUG [..] Trying to add source file: /Users/matt/Code/temp/SrcGenSampleApp/SrcGenSampleApp/obj/Release/net6.0/.NETCoreApp,Version=v6.0.AssemblyAttributes.cs
DEBUG [..] Trying to add source file: /Users/matt/Code/temp/SrcGenSampleApp/SrcGenSampleApp/obj/Release/net6.0/SrcGenSampleApp.AssemblyInfo.cs
DEBUG [..] Skipping embedded source file: /Users/matt/Code/temp/SrcGenSampleApp/SrcGenSampleApp/MySourceGenerator/MySourceGenerator.HelloSourceGenerator/Program.g.cs
skipped tests/integration/_fixtures/SrcGenSampleApp.pdb (no files found)
DEBUG [..] skipping update nagger because session is not attended

```
```
$ sentry-cli --log-level=debug debug-files bundle-sources tests/integration/_fixtures/SrcGenSampleApp.pdb
? success
INFO [..] Loaded config from [CWD]/.sentryclirc
DEBUG [..] sentry-cli version: [VERSION], platform: [..], architecture: [..]
INFO [..] sentry-cli was invoked with the following command line: "[CWD]/target/debug/sentry-cli[EXE]" "--log-level=debug" "debug-files" "bundle-sources" "tests/integration/_fixtures/SrcGenSampleApp.pdb"
DEBUG [..] Trying to add source file: /Users/matt/Code/temp/SrcGenSampleApp/SrcGenSampleApp/Program.cs
DEBUG [..] Trying to add source file: /Users/matt/Code/temp/SrcGenSampleApp/SrcGenSampleApp/obj/Release/net6.0/SrcGenSampleApp.GlobalUsings.g.cs
DEBUG [..] Trying to add source file: /Users/matt/Code/temp/SrcGenSampleApp/SrcGenSampleApp/obj/Release/net6.0/.NETCoreApp,Version=v6.0.AssemblyAttributes.cs
DEBUG [..] Trying to add source file: /Users/matt/Code/temp/SrcGenSampleApp/SrcGenSampleApp/obj/Release/net6.0/SrcGenSampleApp.AssemblyInfo.cs
DEBUG [..] Skipping embedded source file: /Users/matt/Code/temp/SrcGenSampleApp/SrcGenSampleApp/MySourceGenerator/MySourceGenerator.HelloSourceGenerator/Program.g.cs
skipped tests/integration/_fixtures/SrcGenSampleApp.pdb (no files found)
DEBUG [..] skipping update nagger because session is not attended

```
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
```
$ sentry-cli --log-level=debug debug-files upload --include-sources tests/integration/_fixtures/SrcGenSampleApp.pdb
? success
DEBUG [..] sentry-cli version: [VERSION], platform: "[..]", architecture: "[..]"
INFO [..] sentry-cli was invoked with the following command line: "[CWD]/target/debug/sentry-cli[EXE]" "--log-level=debug" "debug-files" "upload" "--include-sources" "tests/integration/_fixtures/SrcGenSampleApp.pdb"
...
> Found 1 debug information file (1 with embedded sources)
> Resolved source code for 0 debug information files
> Prepared debug information file for upload
...

```
```
$ sentry-cli --log-level=debug debug-files upload --include-sources tests/integration/_fixtures/SrcGenSampleApp.pdb
? success
INFO [..] Loaded config from [CWD]/.sentryclirc
...
> Found 1 debug information file (1 with embedded sources)
> Resolved source code for 0 debug information files
> Prepared debug information file for upload
...

```
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
```
$ sentry-cli send-envelope tests/integration/_fixtures/envelope.dat --log-level=debug
? success
INFO [..] Loaded config from [CWD]/.sentryclirc
DEBUG [..] sentry-cli version: [VERSION], platform: [..], architecture: [..]
INFO [..] sentry-cli was invoked with the following command line: "[CWD]/target/debug/sentry-cli[EXE]" "send-envelope" "tests/integration/_fixtures/envelope.dat" "--log-level=debug"
DEBUG [..] Envelope { event_id: Some(22d00b3f-d1b1-4b5d-8d20-49d138cd8a9c), items: EnvelopeItems([Event(Event { event_id: 22d00b3f-d1b1-4b5d-8d20-49d138cd8a9c, level: Error, fingerprint: ["{{ default }}"], culprit: None, transaction: None, message: None, logentry: None, logger: None, modules: {}, platform: "other", timestamp: [..], server_name: None, release: None, dist: None, environment: None, user: None, request: None, contexts: {}, breadcrumbs: Values { values: [] }, exception: Values { values: [] }, stacktrace: None, template: None, threads: Values { values: [] }, tags: {}, extra: {}, debug_meta: DebugMeta { sdk_info: None, images: [] }, sdk: None }), Transaction(Transaction { event_id: 22d00b3f-d1b1-4b5d-8d20-49d138cd8a9d, name: None, release: None, environment: None, tags: {}, extra: {}, sdk: None, platform: "other", timestamp: None, start_timestamp: [..], spans: [Span { span_id: SpanId([212, 44, 238, 159, 195, 231, 79, 92]), trace_id: TraceId([51, 94, 83, 214, 20, 71, 74, 204, 159, 137, 230, 50, 183, 118, 204, 40]), parent_span_id: None, same_process_as_parent: None, op: None, description: None, timestamp: None, start_timestamp: [..], status: None, tags: {}, data: {} }], contexts: {}, request: None, active_thread_id: None }), SessionUpdate(SessionUpdate { session_id: 22d00b3f-d1b1-4b5d-8d20-49d138cd8a9c, distinct_id: Some("[email protected]"), sequence: None, timestamp: None, started: [..], init: true, duration: Some(1.234), status: Ok, errors: 123, attributes: SessionAttributes { release: "[email protected]", environment: Some("production"), ip_address: None, user_agent: None } }), Attachment(Attachment { buffer: 12, filename: "file.txt", content_type: Some("application/octet-stream"), type: Some(Attachment) })]) }
DEBUG [..] Envelope { event_id: Some([..]), items: EnvelopeItems([Event(Event { event_id: [..], level: Error, fingerprint: ["{{ default }}"], culprit: None, transaction: None, message: None, logentry: None, logger: None, modules: {}, platform: "other", timestamp: [..], server_name: None, release: None, dist: None, environment: None, user: None, request: None, contexts: {}, breadcrumbs: Values { values: [] }, exception: Values { values: [] }, stacktrace: None, template: None, threads: Values { values: [] }, tags: {}, extra: {}, debug_meta: DebugMeta { sdk_info: None, images: [] }, sdk: None }), Transaction(Transaction { event_id: 22d00b3f-d1b1-4b5d-8d20-49d138cd8a9d, name: None, release: None, environment: None, tags: {}, extra: {}, sdk: None, platform: "other", timestamp: None, start_timestamp: [..], spans: [Span { span_id: SpanId([212, 44, 238, 159, 195, 231, 79, 92]), trace_id: TraceId([51, 94, 83, 214, 20, 71, 74, 204, 159, 137, 230, 50, 183, 118, 204, 40]), parent_span_id: None, same_process_as_parent: None, op: None, description: None, timestamp: None, start_timestamp: [..], status: None, tags: {}, data: {} }], contexts: {}, request: None, active_thread_id: None }), SessionUpdate(SessionUpdate { session_id: [..], distinct_id: Some("[email protected]"), sequence: None, timestamp: None, started: [..], init: true, duration: Some(1.234), status: Ok, errors: 123, attributes: SessionAttributes { release: "[email protected]", environment: Some("production"), ip_address: None, user_agent: None } }), Attachment(Attachment { buffer: 12, filename: "file.txt", content_type: Some("application/octet-stream"), type: Some(Attachment) })]) }
Envelope from file tests/integration/_fixtures/envelope.dat dispatched

```
1 change: 1 addition & 0 deletions tests/integration/_cases/send_event/send_event-raw.trycmd
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ $ sentry-cli send-event --log-level=debug
> --fingerprint custom-fingerprint
> --no-environ
? success
INFO [..] Loaded config from [CWD]/.sentryclirc
DEBUG [..] sentry-cli version: [VERSION], platform: [..], architecture: [..]
INFO [..] sentry-cli was invoked with the following command line: "[CWD]/target/debug/sentry-cli[EXE]" "send-event" "--log-level=debug" "--level" "debug" "--timestamp" "1649335000929" "--release" "my-release" "--dist" "my-dist" "--env" "production" "--message" "hello" "--platform" "prod" "--tag" "hello:there" "--extra" "hello:there" "--user" "id:42" "--fingerprint" "custom-fingerprint" "--no-environ"
DEBUG [..] Event { event_id: [..], level: Debug, fingerprint: ["custom-fingerprint"], culprit: None, transaction: None, message: None, logentry: Some(LogEntry { message: "hello", params: [] }), logger: None, modules: {}, platform: "prod", timestamp: SystemTime { tv_sec: 1649335000929, tv_nsec: 0 }, server_name: None, release: Some("my-release"), dist: Some("my-dist"), environment: Some("production"), user: Some(User { id: Some("42"), email: None, ip_address: Some(Auto), username: None, other: {} }), request: None, contexts: {}, breadcrumbs: Values { values: [] }, exception: Values { values: [] }, stacktrace: None, template: None, threads: Values { values: [] }, tags: {"hello": "there"}, extra: {"hello": String("there")}, debug_meta: DebugMeta { sdk_info: None, images: [] }, sdk: Some(ClientSdkInfo { name: "sentry-cli", version: "[VERSION]", integrations: [], packages: [] }) }
Expand Down
4 changes: 3 additions & 1 deletion tests/integration/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ fn command_info_help() {
#[test]
fn command_info_no_token() {
// Special case where we don't want any env variables set, so we don't use `register_task` helper.
TestCases::new().case("tests/integration/_cases/info/info-no-token.trycmd");
TestCases::new()
.env("SENTRY_INTEGRATION_TEST", "1")
.case("tests/integration/_cases/info/info-no-token.trycmd");
}

#[test]
Expand Down
Loading