Skip to content

Commit 74d533a

Browse files
bors-voyager[bot]jtgeibelcarols10cents
committed
Merge #1511
1511: Build on the new test infrastructure and convert more tests r=carols10cents a=jtgeibel This builds on the test infrastructure improvements made by @carols10cents. In particular, it adds a `Response<T>` type that wraps the `conduit::Response` and a callback. Tests can use the `good()` method which asserts that the response status is good and returns the deserialized `T`. It is the equivalent of `::json(&mut ok_resp!(...))`. The `bad()` method is equivalent to `bad_resp!`. The callback is called if the test calls `good()` on the response. This allows helper methods to return a `Response<T>` with attached assertions that are run if the test expects the response to deserialize correctly, but are not run if the test expects an error response. Co-authored-by: Justin Geibel <[email protected]> Co-authored-by: Carol (Nichols || Goulding) <[email protected]>
2 parents 4744f77 + 442c32a commit 74d533a

File tree

13 files changed

+685
-771
lines changed

13 files changed

+685
-771
lines changed

src/controllers/user/me.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use util::bad_request;
88

99
use models::{Email, Follow, NewEmail, User, Version};
1010
use schema::{crates, emails, follows, users, versions};
11-
use views::{EncodablePrivateUser, EncodableVersion};
11+
use views::{EncodableMe, EncodableVersion};
1212

1313
/// Handles the `GET /me` route.
1414
pub fn me(req: &mut dyn Request) -> CargoResult<Response> {
@@ -40,11 +40,7 @@ pub fn me(req: &mut dyn Request) -> CargoResult<Response> {
4040
let verification_sent = verified || verification_sent;
4141
let user = User { email, ..user };
4242

43-
#[derive(Serialize)]
44-
struct R {
45-
user: EncodablePrivateUser,
46-
}
47-
Ok(req.json(&R {
43+
Ok(req.json(&EncodableMe {
4844
user: user.encodable_private(verified, verification_sent),
4945
}))
5046
}

src/db.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub trait RequestTransaction {
3636
/// Return the lazily initialized postgres connection for this request.
3737
///
3838
/// The connection will live for the lifetime of the request.
39+
// FIXME: This description does not match the implementation below.
3940
fn db_conn(&self) -> CargoResult<DieselPooledConn>;
4041
}
4142

src/tests/all.rs

Lines changed: 25 additions & 204 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,14 @@ use cargo_registry::app::App;
3838
use cargo_registry::middleware::current_user::AuthenticationSource;
3939
use cargo_registry::Replica;
4040
use chrono::Utc;
41-
use conduit::{Handler, Method, Request};
41+
use conduit::{Method, Request};
4242
use conduit_test::MockRequest;
4343
use diesel::prelude::*;
4444
use flate2::write::GzEncoder;
4545
use flate2::Compression;
4646

47-
pub use cargo_registry::{models, schema, views};
47+
use cargo_registry::{models, schema, views};
48+
use util::{Bad, RequestHelper, TestApp};
4849

4950
use models::{Crate, CrateDownload, CrateOwner, Dependency, Keyword, Team, User, Version};
5051
use models::{NewCategory, NewCrate, NewTeam, NewUser, NewVersion};
@@ -61,15 +62,9 @@ macro_rules! t {
6162
};
6263
}
6364

64-
macro_rules! t_resp {
65-
($e:expr) => {
66-
t!($e)
67-
};
68-
}
69-
7065
macro_rules! ok_resp {
7166
($e:expr) => {{
72-
let resp = t_resp!($e);
67+
let resp = t!($e);
7368
if !::ok_resp(&resp) {
7469
panic!("bad response: {:?}", resp.status);
7570
}
@@ -79,23 +74,14 @@ macro_rules! ok_resp {
7974

8075
macro_rules! bad_resp {
8176
($e:expr) => {{
82-
let mut resp = t_resp!($e);
77+
let mut resp = t!($e);
8378
match ::bad_resp(&mut resp) {
8479
None => panic!("ok response: {:?}", resp.status),
8580
Some(b) => b,
8681
}
8782
}};
8883
}
8984

90-
#[derive(Deserialize, Debug)]
91-
struct Error {
92-
detail: String,
93-
}
94-
#[derive(Deserialize)]
95-
struct Bad {
96-
errors: Vec<Error>,
97-
}
98-
9985
mod badge;
10086
mod categories;
10187
mod category;
@@ -108,10 +94,11 @@ mod schema_details;
10894
mod team;
10995
mod token;
11096
mod user;
97+
mod util;
11198
mod version;
11299

113100
#[derive(Deserialize, Debug)]
114-
struct GoodCrate {
101+
pub struct GoodCrate {
115102
#[serde(rename = "crate")]
116103
krate: EncodableCrate,
117104
warnings: Warnings,
@@ -137,6 +124,10 @@ pub struct CrateResponse {
137124
versions: Vec<EncodableVersion>,
138125
keywords: Vec<EncodableKeyword>,
139126
}
127+
#[derive(Deserialize)]
128+
struct OkBool {
129+
ok: bool,
130+
}
140131

141132
fn app() -> (
142133
record::Bomb,
@@ -185,16 +176,15 @@ fn app() -> (
185176
}
186177

187178
// Return the environment variable only if it has been defined
188-
fn env(s: &str) -> String {
189-
// Handles both the `None` and empty string cases e.g. VAR=
190-
// by converting `None` to an empty string
191-
let env_result = env::var(s).ok().unwrap_or_default();
192-
193-
if env_result == "" {
194-
panic!("must have `{}` defined", s);
179+
fn env(var: &str) -> String {
180+
match env::var(var) {
181+
Ok(ref s) if s == "" => panic!("environment variable `{}` must not be empty", var),
182+
Ok(s) => s,
183+
_ => panic!(
184+
"environment variable `{}` must be defined and valid unicode",
185+
var
186+
),
195187
}
196-
197-
env_result
198188
}
199189

200190
fn req(method: conduit::Method, path: &str) -> MockRequest {
@@ -226,11 +216,11 @@ where
226216
}
227217
}
228218

229-
static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
219+
static NEXT_GH_ID: AtomicUsize = ATOMIC_USIZE_INIT;
230220

231221
fn new_user(login: &str) -> NewUser<'_> {
232222
NewUser {
233-
gh_id: NEXT_ID.fetch_add(1, Ordering::SeqCst) as i32,
223+
gh_id: NEXT_GH_ID.fetch_add(1, Ordering::SeqCst) as i32,
234224
gh_login: login,
235225
email: None,
236226
name: None,
@@ -239,21 +229,9 @@ fn new_user(login: &str) -> NewUser<'_> {
239229
}
240230
}
241231

242-
fn user(login: &str) -> User {
243-
User {
244-
id: NEXT_ID.fetch_add(1, Ordering::SeqCst) as i32,
245-
gh_id: NEXT_ID.fetch_add(1, Ordering::SeqCst) as i32,
246-
gh_login: login.to_string(),
247-
email: None,
248-
name: None,
249-
gh_avatar: None,
250-
gh_access_token: "some random token".into(),
251-
}
252-
}
253-
254232
fn new_team(login: &str) -> NewTeam<'_> {
255233
NewTeam {
256-
github_id: NEXT_ID.fetch_add(1, Ordering::SeqCst) as i32,
234+
github_id: NEXT_GH_ID.fetch_add(1, Ordering::SeqCst) as i32,
257235
login,
258236
name: None,
259237
avatar: None,
@@ -511,8 +489,10 @@ fn new_version(crate_id: i32, num: &str, crate_size: Option<i32>) -> NewVersion
511489
}
512490

513491
fn krate(name: &str) -> Crate {
492+
static NEXT_CRATE_ID: AtomicUsize = ATOMIC_USIZE_INIT;
493+
514494
Crate {
515-
id: NEXT_ID.fetch_add(1, Ordering::SeqCst) as i32,
495+
id: NEXT_CRATE_ID.fetch_add(1, Ordering::SeqCst) as i32,
516496
name: name.to_string(),
517497
updated_at: Utc::now().naive_utc(),
518498
created_at: Utc::now().naive_utc(),
@@ -758,165 +738,6 @@ fn new_crate_to_body_with_tarball(new_crate: &u::NewCrate, tarball: &[u8]) -> Ve
758738
body
759739
}
760740

761-
/// A struct representing a user's session to be used for the duration of a test.
762-
/// This injects requests directly into the router, so doesn't quite exercise the application
763-
/// exactly as in production, but it's close enough for most testing purposes.
764-
/// Has useful methods for making common HTTP requests.
765-
pub struct MockUserSession {
766-
app: Arc<App>,
767-
// The bomb needs to be held in scope until the end of the test.
768-
_bomb: record::Bomb,
769-
middle: conduit_middleware::MiddlewareBuilder,
770-
request: MockRequest,
771-
_user: User,
772-
}
773-
774-
impl MockUserSession {
775-
/// Create a session logged in with an arbitrary user.
776-
pub fn logged_in() -> Self {
777-
let (bomb, app, middle) = app();
778-
779-
let mut request = MockRequest::new(Method::Get, "/");
780-
let user = {
781-
let conn = app.diesel_database.get().unwrap();
782-
let user = new_user("foo").create_or_update(&conn).unwrap();
783-
request.mut_extensions().insert(user.clone());
784-
request
785-
.mut_extensions()
786-
.insert(AuthenticationSource::SessionCookie);
787-
788-
user
789-
};
790-
791-
MockUserSession {
792-
app,
793-
_bomb: bomb,
794-
middle,
795-
request,
796-
_user: user,
797-
}
798-
}
799-
800-
/// Create a session logged in with the given user.
801-
// pub fn logged_in_as(_user: &User) -> Self {
802-
// unimplemented!();
803-
// }
804-
805-
/// For internal use only: make the current request
806-
fn make_request(&mut self) -> conduit::Response {
807-
ok_resp!(self.middle.call(&mut self.request))
808-
}
809-
810-
/// Log out the currently logged in user.
811-
pub fn logout(&mut self) {
812-
logout(&mut self.request);
813-
}
814-
815-
/// Using the same session, log in as a different user.
816-
pub fn log_in_as(&mut self, user: &User) {
817-
sign_in_as(&mut self.request, user);
818-
}
819-
820-
/// Publish the crate as specified by the given builder
821-
pub fn publish(&mut self, publish_builder: PublishBuilder) {
822-
let krate_name = publish_builder.krate_name.clone();
823-
self.request
824-
.with_path("/api/v1/crates/new")
825-
.with_method(Method::Put)
826-
.with_body(&publish_builder.body());
827-
let mut response = self.make_request();
828-
let json: GoodCrate = json(&mut response);
829-
assert_eq!(json.krate.name, krate_name);
830-
}
831-
832-
/// Request the JSON used for a crate's page.
833-
pub fn show_crate(&mut self, krate_name: &str) -> CrateResponse {
834-
self.request
835-
.with_method(Method::Get)
836-
.with_path(&format!("/api/v1/crates/{}", krate_name));
837-
let mut response = self.make_request();
838-
json(&mut response)
839-
}
840-
841-
/// Yank the specified version of the specified crate.
842-
pub fn yank(&mut self, krate_name: &str, version: &str) -> conduit::Response {
843-
self.request
844-
.with_path(&format!("/api/v1/crates/{}/{}/yank", krate_name, version))
845-
.with_method(Method::Delete);
846-
self.make_request()
847-
}
848-
849-
/// Add a user as an owner for a crate.
850-
pub fn add_owner(&mut self, krate_name: &str, user: &User) {
851-
let body = format!("{{\"users\":[\"{}\"]}}", user.gh_login);
852-
self.request
853-
.with_path(&format!("/api/v1/crates/{}/owners", krate_name))
854-
.with_method(Method::Put)
855-
.with_body(body.as_bytes());
856-
857-
let mut response = self.make_request();
858-
859-
#[derive(Deserialize)]
860-
struct O {
861-
ok: bool,
862-
}
863-
assert!(json::<O>(&mut response).ok);
864-
}
865-
866-
/// As the currently logged in user, accept an invitation to become an owner of the named
867-
/// crate.
868-
pub fn accept_ownership_invitation(&mut self, krate_name: &str) {
869-
use views::InvitationResponse;
870-
871-
let krate_id = {
872-
let conn = self.app.diesel_database.get().unwrap();
873-
Crate::by_name(krate_name)
874-
.first::<Crate>(&*conn)
875-
.unwrap()
876-
.id
877-
};
878-
879-
let body = json!({
880-
"crate_owner_invite": {
881-
"invited_by_username": "",
882-
"crate_name": krate_name,
883-
"crate_id": krate_id,
884-
"created_at": "",
885-
"accepted": true
886-
}
887-
});
888-
889-
self.request
890-
.with_path(&format!("/api/v1/me/crate_owner_invitations/{}", krate_id))
891-
.with_method(Method::Put)
892-
.with_body(body.to_string().as_bytes());
893-
894-
let mut response = self.make_request();
895-
896-
#[derive(Deserialize)]
897-
struct CrateOwnerInvitation {
898-
crate_owner_invitation: InvitationResponse,
899-
}
900-
901-
let crate_owner_invite = ::json::<CrateOwnerInvitation>(&mut response);
902-
assert!(crate_owner_invite.crate_owner_invitation.accepted);
903-
assert_eq!(crate_owner_invite.crate_owner_invitation.crate_id, krate_id);
904-
}
905-
906-
/// Get the crates owned by the specified user.
907-
pub fn crates_owned_by(&mut self, user: &User) -> CrateList {
908-
let query = format!("user_id={}", user.id);
909-
self.request
910-
.with_path("/api/v1/crates")
911-
.with_method(Method::Get)
912-
.with_query(&query);
913-
914-
let mut response = self.make_request();
915-
916-
json::<CrateList>(&mut response)
917-
}
918-
}
919-
920741
lazy_static! {
921742
static ref EMPTY_TARBALL_BYTES: Vec<u8> = {
922743
let mut empty_tarball = vec![];

0 commit comments

Comments
 (0)