Skip to content

Commit 00cc70b

Browse files
authored
tests/user: Move tests into routes submodules (#5541)
1 parent 23d7e73 commit 00cc70b

File tree

14 files changed

+565
-531
lines changed

14 files changed

+565
-531
lines changed

src/tests/routes/crates/list.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,3 +819,31 @@ fn pagination_parameters_only_accept_integers() {
819819
json!({ "errors": [{ "detail": "invalid digit found in string" }] })
820820
);
821821
}
822+
823+
#[test]
824+
fn crates_by_user_id() {
825+
let (app, _, user) = TestApp::init().with_user();
826+
let id = user.as_model().id;
827+
app.db(|conn| {
828+
CrateBuilder::new("foo_my_packages", id).expect_build(conn);
829+
});
830+
831+
let response = user.search_by_user_id(id);
832+
assert_eq!(response.crates.len(), 1);
833+
}
834+
835+
#[test]
836+
fn crates_by_user_id_not_including_deleted_owners() {
837+
let (app, anon, user) = TestApp::init().with_user();
838+
let user = user.as_model();
839+
840+
app.db(|conn| {
841+
let krate = CrateBuilder::new("foo_my_packages", user.id).expect_build(conn);
842+
krate
843+
.owner_remove(app.as_inner(), conn, user, "foo")
844+
.unwrap();
845+
});
846+
847+
let response = anon.search_by_user_id(user.id);
848+
assert_eq!(response.crates.len(), 0);
849+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
use crate::builders::CrateBuilder;
2+
use crate::util::{RequestHelper, TestApp};
3+
use crate::{new_user, OkBool};
4+
use cargo_registry::schema::crate_owners;
5+
use diesel::prelude::*;
6+
7+
#[derive(Serialize)]
8+
struct EmailNotificationsUpdate {
9+
id: i32,
10+
email_notifications: bool,
11+
}
12+
13+
impl crate::util::MockCookieUser {
14+
fn update_email_notifications(&self, updates: Vec<EmailNotificationsUpdate>) -> OkBool {
15+
self.put(
16+
"/api/v1/me/email_notifications",
17+
json!(updates).to_string().as_bytes(),
18+
)
19+
.good()
20+
}
21+
}
22+
23+
/// A user should be able to update the email notifications for crates they own. Only the crates that
24+
/// were sent in the request should be updated to the corresponding `email_notifications` value.
25+
#[test]
26+
fn test_update_email_notifications() {
27+
let (app, _, user) = TestApp::init().with_user();
28+
29+
let my_crates = app.db(|conn| {
30+
vec![
31+
CrateBuilder::new("test_package", user.as_model().id).expect_build(conn),
32+
CrateBuilder::new("another_package", user.as_model().id).expect_build(conn),
33+
]
34+
});
35+
36+
let a_id = my_crates.get(0).unwrap().id;
37+
let b_id = my_crates.get(1).unwrap().id;
38+
39+
// Update crate_a: email_notifications = false
40+
// crate_a should be false, crate_b should be true
41+
user.update_email_notifications(vec![EmailNotificationsUpdate {
42+
id: a_id,
43+
email_notifications: false,
44+
}]);
45+
let json = user.show_me();
46+
47+
assert!(
48+
!json
49+
.owned_crates
50+
.iter()
51+
.find(|c| c.id == a_id)
52+
.unwrap()
53+
.email_notifications
54+
);
55+
assert!(
56+
json.owned_crates
57+
.iter()
58+
.find(|c| c.id == b_id)
59+
.unwrap()
60+
.email_notifications
61+
);
62+
63+
// Update crate_b: email_notifications = false
64+
// Both should be false now
65+
user.update_email_notifications(vec![EmailNotificationsUpdate {
66+
id: b_id,
67+
email_notifications: false,
68+
}]);
69+
let json = user.show_me();
70+
71+
assert!(
72+
!json
73+
.owned_crates
74+
.iter()
75+
.find(|c| c.id == a_id)
76+
.unwrap()
77+
.email_notifications
78+
);
79+
assert!(
80+
!json
81+
.owned_crates
82+
.iter()
83+
.find(|c| c.id == b_id)
84+
.unwrap()
85+
.email_notifications
86+
);
87+
88+
// Update crate_a and crate_b: email_notifications = true
89+
// Both should be true
90+
user.update_email_notifications(vec![
91+
EmailNotificationsUpdate {
92+
id: a_id,
93+
email_notifications: true,
94+
},
95+
EmailNotificationsUpdate {
96+
id: b_id,
97+
email_notifications: true,
98+
},
99+
]);
100+
let json = user.show_me();
101+
102+
json.owned_crates.iter().for_each(|c| {
103+
assert!(c.email_notifications);
104+
})
105+
}
106+
107+
/// A user should not be able to update the `email_notifications` value for a crate that is not
108+
/// owned by them.
109+
#[test]
110+
fn test_update_email_notifications_not_owned() {
111+
let (app, _, user) = TestApp::init().with_user();
112+
113+
let not_my_crate = app.db(|conn| {
114+
let u = new_user("arbitrary_username")
115+
.create_or_update(None, &app.as_inner().emails, conn)
116+
.unwrap();
117+
CrateBuilder::new("test_package", u.id).expect_build(conn)
118+
});
119+
120+
user.update_email_notifications(vec![EmailNotificationsUpdate {
121+
id: not_my_crate.id,
122+
email_notifications: false,
123+
}]);
124+
125+
let email_notifications: bool = app
126+
.db(|conn| {
127+
crate_owners::table
128+
.select(crate_owners::email_notifications)
129+
.filter(crate_owners::crate_id.eq(not_my_crate.id))
130+
.first(conn)
131+
})
132+
.unwrap();
133+
134+
// There should be no change to the `email_notifications` value for a crate not belonging to me
135+
assert!(email_notifications);
136+
}

src/tests/routes/me/get.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use crate::builders::CrateBuilder;
2+
use crate::util::{RequestHelper, TestApp};
3+
use cargo_registry::views::{EncodablePrivateUser, OwnedCrate};
4+
5+
impl crate::util::MockCookieUser {
6+
pub fn show_me(&self) -> UserShowPrivateResponse {
7+
let url = "/api/v1/me";
8+
self.get(url).good()
9+
}
10+
}
11+
12+
#[derive(Deserialize)]
13+
pub struct UserShowPrivateResponse {
14+
pub user: EncodablePrivateUser,
15+
pub owned_crates: Vec<OwnedCrate>,
16+
}
17+
18+
#[test]
19+
fn me() {
20+
let url = "/api/v1/me";
21+
let (app, anon) = TestApp::init().empty();
22+
anon.get(url).assert_forbidden();
23+
24+
let user = app.db_new_user("foo");
25+
let json = user.show_me();
26+
27+
assert_eq!(json.owned_crates.len(), 0);
28+
29+
app.db(|conn| {
30+
CrateBuilder::new("foo_my_packages", user.as_model().id).expect_build(conn);
31+
assert_eq!(json.user.email, user.as_model().email(conn).unwrap());
32+
});
33+
let updated_json = user.show_me();
34+
35+
assert_eq!(updated_json.owned_crates.len(), 1);
36+
}
37+
38+
#[test]
39+
fn test_user_owned_crates_doesnt_include_deleted_ownership() {
40+
let (app, _, user) = TestApp::init().with_user();
41+
let user_model = user.as_model();
42+
43+
app.db(|conn| {
44+
let krate = CrateBuilder::new("foo_my_packages", user_model.id).expect_build(conn);
45+
krate
46+
.owner_remove(app.as_inner(), conn, user_model, &user_model.gh_login)
47+
.unwrap();
48+
});
49+
50+
let json = user.show_me();
51+
assert_eq!(json.owned_crates.len(), 0);
52+
}

src/tests/routes/me/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1+
mod email_notifications;
2+
pub mod get;
13
pub mod tokens;
4+
mod updates;

src/tests/routes/me/updates.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
use crate::builders::{CrateBuilder, VersionBuilder};
2+
use crate::util::{RequestHelper, TestApp};
3+
use crate::OkBool;
4+
use cargo_registry::schema::versions;
5+
use cargo_registry::views::EncodableVersion;
6+
use diesel::prelude::*;
7+
use diesel::update;
8+
use http::StatusCode;
9+
10+
#[test]
11+
fn api_token_cannot_get_user_updates() {
12+
let (_, _, _, token) = TestApp::init().with_token();
13+
token.get("/api/v1/me/updates").assert_forbidden();
14+
}
15+
16+
#[test]
17+
fn following() {
18+
#[derive(Deserialize)]
19+
struct R {
20+
versions: Vec<EncodableVersion>,
21+
meta: Meta,
22+
}
23+
#[derive(Deserialize)]
24+
struct Meta {
25+
more: bool,
26+
}
27+
28+
let (app, _, user) = TestApp::init().with_user();
29+
let user_model = user.as_model();
30+
let user_id = user_model.id;
31+
app.db(|conn| {
32+
CrateBuilder::new("foo_fighters", user_id)
33+
.version(VersionBuilder::new("1.0.0"))
34+
.expect_build(conn);
35+
36+
// Make foo_fighters's version mimic a version published before we started recording who
37+
// published versions
38+
let none: Option<i32> = None;
39+
update(versions::table)
40+
.set(versions::published_by.eq(none))
41+
.execute(conn)
42+
.unwrap();
43+
44+
CrateBuilder::new("bar_fighters", user_id)
45+
.version(VersionBuilder::new("1.0.0"))
46+
.expect_build(conn);
47+
});
48+
49+
let r: R = user.get("/api/v1/me/updates").good();
50+
assert_eq!(r.versions.len(), 0);
51+
assert!(!r.meta.more);
52+
53+
user.put::<OkBool>("/api/v1/crates/foo_fighters/follow", b"")
54+
.good();
55+
user.put::<OkBool>("/api/v1/crates/bar_fighters/follow", b"")
56+
.good();
57+
58+
let r: R = user.get("/api/v1/me/updates").good();
59+
assert_eq!(r.versions.len(), 2);
60+
assert!(!r.meta.more);
61+
let foo_version = r
62+
.versions
63+
.iter()
64+
.find(|v| v.krate == "foo_fighters")
65+
.unwrap();
66+
assert_none!(&foo_version.published_by);
67+
let bar_version = r
68+
.versions
69+
.iter()
70+
.find(|v| v.krate == "bar_fighters")
71+
.unwrap();
72+
assert_eq!(
73+
bar_version.published_by.as_ref().unwrap().login,
74+
user_model.gh_login
75+
);
76+
77+
let r: R = user
78+
.get_with_query("/api/v1/me/updates", "per_page=1")
79+
.good();
80+
assert_eq!(r.versions.len(), 1);
81+
assert!(r.meta.more);
82+
83+
user.delete::<OkBool>("/api/v1/crates/bar_fighters/follow")
84+
.good();
85+
let r: R = user
86+
.get_with_query("/api/v1/me/updates", "page=2&per_page=1")
87+
.good();
88+
assert_eq!(r.versions.len(), 0);
89+
assert!(!r.meta.more);
90+
91+
let response = user.get_with_query::<()>("/api/v1/me/updates", "page=0");
92+
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
93+
assert_eq!(
94+
response.into_json(),
95+
json!({ "errors": [{ "detail": "page indexing starts from 1, page 0 is invalid" }] })
96+
);
97+
}

src/tests/routes/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,7 @@ pub mod crates;
1717
pub mod keywords;
1818
pub mod me;
1919
pub mod metrics;
20+
pub mod session;
2021
pub mod summary;
22+
pub mod users;
2123
pub mod versions;

src/tests/routes/session/authorize.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use crate::util::{RequestHelper, TestApp};
2+
use http::StatusCode;
3+
4+
#[test]
5+
fn access_token_needs_data() {
6+
let (_, anon) = TestApp::init().empty();
7+
let response = anon.get::<()>("/api/private/session/authorize");
8+
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
9+
assert_eq!(
10+
response.into_json(),
11+
json!({ "errors": [{ "detail": "invalid state parameter" }] })
12+
);
13+
}

src/tests/routes/session/begin.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use crate::util::{RequestHelper, TestApp};
2+
3+
#[derive(Deserialize)]
4+
struct AuthResponse {
5+
url: String,
6+
state: String,
7+
}
8+
9+
#[test]
10+
fn auth_gives_a_token() {
11+
let (_, anon) = TestApp::init().empty();
12+
let json: AuthResponse = anon.get("/api/private/session/begin").good();
13+
assert!(json.url.contains(&json.state));
14+
}

src/tests/routes/session/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mod authorize;
2+
mod begin;

src/tests/routes/users/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mod read;
2+
mod stats;
3+
pub mod update;

0 commit comments

Comments
 (0)