Skip to content

Commit 841bd33

Browse files
committed
Break the tests up into a more logical structure
Due to limitations of the language, I still cannot have the tests live in another crate (unfortunate, as it means this is actually impossible to use in any context...) However, we can force a lot of the code changes (mostly namespacing in `macros`) by moving the tests out of `lib.rs`. The two blockers for usage in another crate are the impls for `SelectableColumn` created by the `joinable_inner!` macro (this constitutes a blanket impl for a non-crate-local type). I can define a generic implementation in the library once either rust-lang/rfcs#1268, or specialization lands. The second blocker is the impl for `Queriable` that is generated by `belongs_to!`, as `(A, B)` is not considered crate-local, even if both `A` and `B` are crate local. I can define a generic impl for this one, but only once specialization lands. (I'm not sure that I want to in this case, but it looks like I might not have a choice?) In fact, in the second one, I'm almost certain that I'd rather not have to define a generic type, as I will probably want to define a separate implementation depending on whether the relationship is one-to-many, one-to-one, and optional one-to-one. It also kinda sucks that these restrictions exist at all when the definition comes from a macro in the library's crate. However I understand that for coherence reasons, where the macro is defined makes absolutely no difference.
1 parent bdc020d commit 841bd33

File tree

8 files changed

+496
-480
lines changed

8 files changed

+496
-480
lines changed

src/lib.rs

+3-464
Large diffs are not rendered by default.

src/macros.rs

+22-16
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ macro_rules! table {
1616
$($column_name:ident -> $Type:ty,)+
1717
}
1818
) => {
19-
mod $name {
19+
pub mod $name {
2020
use $crate::{QuerySource, Table, Column};
2121
use $crate::types::*;
2222
pub use self::columns::*;
@@ -98,9 +98,9 @@ macro_rules! queriable {
9898
$($field_name:ident -> $Type:ty,)+
9999
}
100100
) => {
101-
impl<ST> Queriable<ST> for $Struct where
102-
ST: NativeSqlType,
103-
($($Type),+): types::FromSqlRow<ST>,
101+
impl<ST> $crate::Queriable<ST> for $Struct where
102+
ST: $crate::types::NativeSqlType,
103+
($($Type),+): $crate::types::FromSqlRow<ST>,
104104
{
105105
type Row = ($($Type),+);
106106

@@ -150,43 +150,49 @@ macro_rules! insertable {
150150

151151
macro_rules! joinable {
152152
($child:ident -> $parent:ident ($source:ident = $target:ident)) => {
153-
use query_source::{SelectableColumn, InnerJoinSource};
154-
155153
joinable_inner!($child -> $parent ($source = $target));
156154
joinable_inner!($parent -> $child ($target = $source));
157155
}
158156
}
159157

160158
macro_rules! joinable_inner {
161159
($child:ident -> $parent:ident ($source:ident = $target:ident)) => {
162-
impl JoinTo<$parent::table> for $child::table {
160+
impl $crate::JoinTo<$parent::table> for $child::table {
163161
fn join_sql(&self) -> String {
162+
use $crate::Column;
164163
format!("{} = {}", $child::$source.qualified_name(), $parent::$target.qualified_name())
165164
}
166165
}
167166

168-
impl<C> SelectableColumn<$parent::table, InnerJoinSource<$child::table, $parent::table>> for C where
169-
C: Column<$parent::table>,
167+
impl<C> $crate::query_source::SelectableColumn<
168+
$parent::table,
169+
$crate::query_source::InnerJoinSource<$child::table, $parent::table>
170+
> for C where
171+
C: $crate::Column<$parent::table>,
170172
{}
171173

172-
impl<C> SelectableColumn<$child::table, InnerJoinSource<$child::table, $parent::table>> for C where
173-
C: Column<$child::table>,
174+
impl<C> $crate::query_source::SelectableColumn<
175+
$child::table,
176+
$crate::query_source::InnerJoinSource<$child::table, $parent::table>
177+
> for C where
178+
C: $crate::Column<$child::table>,
174179
{}
175180
}
176181
}
177182

178183
macro_rules! belongs_to {
179184
($parent:ty, $parent_table:ident, $child:ty, $child_table:ident) => {
180-
impl Queriable<($child_table::SqlType, $parent_table::SqlType)> for ($child, $parent) {
185+
impl $crate::Queriable<($child_table::SqlType, $parent_table::SqlType)>
186+
for ($child, $parent) {
181187
type Row = (
182-
<$child as Queriable<$child_table::SqlType>>::Row,
183-
<$parent as Queriable<$parent_table::SqlType>>::Row,
188+
<$child as $crate::Queriable<$child_table::SqlType>>::Row,
189+
<$parent as $crate::Queriable<$parent_table::SqlType>>::Row,
184190
);
185191

186192
fn build(row: Self::Row) -> Self {
187193
(
188-
<$child as Queriable<$child_table::SqlType>>::build(row.0),
189-
<$parent as Queriable<$parent_table::SqlType>>::build(row.1),
194+
<$child as $crate::Queriable<$child_table::SqlType>>::build(row.0),
195+
<$parent as $crate::Queriable<$parent_table::SqlType>>::build(row.1),
190196
)
191197
}
192198
}

src/tests/find.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use super::schema::*;
2+
3+
#[test]
4+
fn find() {
5+
use tests::schema::users::table as users;
6+
7+
let connection = connection();
8+
setup_users_table(&connection);
9+
10+
connection.execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')")
11+
.unwrap();
12+
13+
assert_eq!(Ok(Some(User::new(1, "Sean"))), connection.find(&users, &1));
14+
assert_eq!(Ok(Some(User::new(2, "Tess"))), connection.find(&users, &2));
15+
assert_eq!(Ok(None::<User>), connection.find(&users, &3));
16+
// This should fail type checking, and we should add a test to ensure
17+
// it continues to fail to compile.
18+
// connection.find(&users, &"1").unwrap();
19+
}
20+
21+
table! {
22+
users_with_name_pk (name) {
23+
name -> VarChar,
24+
}
25+
}
26+
27+
#[test]
28+
fn find_with_non_serial_pk() {
29+
use self::users_with_name_pk::table as users;
30+
31+
let connection = connection();
32+
connection.execute("CREATE TABLE users_with_name_pk (name VARCHAR PRIMARY KEY)")
33+
.unwrap();
34+
connection.execute("INSERT INTO users_with_name_pk (name) VALUES ('Sean'), ('Tess')")
35+
.unwrap();
36+
37+
assert_eq!(Ok(Some("Sean".to_string())), connection.find(&users, &"Sean"));
38+
assert_eq!(Ok(Some("Tess".to_string())), connection.find(&users, &"Tess".to_string()));
39+
assert_eq!(Ok(None::<String>), connection.find(&users, &"Wibble"));
40+
// This should fail type checking, and we should add a test to ensure
41+
// it continues to fail to compile.
42+
// connection.find(&users, &1).unwrap();
43+
}
44+

src/tests/insert.rs

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use super::schema::*;
2+
3+
#[test]
4+
fn insert_records() {
5+
use tests::schema::users::table as users;
6+
let connection = connection();
7+
setup_users_table(&connection);
8+
9+
let new_users = vec![
10+
NewUser::new("Sean", Some("Black")),
11+
NewUser::new("Tess", None),
12+
];
13+
let inserted_users: Vec<_> = connection.insert(&users, new_users).unwrap().collect();
14+
15+
let expected_users = vec![
16+
User { id: 1, name: "Sean".to_string(), hair_color: Some("Black".to_string()) },
17+
User { id: 2, name: "Tess".to_string(), hair_color: None },
18+
];
19+
let actual_users: Vec<_> = connection.query_all(&users).unwrap().collect();
20+
21+
assert_eq!(expected_users, actual_users);
22+
assert_eq!(expected_users, inserted_users);
23+
}
24+
25+
#[test]
26+
fn insert_with_defaults() {
27+
use tests::schema::users::table as users;
28+
let connection = connection();
29+
connection.execute("CREATE TABLE users (
30+
id SERIAL PRIMARY KEY,
31+
name VARCHAR NOT NULL,
32+
hair_color VARCHAR NOT NULL DEFAULT 'Green'
33+
)").unwrap();
34+
let new_users = vec![
35+
NewUser::new("Sean", Some("Black")),
36+
NewUser::new("Tess", None),
37+
];
38+
let inserted_users: Vec<_> = connection.insert(&users, new_users).unwrap().collect();
39+
40+
let expected_users = vec![
41+
User { id: 1, name: "Sean".to_string(), hair_color: Some("Black".to_string()) },
42+
User { id: 2, name: "Tess".to_string(), hair_color: Some("Green".to_string()) },
43+
];
44+
let actual_users: Vec<_> = connection.query_all(&users).unwrap().collect();
45+
46+
assert_eq!(expected_users, actual_users);
47+
assert_eq!(expected_users, inserted_users);
48+
}
49+
50+
#[test]
51+
fn insert_with_defaults_not_provided() {
52+
use tests::schema::users::table as users;
53+
let connection = connection();
54+
connection.execute("CREATE TABLE users (
55+
id SERIAL PRIMARY KEY,
56+
name VARCHAR NOT NULL,
57+
hair_color VARCHAR NOT NULL DEFAULT 'Green'
58+
)").unwrap();
59+
let new_users = vec![
60+
BaldUser { name: "Sean".to_string() },
61+
BaldUser { name: "Tess".to_string() },
62+
];
63+
let inserted_users: Vec<_> = connection.insert(&users, new_users).unwrap().collect();
64+
65+
let expected_users = vec![
66+
User { id: 1, name: "Sean".to_string(), hair_color: Some("Green".to_string()) },
67+
User { id: 2, name: "Tess".to_string(), hair_color: Some("Green".to_string()) },
68+
];
69+
let actual_users: Vec<_> = connection.query_all(&users).unwrap().collect();
70+
71+
assert_eq!(expected_users, actual_users);
72+
assert_eq!(expected_users, inserted_users);
73+
}
74+
75+
struct BaldUser {
76+
name: String,
77+
}
78+
79+
insertable! {
80+
BaldUser -> users {
81+
name -> String,
82+
}
83+
}

src/tests/joins.rs

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use super::schema::*;
2+
use {Table, QuerySource};
3+
4+
#[test]
5+
fn belongs_to() {
6+
let connection = connection();
7+
setup_users_table(&connection);
8+
setup_posts_table(&connection);
9+
10+
connection.execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')")
11+
.unwrap();
12+
connection.execute("INSERT INTO posts (user_id, title) VALUES
13+
(1, 'Hello'),
14+
(2, 'World')
15+
").unwrap();
16+
17+
let sean = User::new(1, "Sean");
18+
let tess = User::new(2, "Tess");
19+
let seans_post = Post { id: 1, user_id: 1, title: "Hello".to_string() };
20+
let tess_post = Post { id: 2, user_id: 2, title: "World".to_string() };
21+
22+
let expected_data = vec![(seans_post, sean), (tess_post, tess)];
23+
let source = posts::table.inner_join(users::table);
24+
let actual_data: Vec<_> = connection.query_all(&source).unwrap().collect();
25+
26+
assert_eq!(expected_data, actual_data);
27+
}
28+
29+
#[test]
30+
fn select_single_from_join() {
31+
let connection = connection();
32+
setup_users_table(&connection);
33+
setup_posts_table(&connection);
34+
35+
connection.execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')")
36+
.unwrap();
37+
connection.execute("INSERT INTO posts (user_id, title) VALUES
38+
(1, 'Hello'),
39+
(2, 'World')
40+
").unwrap();
41+
42+
let source = posts::table.inner_join(users::table);
43+
let select_name = source.select(users::name);
44+
let select_title = source.select(posts::title);
45+
46+
let expected_names = vec!["Sean".to_string(), "Tess".to_string()];
47+
let actual_names: Vec<String> = connection.query_all(&select_name).unwrap().collect();
48+
49+
assert_eq!(expected_names, actual_names);
50+
51+
let expected_titles = vec!["Hello".to_string(), "World".to_string()];
52+
let actual_titles: Vec<String> = connection.query_all(&select_title).unwrap().collect();
53+
54+
assert_eq!(expected_titles, actual_titles);
55+
}
56+
57+
#[test]
58+
fn select_multiple_from_join() {
59+
let connection = connection();
60+
setup_users_table(&connection);
61+
setup_posts_table(&connection);
62+
63+
connection.execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')")
64+
.unwrap();
65+
connection.execute("INSERT INTO posts (user_id, title) VALUES
66+
(1, 'Hello'),
67+
(2, 'World')
68+
").unwrap();
69+
70+
let source = posts::table.inner_join(users::table)
71+
.select((users::name, posts::title));
72+
73+
let expected_data = vec![
74+
("Sean".to_string(), "Hello".to_string()),
75+
("Tess".to_string(), "World".to_string()),
76+
];
77+
let actual_data: Vec<_> = connection.query_all(&source).unwrap().collect();
78+
79+
assert_eq!(expected_data, actual_data);
80+
}
81+
82+
#[test]
83+
fn select_only_one_side_of_join() {
84+
let connection = connection();
85+
setup_users_table(&connection);
86+
setup_posts_table(&connection);
87+
88+
connection.execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')")
89+
.unwrap();
90+
connection.execute("INSERT INTO posts (user_id, title) VALUES (2, 'Hello')")
91+
.unwrap();
92+
93+
let source = users::table.inner_join(posts::table).select(users::star);
94+
95+
let expected_data = vec![User::new(2, "Tess")];
96+
let actual_data: Vec<_> = connection.query_all(&source).unwrap().collect();
97+
98+
assert_eq!(expected_data, actual_data);
99+
}

src/tests/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mod find;
2+
mod insert;
3+
mod joins;
4+
mod schema;
5+
mod select;

0 commit comments

Comments
 (0)