Skip to content

Commit b37b34b

Browse files
chore:added a testcase for sqlx migrate add ... (#3352)
1 parent 543395d commit b37b34b

File tree

5 files changed

+275
-2
lines changed

5 files changed

+275
-2
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ url = "2.2.2"
176176
rand = "0.8.4"
177177
rand_xoshiro = "0.6.0"
178178
hex = "0.4.3"
179-
tempfile = "3.9.0"
179+
tempfile = "3.10.1"
180180
criterion = { version = "0.5.1", features = ["async_tokio"] }
181181

182182
# If this is an unconditional dev-dependency then Cargo will *always* try to build `libsqlite3-sys`,

sqlx-cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,4 @@ completions = ["dep:clap_complete"]
6767

6868
[dev-dependencies]
6969
assert_cmd = "2.0.11"
70+
tempfile = "3.10.1"

sqlx-cli/tests/add.rs

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
use assert_cmd::Command;
2+
use std::cmp::Ordering;
3+
use std::fs::read_dir;
4+
use std::path::{Path, PathBuf};
5+
use tempfile::TempDir;
6+
7+
#[test]
8+
fn add_migration_ambiguous() -> anyhow::Result<()> {
9+
for reversible in [true, false] {
10+
let files = AddMigrations::new()?
11+
.run("hello world", reversible, true, true, false)?
12+
.fs_output()?;
13+
assert_eq!(files.0, Vec::<FileName>::new());
14+
}
15+
Ok(())
16+
}
17+
18+
#[derive(Debug, PartialEq, Eq)]
19+
struct FileName {
20+
id: usize,
21+
description: String,
22+
suffix: String,
23+
}
24+
25+
impl PartialOrd<Self> for FileName {
26+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
27+
if self.id != other.id {
28+
self.id.partial_cmp(&other.id)
29+
} else {
30+
self.suffix.partial_cmp(&other.suffix)
31+
}
32+
}
33+
}
34+
35+
impl FileName {
36+
fn assert_is_timestamp(&self) {
37+
//if the library is still used in 2050, this will need bumping ^^
38+
assert!(
39+
self.id < 20500101000000,
40+
"{self:?} is too high for a timestamp"
41+
);
42+
assert!(
43+
self.id > 20200101000000,
44+
"{self:?} is too low for a timestamp"
45+
);
46+
}
47+
}
48+
49+
impl From<PathBuf> for FileName {
50+
fn from(path: PathBuf) -> Self {
51+
let filename = path.file_name().unwrap().to_string_lossy();
52+
let (id, rest) = filename.split_once("_").unwrap();
53+
let id: usize = id.parse().unwrap();
54+
let (description, suffix) = rest.split_once(".").unwrap();
55+
Self {
56+
id,
57+
description: description.to_string(),
58+
suffix: suffix.to_string(),
59+
}
60+
}
61+
}
62+
#[test]
63+
fn add_migration_sequential() -> anyhow::Result<()> {
64+
{
65+
let files = AddMigrations::new()?
66+
.run("hello world", false, false, true, true)?
67+
.fs_output()?;
68+
assert_eq!(files.len(), 1);
69+
files.assert_is_not_reversible();
70+
assert_eq!(files.0[0].id, 1);
71+
}
72+
{
73+
let files = AddMigrations::new()?
74+
.run("hello world1", false, false, true, true)?
75+
.run("hello world2", true, false, true, true)?
76+
.fs_output()?;
77+
assert_eq!(files.len(), 2);
78+
files.assert_is_not_reversible();
79+
assert_eq!(files.0[0].id, 1);
80+
assert_eq!(files.0[1].id, 2);
81+
}
82+
Ok(())
83+
}
84+
#[test]
85+
fn add_migration_sequential_reversible() -> anyhow::Result<()> {
86+
{
87+
let files = AddMigrations::new()?
88+
.run("hello world", true, false, true, true)?
89+
.fs_output()?;
90+
assert_eq!(files.len(), 2);
91+
files.assert_is_reversible();
92+
assert_eq!(files.0[0].id, 1);
93+
assert_eq!(files.0[0].id, 1);
94+
}
95+
{
96+
let files = AddMigrations::new()?
97+
.run("hello world1", true, false, true, true)?
98+
.run("hello world2", true, true, false, true)?
99+
.run("hello world3", true, false, true, true)?
100+
.fs_output()?;
101+
assert_eq!(files.len(), 6);
102+
files.assert_is_reversible();
103+
assert_eq!(files.0[0].id, 1);
104+
assert_eq!(files.0[1].id, 1);
105+
// sequential -> timestamp is one way
106+
files.0[2].assert_is_timestamp();
107+
files.0[3].assert_is_timestamp();
108+
files.0[4].assert_is_timestamp();
109+
files.0[5].assert_is_timestamp();
110+
}
111+
Ok(())
112+
}
113+
114+
#[test]
115+
fn add_migration_timestamp() -> anyhow::Result<()> {
116+
{
117+
let files = AddMigrations::new()?
118+
.run("hello world", false, true, false, true)?
119+
.fs_output()?;
120+
assert_eq!(files.len(), 1);
121+
files.assert_is_not_reversible();
122+
files.0[0].assert_is_timestamp();
123+
}
124+
{
125+
let files = AddMigrations::new()?
126+
.run("hello world1", false, true, false, true)?
127+
.run("hello world2", true, false, true, true)?
128+
.fs_output()?;
129+
assert_eq!(files.len(), 2);
130+
files.assert_is_not_reversible();
131+
files.0[0].assert_is_timestamp();
132+
// sequential -> timestamp is one way
133+
files.0[1].assert_is_timestamp();
134+
}
135+
Ok(())
136+
}
137+
#[test]
138+
fn add_migration_timestamp_reversible() -> anyhow::Result<()> {
139+
{
140+
let files = AddMigrations::new()?
141+
.run("hello world", true, false, false, true)?
142+
.fs_output()?;
143+
assert_eq!(files.len(), 2);
144+
files.assert_is_reversible();
145+
files.0[0].assert_is_timestamp();
146+
files.0[1].assert_is_timestamp();
147+
}
148+
{
149+
let files = AddMigrations::new()?
150+
.run("hello world", true, true, false, true)?
151+
.fs_output()?;
152+
assert_eq!(files.len(), 2);
153+
files.assert_is_reversible();
154+
files.0[0].assert_is_timestamp();
155+
files.0[1].assert_is_timestamp();
156+
}
157+
{
158+
let files = AddMigrations::new()?
159+
.run("hello world1", true, true, false, true)?
160+
.run("hello world2", true, false, true, true)?
161+
.fs_output()?;
162+
assert_eq!(files.len(), 4);
163+
files.assert_is_reversible();
164+
files.0[0].assert_is_timestamp();
165+
files.0[1].assert_is_timestamp();
166+
files.0[2].assert_is_timestamp();
167+
files.0[3].assert_is_timestamp();
168+
}
169+
Ok(())
170+
}
171+
172+
struct AddMigrationsResult(Vec<FileName>);
173+
impl AddMigrationsResult {
174+
fn len(&self) -> usize {
175+
self.0.len()
176+
}
177+
fn assert_is_reversible(&self) {
178+
let mut up_cnt = 0;
179+
let mut down_cnt = 0;
180+
for file in self.0.iter() {
181+
if file.suffix == "down.sql" {
182+
down_cnt += 1;
183+
} else if file.suffix == "up.sql" {
184+
up_cnt += 1;
185+
} else {
186+
panic!("unknown suffix for {file:?}");
187+
}
188+
assert!(file.description.starts_with("hello_world"));
189+
}
190+
assert_eq!(up_cnt, down_cnt);
191+
}
192+
fn assert_is_not_reversible(&self) {
193+
for file in self.0.iter() {
194+
assert_eq!(file.suffix, "sql");
195+
assert!(file.description.starts_with("hello_world"));
196+
}
197+
}
198+
}
199+
struct AddMigrations(TempDir);
200+
201+
impl AddMigrations {
202+
fn new() -> anyhow::Result<Self> {
203+
anyhow::Ok(Self(TempDir::new()?))
204+
}
205+
fn run(
206+
self,
207+
description: &str,
208+
revesible: bool,
209+
timestamp: bool,
210+
sequential: bool,
211+
expect_success: bool,
212+
) -> anyhow::Result<Self> {
213+
let cmd_result = Command::cargo_bin("cargo-sqlx")?
214+
.current_dir(&self.0)
215+
.args(
216+
[
217+
vec!["sqlx", "migrate", "add", description],
218+
match revesible {
219+
true => vec!["-r"],
220+
false => vec![],
221+
},
222+
match timestamp {
223+
true => vec!["--timestamp"],
224+
false => vec![],
225+
},
226+
match sequential {
227+
true => vec!["--sequential"],
228+
false => vec![],
229+
},
230+
]
231+
.concat(),
232+
)
233+
.assert();
234+
if expect_success {
235+
cmd_result.success();
236+
} else {
237+
cmd_result.failure();
238+
}
239+
anyhow::Ok(self)
240+
}
241+
fn fs_output(&self) -> anyhow::Result<AddMigrationsResult> {
242+
let files = recurse_files(&self.0)?;
243+
let mut fs_paths = Vec::with_capacity(files.len());
244+
for path in files {
245+
let relative_path = path.strip_prefix(self.0.path())?.to_path_buf();
246+
fs_paths.push(FileName::from(relative_path));
247+
}
248+
Ok(AddMigrationsResult(fs_paths))
249+
}
250+
}
251+
252+
fn recurse_files(path: impl AsRef<Path>) -> anyhow::Result<Vec<PathBuf>> {
253+
let mut buf = vec![];
254+
let entries = read_dir(path)?;
255+
256+
for entry in entries {
257+
let entry = entry?;
258+
let meta = entry.metadata()?;
259+
260+
if meta.is_dir() {
261+
let mut subdir = recurse_files(entry.path())?;
262+
buf.append(&mut subdir);
263+
}
264+
265+
if meta.is_file() {
266+
buf.push(entry.path());
267+
}
268+
}
269+
buf.sort();
270+
Ok(buf)
271+
}

sqlx-macros-core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,6 @@ serde = { version = "1.0.132", features = ["derive"] }
5959
serde_json = { version = "1.0.73" }
6060
sha2 = { version = "0.10.0" }
6161
syn = { version = "2.0.52", default-features = false, features = ["full", "derive", "parsing", "printing", "clone-impls"] }
62-
tempfile = { version = "3.3.0" }
62+
tempfile = { version = "3.10.1" }
6363
quote = { version = "1.0.26", default-features = false }
6464
url = { version = "2.2.2", default-features = false }

0 commit comments

Comments
 (0)