Skip to content

Commit 96910f7

Browse files
authored
Merge pull request #9505 from Turbo87/error-snapshots
Use inline snapshots for error responses
2 parents ab5a7ea + e599e46 commit 96910f7

File tree

93 files changed

+139
-994
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+139
-994
lines changed

src/tests/account_lock.rs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::{util::RequestHelper, TestApp};
2-
use chrono::{Duration, NaiveDateTime, Utc};
2+
use chrono::{DateTime, Duration, NaiveDateTime, Utc};
33
use http::StatusCode;
4+
use insta::assert_snapshot;
45

56
const URL: &str = "/api/v1/me";
67
const LOCK_REASON: &str = "test lock reason";
@@ -28,30 +29,22 @@ async fn account_locked_indefinitely() {
2829

2930
let response = user.get::<()>(URL).await;
3031
assert_eq!(response.status(), StatusCode::FORBIDDEN);
31-
32-
let error_message = format!("This account is indefinitely locked. Reason: {LOCK_REASON}");
33-
assert_eq!(
34-
response.json(),
35-
json!({ "errors": [{ "detail": error_message }] })
36-
);
32+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"This account is indefinitely locked. Reason: test lock reason"}]}"#);
3733
}
3834

3935
#[tokio::test(flavor = "multi_thread")]
4036
async fn account_locked_with_future_expiry() {
41-
let until = Utc::now().naive_utc() + Duration::days(1);
37+
let until = "2099-12-12T12:12:12Z"
38+
.parse::<DateTime<Utc>>()
39+
.unwrap()
40+
.naive_utc();
4241

4342
let (app, _anon, user) = TestApp::init().with_user();
4443
lock_account(&app, user.as_model().id, Some(until));
4544

46-
let until = until.format("%Y-%m-%d at %H:%M:%S UTC");
4745
let response = user.get::<()>(URL).await;
4846
assert_eq!(response.status(), StatusCode::FORBIDDEN);
49-
50-
let error_message = format!("This account is locked until {until}. Reason: {LOCK_REASON}");
51-
assert_eq!(
52-
response.json(),
53-
json!({ "errors": [{ "detail": error_message }] })
54-
);
47+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"This account is locked until 2099-12-12 at 12:12:12 UTC. Reason: test lock reason"}]}"#);
5548
}
5649

5750
#[tokio::test(flavor = "multi_thread")]

src/tests/krate/publish/auth.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crates_io::schema::api_tokens;
44
use diesel::{ExpressionMethods, RunQueryDsl};
55
use googletest::prelude::*;
66
use http::StatusCode;
7-
use insta::{assert_json_snapshot, assert_snapshot};
7+
use insta::assert_snapshot;
88

99
#[tokio::test(flavor = "multi_thread")]
1010
async fn new_wrong_token() {
@@ -47,7 +47,7 @@ async fn new_krate_wrong_user() {
4747

4848
let response = another_user.publish_crate(crate_to_publish).await;
4949
assert_eq!(response.status(), StatusCode::FORBIDDEN);
50-
assert_json_snapshot!(response.json());
50+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"this crate exists but you don't seem to be an owner. If you believe this is a mistake, perhaps you need to accept an invitation to be an owner before publishing."}]}"#);
5151

5252
assert_that!(app.stored_files().await, empty());
5353
assert_that!(app.emails(), empty());

src/tests/krate/publish/basics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ async fn new_krate_duplicate_version() {
125125
let crate_to_publish = PublishBuilder::new("foo_dupe", "1.0.0");
126126
let response = token.publish_crate(crate_to_publish).await;
127127
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
128-
assert_json_snapshot!(response.json());
128+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"crate version `1.0.0` is already uploaded"}]}"#);
129129

130130
assert_that!(app.stored_files().await, empty());
131131
}

src/tests/krate/publish/categories.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,6 @@ async fn too_many_categories() {
5050
)
5151
.await;
5252
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
53-
assert_json_snapshot!(response.json());
53+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"expected at most 5 categories per crate"}]}"#);
5454
assert_that!(app.stored_files().await, empty());
5555
}

src/tests/krate/publish/dependencies.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ async fn invalid_dependency_name() {
1212
.publish_crate(PublishBuilder::new("foo", "1.0.0").dependency(DependencyBuilder::new("🦀")))
1313
.await;
1414
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
15-
assert_json_snapshot!(response.json());
15+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"invalid character `🦀` in dependency name: `🦀`, the first character must be an ASCII character"}]}"#);
1616
assert_that!(app.stored_files().await, empty());
1717
}
1818

@@ -50,7 +50,7 @@ async fn invalid_dependency_rename() {
5050
)
5151
.await;
5252
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
53-
assert_json_snapshot!(response.json());
53+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"invalid character `💩` in dependency name: `💩`, the first character must be an ASCII character, or `_`"}]}"#);
5454
assert_that!(app.stored_files().await, empty());
5555
}
5656

@@ -70,7 +70,7 @@ async fn invalid_dependency_name_starts_with_digit() {
7070
)
7171
.await;
7272
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
73-
assert_json_snapshot!(response.json());
73+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"the name `1-foo` cannot be used as a dependency name, the name cannot start with a digit"}]}"#);
7474
assert_that!(app.stored_files().await, empty());
7575
}
7676

@@ -90,7 +90,7 @@ async fn invalid_dependency_name_contains_unicode_chars() {
9090
)
9191
.await;
9292
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
93-
assert_json_snapshot!(response.json());
93+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"invalid character `🦀` in dependency name: `foo-🦀-bar`, characters must be an ASCII alphanumeric characters, `-`, or `_`"}]}"#);
9494
assert_that!(app.stored_files().await, empty());
9595
}
9696

@@ -110,7 +110,7 @@ async fn invalid_too_long_dependency_name() {
110110
)
111111
.await;
112112
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
113-
assert_json_snapshot!(response.json());
113+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"the dependency name `fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff` is too long (max 64 characters)"}]}"#);
114114
assert_that!(app.stored_files().await, empty());
115115
}
116116

@@ -129,7 +129,7 @@ async fn empty_dependency_name() {
129129
)
130130
.await;
131131
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
132-
assert_json_snapshot!(response.json());
132+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"dependency name cannot be empty"}]}"#);
133133
assert_that!(app.stored_files().await, empty());
134134
}
135135

@@ -202,7 +202,7 @@ async fn new_krate_with_broken_dependency_requirement() {
202202
let crate_to_publish = PublishBuilder::new("new_dep", "1.0.0").dependency(dependency);
203203
let response = token.publish_crate(crate_to_publish).await;
204204
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
205-
assert_json_snapshot!(response.json());
205+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"\"broken\" is an invalid version requirement"}]}"#);
206206
assert_that!(app.stored_files().await, empty());
207207
}
208208

@@ -221,7 +221,7 @@ async fn reject_new_krate_with_non_exact_dependency() {
221221

222222
let response = token.publish_crate(crate_to_publish).await;
223223
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
224-
assert_json_snapshot!(response.json());
224+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"no known crate named `foo_dep`"}]}"#);
225225
assert_that!(app.stored_files().await, empty());
226226
}
227227

@@ -249,7 +249,7 @@ async fn reject_new_crate_with_alternative_registry_dependency() {
249249
PublishBuilder::new("depends-on-alt-registry", "1.0.0").dependency(dependency);
250250
let response = token.publish_crate(crate_to_publish).await;
251251
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
252-
assert_json_snapshot!(response.json());
252+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"Dependency `dep` is hosted on another registry. Cross-registry dependencies are not permitted on crates.io."}]}"#);
253253
assert_that!(app.stored_files().await, empty());
254254
}
255255

@@ -268,7 +268,7 @@ async fn new_krate_with_wildcard_dependency() {
268268

269269
let response = token.publish_crate(crate_to_publish).await;
270270
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
271-
assert_json_snapshot!(response.json());
271+
assert_snapshot!(response.text(), @r##"{"errors":[{"detail":"wildcard (`*`) dependency constraints are not allowed on crates.io. Crate with this problem: `foo_wild` See https://doc.rust-lang.org/cargo/faq.html#can-libraries-use--as-a-version-for-their-dependencies for more information"}]}"##);
272272
assert_that!(app.stored_files().await, empty());
273273
}
274274

@@ -283,7 +283,7 @@ async fn new_krate_dependency_missing() {
283283

284284
let response = token.publish_crate(crate_to_publish).await;
285285
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
286-
assert_json_snapshot!(response.json());
286+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"no known crate named `bar_missing`"}]}"#);
287287
assert_that!(app.stored_files().await, empty());
288288
}
289289

@@ -321,7 +321,7 @@ async fn invalid_feature_name() {
321321
)
322322
.await;
323323
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
324-
assert_json_snapshot!(response.json());
324+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"invalid character `🍺` in feature `🍺`, the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`)"}]}"#);
325325
assert_that!(app.stored_files().await, empty());
326326
}
327327

src/tests/krate/publish/emails.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crates_io::schema::emails;
44
use diesel::{delete, update, ExpressionMethods, RunQueryDsl};
55
use googletest::prelude::*;
66
use http::StatusCode;
7-
use insta::assert_json_snapshot;
7+
use insta::assert_snapshot;
88

99
#[tokio::test(flavor = "multi_thread")]
1010
async fn new_krate_without_any_email_fails() {
@@ -18,7 +18,7 @@ async fn new_krate_without_any_email_fails() {
1818

1919
let response = token.publish_crate(crate_to_publish).await;
2020
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
21-
assert_json_snapshot!(response.json());
21+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"A verified email address is required to publish crates to crates.io. Visit https://crates.io/settings/profile to set and verify your email address."}]}"#);
2222
assert_that!(app.stored_files().await, empty());
2323
assert_that!(app.emails(), empty());
2424
}
@@ -38,7 +38,7 @@ async fn new_krate_with_unverified_email_fails() {
3838

3939
let response = token.publish_crate(crate_to_publish).await;
4040
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
41-
assert_json_snapshot!(response.json());
41+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"A verified email address is required to publish crates to crates.io. Visit https://crates.io/settings/profile to set and verify your email address."}]}"#);
4242
assert_that!(app.stored_files().await, empty());
4343
assert_that!(app.emails(), empty());
4444
}

src/tests/krate/publish/features.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::builders::{CrateBuilder, DependencyBuilder, PublishBuilder};
22
use crate::util::{RequestHelper, TestApp};
33
use googletest::prelude::*;
44
use http::StatusCode;
5-
use insta::assert_json_snapshot;
5+
use insta::{assert_json_snapshot, assert_snapshot};
66

77
#[tokio::test(flavor = "multi_thread")]
88
async fn features_version_2() {
@@ -60,7 +60,7 @@ async fn empty_feature_name() {
6060
let crate_to_publish = PublishBuilder::new("foo", "1.0.0").feature("", &[]);
6161
let response = token.publish_crate(crate_to_publish).await;
6262
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
63-
assert_json_snapshot!(response.json());
63+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"feature cannot be empty"}]}"#);
6464
assert!(app.stored_files().await.is_empty());
6565
}
6666

@@ -71,7 +71,7 @@ async fn invalid_feature_name1() {
7171
let crate_to_publish = PublishBuilder::new("foo", "1.0.0").feature("~foo", &[]);
7272
let response = token.publish_crate(crate_to_publish).await;
7373
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
74-
assert_json_snapshot!(response.json());
74+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"invalid character `~` in feature `~foo`, the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`)"}]}"#);
7575
assert_that!(app.stored_files().await, empty());
7676
}
7777

@@ -82,7 +82,7 @@ async fn invalid_feature_name2() {
8282
let crate_to_publish = PublishBuilder::new("foo", "1.0.0").feature("foo", &["!bar"]);
8383
let response = token.publish_crate(crate_to_publish).await;
8484
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
85-
assert_json_snapshot!(response.json());
85+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"invalid character `!` in feature `!bar`, the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`)"}]}"#);
8686
assert_that!(app.stored_files().await, empty());
8787
}
8888

@@ -92,7 +92,7 @@ async fn invalid_feature_name_start_with_hyphen() {
9292
let crate_to_publish = PublishBuilder::new("foo", "1.0.0").feature("-foo1.bar", &[]);
9393
let response = token.publish_crate(crate_to_publish).await;
9494
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
95-
assert_json_snapshot!(response.json());
95+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"invalid character `-` in feature `-foo1.bar`, the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`)"}]}"#);
9696
assert!(app.stored_files().await.is_empty());
9797
}
9898

@@ -112,7 +112,7 @@ async fn too_many_features() {
112112
.feature("five", &[]);
113113
let response = token.publish_crate(publish_builder).await;
114114
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
115-
assert_json_snapshot!(response.json());
115+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"crates.io only allows a maximum number of 3 features, but your crate is declaring 5 features.\n\nTake a look at https://blog.rust-lang.org/2023/10/26/broken-badges-and-23k-keywords.html to understand why this restriction was introduced.\n\nIf you have a use case that requires an increase of this limit, please send us an email to [email protected] to discuss the details."}]}"#);
116116
assert_that!(app.stored_files().await, empty());
117117
}
118118

@@ -138,7 +138,7 @@ async fn too_many_features_with_custom_limit() {
138138
.feature("five", &[]);
139139
let response = token.publish_crate(publish_builder).await;
140140
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
141-
assert_json_snapshot!(response.json());
141+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"crates.io only allows a maximum number of 4 features, but your crate is declaring 5 features.\n\nTake a look at https://blog.rust-lang.org/2023/10/26/broken-badges-and-23k-keywords.html to understand why this restriction was introduced.\n\nIf you have a use case that requires an increase of this limit, please send us an email to [email protected] to discuss the details."}]}"#);
142142
assert_that!(app.stored_files().await, empty());
143143

144144
let publish_builder = PublishBuilder::new("foo", "1.0.0")
@@ -169,7 +169,7 @@ async fn too_many_enabled_features() {
169169
.feature("default", &["one", "two", "three", "four", "five"]);
170170
let response = token.publish_crate(publish_builder).await;
171171
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
172-
assert_json_snapshot!(response.json());
172+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"crates.io only allows a maximum number of 3 features or dependencies that another feature can enable, but the \"default\" feature of your crate is enabling 5 features or dependencies.\n\nTake a look at https://blog.rust-lang.org/2023/10/26/broken-badges-and-23k-keywords.html to understand why this restriction was introduced.\n\nIf you have a use case that requires an increase of this limit, please send us an email to [email protected] to discuss the details."}]}"#);
173173
assert_that!(app.stored_files().await, empty());
174174
}
175175

@@ -191,7 +191,7 @@ async fn too_many_enabled_features_with_custom_limit() {
191191
.feature("default", &["one", "two", "three", "four", "five"]);
192192
let response = token.publish_crate(publish_builder).await;
193193
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
194-
assert_json_snapshot!(response.json());
194+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"crates.io only allows a maximum number of 4 features or dependencies that another feature can enable, but the \"default\" feature of your crate is enabling 5 features or dependencies.\n\nTake a look at https://blog.rust-lang.org/2023/10/26/broken-badges-and-23k-keywords.html to understand why this restriction was introduced.\n\nIf you have a use case that requires an increase of this limit, please send us an email to [email protected] to discuss the details."}]}"#);
195195
assert_that!(app.stored_files().await, empty());
196196

197197
let publish_builder =

src/tests/krate/publish/inheritance.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::builders::PublishBuilder;
22
use crate::util::{RequestHelper, TestApp};
33
use http::StatusCode;
4-
use insta::assert_json_snapshot;
4+
use insta::assert_snapshot;
55

66
#[tokio::test(flavor = "multi_thread")]
77
async fn workspace_inheritance() {
@@ -14,7 +14,7 @@ async fn workspace_inheritance() {
1414
)
1515
.await;
1616
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
17-
assert_json_snapshot!(response.json());
17+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"failed to parse `Cargo.toml` manifest file\n\nvalue from workspace hasn't been set"}]}"#);
1818
}
1919

2020
#[tokio::test(flavor = "multi_thread")]
@@ -25,5 +25,5 @@ async fn workspace_inheritance_with_dep() {
2525
"[package]\nname = \"foo\"\nversion = \"1.0.0\"\n\n[dependencies]\nserde.workspace = true\n",
2626
)).await;
2727
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
28-
assert_json_snapshot!(response.json());
28+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"failed to parse `Cargo.toml` manifest file\n\nvalue from workspace hasn't been set"}]}"#);
2929
}

src/tests/krate/publish/keywords.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::builders::PublishBuilder;
22
use crate::util::{RequestHelper, TestApp};
33
use googletest::prelude::*;
44
use http::StatusCode;
5-
use insta::assert_json_snapshot;
5+
use insta::{assert_json_snapshot, assert_snapshot};
66

77
#[tokio::test(flavor = "multi_thread")]
88
async fn good_keywords() {
@@ -26,17 +26,17 @@ async fn bad_keywords() {
2626
PublishBuilder::new("foo_bad_key", "1.0.0").keyword("super-long-keyword-name-oh-no");
2727
let response = token.publish_crate(crate_to_publish).await;
2828
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
29-
assert_json_snapshot!(response.json());
29+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"\"super-long-keyword-name-oh-no\" is an invalid keyword (keywords must have less than 20 characters)"}]}"#);
3030

3131
let crate_to_publish = PublishBuilder::new("foo_bad_key", "1.0.0").keyword("?@?%");
3232
let response = token.publish_crate(crate_to_publish).await;
3333
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
34-
assert_json_snapshot!(response.json());
34+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"\"?@?%\" is an invalid keyword"}]}"#);
3535

3636
let crate_to_publish = PublishBuilder::new("foo_bad_key", "1.0.0").keyword("áccênts");
3737
let response = token.publish_crate(crate_to_publish).await;
3838
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
39-
assert_json_snapshot!(response.json());
39+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"\"áccênts\" is an invalid keyword"}]}"#);
4040
}
4141

4242
#[tokio::test(flavor = "multi_thread")]
@@ -54,6 +54,6 @@ async fn too_many_keywords() {
5454
)
5555
.await;
5656
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
57-
assert_json_snapshot!(response.json());
57+
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"expected at most 5 keywords per crate"}]}"#);
5858
assert_that!(app.stored_files().await, empty());
5959
}

0 commit comments

Comments
 (0)