Skip to content

Commit 45040bf

Browse files
authored
Merge pull request #10040 from Turbo87/async-test-builders
tests/builders: Migrate to async/await
2 parents 0dd4493 + ab87ead commit 45040bf

Some content is hidden

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

53 files changed

+1228
-559
lines changed

src/index.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,7 @@ mod tests {
145145
#[tokio::test]
146146
async fn test_index_metadata() {
147147
let test_db = TestDatabase::new();
148-
let mut conn = test_db.connect();
149-
let mut async_conn = test_db.async_connect().await;
148+
let mut conn = test_db.async_connect().await;
150149

151150
let user_id = diesel::insert_into(users::table)
152151
.values((
@@ -156,7 +155,7 @@ mod tests {
156155
users::gh_access_token.eq("some random token"),
157156
))
158157
.returning(users::id)
159-
.get_result::<i32>(&mut async_conn)
158+
.get_result::<i32>(&mut conn)
160159
.await
161160
.unwrap();
162161

@@ -172,9 +171,10 @@ mod tests {
172171

173172
let fooo = CrateBuilder::new("foo", user_id)
174173
.version(VersionBuilder::new("0.1.0"))
175-
.expect_build(&mut conn);
174+
.expect_build(&mut conn)
175+
.await;
176176

177-
let metadata = index_metadata(&fooo, &mut async_conn).await.unwrap();
177+
let metadata = index_metadata(&fooo, &mut conn).await.unwrap();
178178
assert_json_snapshot!(metadata);
179179

180180
let bar = CrateBuilder::new("bar", user_id)
@@ -190,9 +190,10 @@ mod tests {
190190
.dependency(&fooo, None),
191191
)
192192
.version(VersionBuilder::new("1.0.1").checksum("0123456789abcdef"))
193-
.expect_build(&mut conn);
193+
.expect_build(&mut conn)
194+
.await;
194195

195-
let metadata = index_metadata(&bar, &mut async_conn).await.unwrap();
196+
let metadata = index_metadata(&bar, &mut conn).await.unwrap();
196197
assert_json_snapshot!(metadata);
197198
}
198199
}

src/models/category.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use diesel::{
33
delete, dsl, insert_into, sql_query, ExpressionMethods, QueryDsl, QueryResult,
44
TextExpressionMethods,
55
};
6-
use diesel_async::AsyncPgConnection;
6+
use diesel_async::scoped_futures::ScopedFutureExt;
7+
use diesel_async::{AsyncConnection, AsyncPgConnection};
78

89
use crate::models::Crate;
910
use crate::schema::*;
@@ -46,6 +47,50 @@ impl Category {
4647
categories::table.filter(filter)
4748
}
4849

50+
pub async fn async_update_crate(
51+
conn: &mut AsyncPgConnection,
52+
crate_id: i32,
53+
slugs: &[&str],
54+
) -> QueryResult<Vec<String>> {
55+
use diesel_async::RunQueryDsl;
56+
conn.transaction(|conn| {
57+
async move {
58+
let categories: Vec<Category> = categories::table
59+
.filter(categories::slug.eq_any(slugs))
60+
.load(conn)
61+
.await?;
62+
63+
let invalid_categories = slugs
64+
.iter()
65+
.filter(|s| !categories.iter().any(|c| c.slug == **s))
66+
.map(ToString::to_string)
67+
.collect();
68+
69+
let crate_categories = categories
70+
.iter()
71+
.map(|c| CrateCategory {
72+
category_id: c.id,
73+
crate_id,
74+
})
75+
.collect::<Vec<_>>();
76+
77+
delete(crates_categories::table)
78+
.filter(crates_categories::crate_id.eq(crate_id))
79+
.execute(conn)
80+
.await?;
81+
82+
insert_into(crates_categories::table)
83+
.values(&crate_categories)
84+
.execute(conn)
85+
.await?;
86+
87+
Ok(invalid_categories)
88+
}
89+
.scope_boxed()
90+
})
91+
.await
92+
}
93+
4994
pub fn update_crate(
5095
conn: &mut impl Conn,
5196
crate_id: i32,

src/models/keyword.rs

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use chrono::NaiveDateTime;
2-
use diesel_async::AsyncPgConnection;
2+
use diesel_async::scoped_futures::ScopedFutureExt;
3+
use diesel_async::{AsyncConnection, AsyncPgConnection};
34

45
use crate::models::Crate;
56
use crate::schema::*;
@@ -38,6 +39,31 @@ impl Keyword {
3839
.await
3940
}
4041

42+
pub async fn async_find_or_create_all(
43+
conn: &mut AsyncPgConnection,
44+
names: &[&str],
45+
) -> QueryResult<Vec<Keyword>> {
46+
use diesel_async::RunQueryDsl;
47+
48+
let lowercase_names: Vec<_> = names.iter().map(|s| s.to_lowercase()).collect();
49+
50+
let new_keywords: Vec<_> = lowercase_names
51+
.iter()
52+
.map(|s| keywords::keyword.eq(s))
53+
.collect();
54+
55+
diesel::insert_into(keywords::table)
56+
.values(&new_keywords)
57+
.on_conflict_do_nothing()
58+
.execute(conn)
59+
.await?;
60+
61+
keywords::table
62+
.filter(keywords::keyword.eq_any(&lowercase_names))
63+
.load(conn)
64+
.await
65+
}
66+
4167
pub fn find_or_create_all(conn: &mut impl Conn, names: &[&str]) -> QueryResult<Vec<Keyword>> {
4268
use diesel::RunQueryDsl;
4369

@@ -67,6 +93,42 @@ impl Keyword {
6793
&& chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '+')
6894
}
6995

96+
pub async fn async_update_crate(
97+
conn: &mut AsyncPgConnection,
98+
crate_id: i32,
99+
keywords: &[&str],
100+
) -> QueryResult<()> {
101+
conn.transaction(|conn| {
102+
async move {
103+
use diesel_async::RunQueryDsl;
104+
105+
let keywords = Keyword::async_find_or_create_all(conn, keywords).await?;
106+
107+
diesel::delete(crates_keywords::table)
108+
.filter(crates_keywords::crate_id.eq(crate_id))
109+
.execute(conn)
110+
.await?;
111+
112+
let crate_keywords = keywords
113+
.into_iter()
114+
.map(|kw| CrateKeyword {
115+
crate_id,
116+
keyword_id: kw.id,
117+
})
118+
.collect::<Vec<_>>();
119+
120+
diesel::insert_into(crates_keywords::table)
121+
.values(&crate_keywords)
122+
.execute(conn)
123+
.await?;
124+
125+
Ok(())
126+
}
127+
.scope_boxed()
128+
})
129+
.await
130+
}
131+
70132
pub fn update_crate(conn: &mut impl Conn, crate_id: i32, keywords: &[&str]) -> QueryResult<()> {
71133
conn.transaction(|conn| {
72134
use diesel::RunQueryDsl;
@@ -99,23 +161,27 @@ mod tests {
99161
use super::*;
100162
use crates_io_test_db::TestDatabase;
101163

102-
#[test]
103-
fn dont_associate_with_non_lowercased_keywords() {
104-
use diesel::RunQueryDsl;
164+
#[tokio::test]
165+
#[allow(clippy::iter_next_slice)]
166+
async fn dont_associate_with_non_lowercased_keywords() {
167+
use diesel_async::RunQueryDsl;
105168

106169
let test_db = TestDatabase::new();
107-
let conn = &mut test_db.connect();
170+
let mut conn = test_db.async_connect().await;
108171

109172
// The code should be preventing lowercased keywords from existing,
110173
// but if one happens to sneak in there, don't associate crates with it.
111174

112175
diesel::insert_into(keywords::table)
113176
.values(keywords::keyword.eq("NO"))
114-
.execute(conn)
177+
.execute(&mut conn)
178+
.await
115179
.unwrap();
116180

117-
let associated = Keyword::find_or_create_all(conn, &["no"]).unwrap();
181+
let associated = Keyword::async_find_or_create_all(&mut conn, &["no"])
182+
.await
183+
.unwrap();
118184
assert_eq!(associated.len(), 1);
119-
assert_eq!(associated.first().unwrap().keyword, "no");
185+
assert_eq!(associated.iter().next().unwrap().keyword, "no");
120186
}
121187
}

src/models/krate.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use diesel::associations::Identifiable;
33
use diesel::dsl;
44
use diesel::pg::Pg;
55
use diesel::sql_types::{Bool, Integer, Text};
6-
use diesel_async::AsyncPgConnection;
6+
use diesel_async::scoped_futures::ScopedFutureExt;
7+
use diesel_async::{AsyncConnection, AsyncPgConnection};
78
use secrecy::SecretString;
89
use thiserror::Error;
910

@@ -124,6 +125,42 @@ impl<'a> NewCrate<'a> {
124125
.get_result(conn)
125126
}
126127

128+
pub async fn async_create(
129+
&self,
130+
conn: &mut AsyncPgConnection,
131+
user_id: i32,
132+
) -> QueryResult<Crate> {
133+
use diesel_async::RunQueryDsl;
134+
135+
conn.transaction(|conn| {
136+
async move {
137+
let krate: Crate = diesel::insert_into(crates::table)
138+
.values(self)
139+
.on_conflict_do_nothing()
140+
.returning(Crate::as_returning())
141+
.get_result(conn)
142+
.await?;
143+
144+
let owner = CrateOwner {
145+
crate_id: krate.id,
146+
owner_id: user_id,
147+
created_by: user_id,
148+
owner_kind: OwnerKind::User,
149+
email_notifications: true,
150+
};
151+
152+
diesel::insert_into(crate_owners::table)
153+
.values(&owner)
154+
.execute(conn)
155+
.await?;
156+
157+
Ok(krate)
158+
}
159+
.scope_boxed()
160+
})
161+
.await
162+
}
163+
127164
pub fn create(&self, conn: &mut impl Conn, user_id: i32) -> QueryResult<Crate> {
128165
use diesel::RunQueryDsl;
129166

src/models/team.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@ pub struct NewTeam<'a> {
4747
}
4848

4949
impl<'a> NewTeam<'a> {
50+
pub async fn async_create_or_update(&self, conn: &mut AsyncPgConnection) -> QueryResult<Team> {
51+
use diesel::insert_into;
52+
use diesel_async::RunQueryDsl;
53+
54+
insert_into(teams::table)
55+
.values(self)
56+
.on_conflict(teams::github_id)
57+
.do_update()
58+
.set(self)
59+
.get_result(conn)
60+
.await
61+
}
62+
5063
pub fn create_or_update(&self, conn: &mut impl Conn) -> QueryResult<Team> {
5164
use diesel::insert_into;
5265
use diesel::RunQueryDsl;

src/models/version.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use std::collections::BTreeMap;
33
use bon::Builder;
44
use chrono::NaiveDateTime;
55
use crates_io_index::features::FeaturesMap;
6-
use diesel_async::AsyncPgConnection;
6+
use diesel_async::scoped_futures::ScopedFutureExt;
7+
use diesel_async::{AsyncConnection, AsyncPgConnection};
78
use serde::Deserialize;
89

910
use crate::models::{Crate, User};
@@ -117,6 +118,36 @@ impl NewVersion<'_> {
117118
Ok(version)
118119
})
119120
}
121+
122+
pub async fn async_save(
123+
&self,
124+
conn: &mut AsyncPgConnection,
125+
published_by_email: &str,
126+
) -> QueryResult<Version> {
127+
use diesel::insert_into;
128+
use diesel_async::RunQueryDsl;
129+
130+
conn.transaction(|conn| {
131+
async move {
132+
let version: Version = insert_into(versions::table)
133+
.values(self)
134+
.get_result(conn)
135+
.await?;
136+
137+
insert_into(versions_published_by::table)
138+
.values((
139+
versions_published_by::version_id.eq(version.id),
140+
versions_published_by::email.eq(published_by_email),
141+
))
142+
.execute(conn)
143+
.await?;
144+
145+
Ok(version)
146+
}
147+
.scope_boxed()
148+
})
149+
.await
150+
}
120151
}
121152

122153
fn strip_build_metadata(version: &str) -> &str {

src/tests/blocked_routes.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ async fn test_non_blocked_download_route() {
1111
.with_user()
1212
.await;
1313

14-
let mut conn = app.db_conn();
14+
let mut conn = app.async_db_conn().await;
1515

1616
CrateBuilder::new("foo", user.as_model().id)
1717
.version(VersionBuilder::new("1.0.0"))
18-
.expect_build(&mut conn);
18+
.expect_build(&mut conn)
19+
.await;
1920

2021
let status = anon
2122
.get::<()>("/api/v1/crates/foo/1.0.0/download")
@@ -36,11 +37,12 @@ async fn test_blocked_download_route() {
3637
.with_user()
3738
.await;
3839

39-
let mut conn = app.db_conn();
40+
let mut conn = app.async_db_conn().await;
4041

4142
CrateBuilder::new("foo", user.as_model().id)
4243
.version(VersionBuilder::new("1.0.0"))
43-
.expect_build(&mut conn);
44+
.expect_build(&mut conn)
45+
.await;
4446

4547
let status = anon
4648
.get::<()>("/api/v1/crates/foo/1.0.0/download")

0 commit comments

Comments
 (0)