1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.migration;
17
18 import static org.junit.jupiter.api.Assertions.assertEquals;
19 import static org.junit.jupiter.api.Assertions.assertFalse;
20 import static org.junit.jupiter.api.Assertions.assertNotEquals;
21 import static org.junit.jupiter.api.Assertions.assertNotNull;
22 import static org.junit.jupiter.api.Assertions.assertTrue;
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.PrintWriter;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.nio.file.StandardCopyOption;
30 import java.sql.Connection;
31 import java.sql.ResultSet;
32 import java.sql.Statement;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.List;
36 import java.util.Properties;
37 import java.util.Scanner;
38 import java.util.TreeSet;
39
40 import org.apache.ibatis.migration.io.Resources;
41 import org.apache.ibatis.migration.utils.TestUtil;
42 import org.junit.jupiter.api.BeforeAll;
43 import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
44 import org.junit.jupiter.api.Order;
45 import org.junit.jupiter.api.Test;
46 import org.junit.jupiter.api.TestMethodOrder;
47 import org.junit.jupiter.api.condition.EnabledForJreRange;
48
49 import uk.org.webcompere.systemstubs.SystemStubs;
50
51 @TestMethodOrder(OrderAnnotation.class)
52 class MigratorTest {
53
54 private static File dir;
55
56 private static Properties env;
57
58 @BeforeAll
59 static void setup() throws IOException {
60 dir = Resources.getResourceAsFile("org/apache/ibatis/migration/example");
61 env = Resources.getResourceAsProperties("org/apache/ibatis/migration/example/environments/development.properties");
62 }
63
64 @Test
65 @Order(1)
66 void testBootstrapCommand() throws Exception {
67 String output = SystemStubs.tapSystemOut(() -> {
68 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "bootstrap", "--env=development"));
69 });
70 assertFalse(output.contains("FAILURE"));
71 assertTrue(output.contains("-- // Bootstrap.sql"));
72 }
73
74 @Test
75 @Order(2)
76 void testStatusContainsNoPendingEntriesUsingStatusShorthand() throws Exception {
77 String output = SystemStubs.tapSystemOut(() -> {
78 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "sta"));
79 });
80 assertFalse(output.contains("FAILURE"));
81 assertTrue(output.contains("...pending..."));
82 }
83
84 @Test
85 @Order(3)
86 void testUpCommandWithSpecifiedSteps() throws Exception {
87 String output = SystemStubs.tapSystemOut(() -> {
88 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "up", "3000"));
89 });
90 assertFalse(output.contains("FAILURE"));
91 }
92
93 @Test
94 @Order(4)
95 void assertAuthorEmailContainsPlaceholder() throws Exception {
96 try (Connection conn = TestUtil.getConnection(env); Statement stmt = conn.createStatement();
97 ResultSet rs = stmt.executeQuery("select EMAIL from author where id = 1")) {
98 assertTrue(rs.next());
99 assertEquals("jim@${url}", rs.getString("EMAIL"));
100 }
101 }
102
103 @Test
104 @Order(5)
105 void testDownCommandGiven2Steps() throws Exception {
106 testStatusContainsNoPendingMigrations();
107 String output = SystemStubs.tapSystemOut(() -> {
108 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "down", "2"));
109 });
110 assertFalse(output.contains("FAILURE"));
111 testStatusContainsPendingMigrations();
112 }
113
114 @Test
115 @Order(6)
116 void testRedoCommand() throws Exception {
117 String output = SystemStubs.tapSystemOut(() -> {
118 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "status"));
119 });
120 assertFalse(output.contains("20080827200214 ...pending..."));
121 assertTrue(output.contains("20080827200216 ...pending..."));
122
123 output = SystemStubs.tapSystemOut(() -> {
124 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "redo"));
125 });
126 assertFalse(output.contains("FAILURE"));
127 assertEquals(-1, output.indexOf("DROP TABLE post_tag"), "Should down be just one step");
128 int dropIdx = output.indexOf("DROP TABLE comment");
129 int createIdx = output.indexOf("CREATE TABLE comment (");
130 assertNotEquals(-1, dropIdx);
131 assertNotEquals(-1, createIdx);
132 assertTrue(dropIdx < createIdx);
133
134 output = SystemStubs.tapSystemOut(() -> {
135 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "status"));
136 });
137 assertFalse(output.contains("20080827200214 ...pending..."));
138 assertTrue(output.contains("20080827200216 ...pending..."));
139
140 output = SystemStubs.tapSystemOut(() -> {
141 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "redo", "2"));
142 });
143 assertFalse(output.contains("FAILURE"));
144 assertEquals(-1, output.indexOf("DROP TABLE blog"), "Should down be two steps");
145 List<Integer> lineNums = new ArrayList<>();
146 lineNums.add(output.indexOf("DROP TABLE comment"));
147 lineNums.add(output.indexOf("DROP TABLE post"));
148 lineNums.add(output.indexOf("CREATE TABLE post ("));
149 lineNums.add(output.indexOf("CREATE TABLE comment ("));
150 assertEquals(new TreeSet<>(lineNums).toString(), lineNums.toString());
151
152 output = SystemStubs.tapSystemOut(() -> {
153 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "status"));
154 });
155 assertFalse(output.contains("20080827200214 ...pending..."));
156 assertTrue(output.contains("20080827200216 ...pending..."));
157 }
158
159 @Test
160 @Order(7)
161 void testDoPendingScriptCommand() throws Exception {
162 String output = SystemStubs.tapSystemOut(() -> {
163 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "script", "pending"));
164 });
165 assertTrue(output.contains("INSERT"));
166 assertTrue(output.contains("CHANGELOG"));
167 assertFalse(output.contains("-- @UNDO"));
168
169 output = SystemStubs.tapSystemOut(() -> {
170 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "script", "pending_undo"));
171 });
172 assertTrue(output.contains("DELETE"));
173 assertTrue(output.contains("CHANGELOG"));
174 assertTrue(output.contains("-- @UNDO"));
175 }
176
177 @Test
178 @Order(8)
179 void testVersionCommand() throws Exception {
180 String output = SystemStubs.tapSystemOut(() -> {
181 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "version", "20080827200217"));
182 });
183 assertFalse(output.contains("FAILURE"));
184 }
185
186 @Test
187 @Order(9)
188 void testSkippedScript() throws Exception {
189 testStatusContainsNoPendingMigrations();
190 File skipped = Path.of(dir.getCanonicalPath(), "scripts", "20080827200215_skipped_migration.sql").toFile();
191 assertTrue(skipped.createNewFile());
192 try {
193 String output = SystemStubs.tapSystemOut(() -> {
194 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "up"));
195 });
196 assertFalse(output.contains("FAILURE"));
197 assertEquals(1, TestUtil.countStr(output, "WARNING"));
198 assertTrue(output.contains(
199 "WARNING: Migration script '20080827200215_skipped_migration.sql' was not applied to the database."));
200 } finally {
201 skipped.delete();
202 }
203 }
204
205 @Test
206 @Order(10)
207 void testMissingScript() throws Exception {
208 Path original = Path.of(dir + File.separator + "scripts", "20080827200216_create_procs.sql");
209 Path renamed = Path.of(dir + File.separator + "scripts", "20080827200216_create_procs._sql");
210 assertEquals(renamed, Files.move(original, renamed, StandardCopyOption.REPLACE_EXISTING));
211 try {
212 String output = SystemStubs.tapSystemOut(() -> {
213 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "up"));
214 });
215 assertFalse(output.contains("FAILURE"), "Output contains: \n" + output);
216 assertTrue(output.contains("WARNING: Missing migration script. id='20080827200216', description='create procs'."),
217 "Output contains: \n" + output);
218 } finally {
219 assertEquals(original, Files.move(renamed, original, StandardCopyOption.REPLACE_EXISTING));
220 }
221 }
222
223 @Test
224 @Order(11)
225 void testDownCommand() throws Exception {
226 String output = SystemStubs.tapSystemOut(() -> {
227 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "down"));
228 });
229 assertFalse(output.contains("FAILURE"));
230 testStatusContainsPendingMigrations();
231 }
232
233 @Test
234 @Order(12)
235 void testPendingCommand() throws Exception {
236 String output = SystemStubs.tapSystemOut(() -> {
237 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "pending"));
238 });
239 assertFalse(output.contains("FAILURE"));
240 testStatusContainsNoPendingMigrations();
241 }
242
243 @Test
244 @Order(13)
245 void testHelpCommand() throws Exception {
246 String output = SystemStubs.tapSystemOut(() -> {
247 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "--help"));
248 });
249 assertFalse(output.contains("FAILURE"));
250 assertTrue(output.contains("--help"));
251 }
252
253 @Test
254 @Order(14)
255 void testDoScriptCommand() throws Exception {
256 String output = SystemStubs.tapSystemOut(() -> {
257 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "script", "20080827200212", "20080827200214"));
258 });
259 assertFalse(output.contains("FAILURE"));
260 assertFalse(output.contains("20080827200210"));
261 assertFalse(output.contains("20080827200211"));
262 assertFalse(output.contains("20080827200212"));
263 assertTrue(output.contains("20080827200213"));
264 assertTrue(output.contains("20080827200214"));
265 assertFalse(output.contains("20080827200216"));
266 assertFalse(output.contains("-- @UNDO"));
267
268 output = SystemStubs.tapSystemOut(() -> {
269 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "script", "0", "20080827200211"));
270 });
271 assertFalse(output.contains("FAILURE"));
272 assertTrue(output.contains("20080827200210"));
273 assertTrue(output.contains("20080827200211"));
274 assertFalse(output.contains("20080827200212"));
275 assertFalse(output.contains("20080827200213"));
276 assertFalse(output.contains("20080827200214"));
277 assertFalse(output.contains("20080827200216"));
278 assertFalse(output.contains("-- @UNDO"));
279 }
280
281 @Test
282 @Order(15)
283 void testUndoScriptCommand() throws Exception {
284 String output = SystemStubs.tapSystemOut(() -> {
285 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "script", "20080827200216", "20080827200213"));
286 });
287 assertFalse(output.contains("FAILURE"));
288 assertFalse(output.contains("20080827200210"));
289 assertFalse(output.contains("20080827200211"));
290 assertFalse(output.contains("20080827200212"));
291 assertFalse(output.contains("20080827200213"));
292 assertTrue(output.contains("20080827200214"));
293 assertTrue(output.contains("20080827200216"));
294 assertTrue(output.contains("-- @UNDO"));
295
296 output = SystemStubs.tapSystemOut(() -> {
297 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "script", "20080827200211", "0"));
298 });
299 assertFalse(output.contains("FAILURE"));
300 assertTrue(output.contains("20080827200210"));
301 assertTrue(output.contains("20080827200211"));
302 assertFalse(output.contains("20080827200212"));
303 assertFalse(output.contains("20080827200213"));
304 assertFalse(output.contains("20080827200214"));
305 assertFalse(output.contains("20080827200216"));
306 assertFalse(output.contains("DELETE FROM CHANGELOG WHERE ID = 20080827200210;"));
307 assertTrue(output.contains("-- @UNDO"));
308 }
309
310 @EnabledForJreRange(maxVersion = 23)
311 @Test
312 void shouldScriptCommandFailIfSameVersion() throws Exception {
313 String output = SystemStubs.tapSystemOut(() -> {
314 int exitCode = SystemStubs.catchSystemExit(() -> {
315 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "script", "20080827200211", "20080827200211"));
316 });
317 assertEquals(1, exitCode);
318 });
319 assertTrue(output.contains("FAILURE"));
320 }
321
322 @Test
323 void shouldInitTempDirectory() throws Exception {
324 File basePath = TestUtil.getTempDir();
325 Migrator.main(TestUtil.args("--path=" + basePath.getAbsolutePath(), "init"));
326 assertNotNull(basePath.list());
327 assertEquals(4, basePath.list().length);
328 File scriptPath = Path.of(basePath.getCanonicalPath(), "scripts").toFile();
329 assertEquals(3, scriptPath.list().length);
330 Migrator.main(TestUtil.args("--path=" + basePath.getAbsolutePath(), "new", "test new migration"));
331 assertEquals(4, scriptPath.list().length);
332 assertTrue(TestUtil.deleteDirectory(basePath), "delete temp dir");
333 }
334
335 @Test
336 void shouldRespectIdPattern() throws Exception {
337 String idPattern = "000";
338 File basePath = TestUtil.getTempDir();
339 Migrator.main(TestUtil.args("--path=" + basePath.getAbsolutePath(), "--idpattern=" + idPattern, "init"));
340 File changelog = Path.of(basePath.getCanonicalPath(), "scripts", "001_create_changelog.sql").toFile();
341 assertTrue(changelog.exists());
342 Migrator.main(
343 TestUtil.args("--path=" + basePath.getAbsolutePath(), "--idpattern=" + idPattern, "new", "new migration"));
344 File newMigration = Path.of(basePath.getCanonicalPath(), "scripts", "003_new_migration.sql").toFile();
345 assertTrue(newMigration.exists());
346 assertTrue(TestUtil.deleteDirectory(basePath), "delete temp dir");
347 }
348
349 @Test
350 void useCustomTemplate() throws Exception {
351 String desc = "test new migration";
352 File basePath = TestUtil.getTempDir();
353 Migrator.main(TestUtil.args("--path=" + basePath.getAbsolutePath(), "init"));
354 assertNotNull(basePath.list());
355 assertEquals(4, basePath.list().length);
356 File scriptPath = Path.of(basePath.getCanonicalPath(), "scripts").toFile();
357 assertEquals(3, scriptPath.list().length);
358
359 File templatePath = File.createTempFile("customTemplate", "sql");
360 try (PrintWriter writer = new PrintWriter(templatePath)) {
361 writer.println("// ${description}");
362 }
363 Migrator.main(TestUtil.args("--path=" + basePath.getAbsolutePath(), "new", desc,
364 "--template=" + templatePath.getAbsolutePath()));
365 String[] scripts = scriptPath.list();
366 Arrays.sort(scripts);
367 assertEquals(4, scripts.length);
368 try (Scanner scanner = new Scanner(Path.of(scriptPath.getCanonicalPath(), scripts[scripts.length - 2]))) {
369 if (scanner.hasNextLine()) {
370 assertEquals("// " + desc, scanner.nextLine());
371 }
372 }
373 templatePath.delete();
374 assertTrue(TestUtil.deleteDirectory(basePath), "delete temp dir");
375 }
376
377 @Test
378 void useCustomTemplateWithNoValue() throws Exception {
379 File basePath = TestUtil.getTempDir();
380 Migrator.main(TestUtil.args("--path=" + basePath.getAbsolutePath(), "init"));
381 assertNotNull(basePath.list());
382 assertEquals(4, basePath.list().length);
383 File scriptPath = Path.of(basePath.getCanonicalPath(), "scripts").toFile();
384 assertEquals(3, scriptPath.list().length);
385
386 File templatePath = File.createTempFile("customTemplate", "sql");
387 templatePath.createNewFile();
388 Migrator.main(TestUtil.args("--path=" + basePath.getAbsolutePath(), "new", "test new migration", "--template="));
389 assertEquals(4, scriptPath.list().length);
390 templatePath.delete();
391 assertTrue(TestUtil.deleteDirectory(basePath), "delete temp dir");
392 }
393
394 @Test
395 void useCustomTemplateWithBadPath() throws Exception {
396 System.setProperty("migrationsHome", TestUtil.getTempDir().getAbsolutePath());
397 File basePath = TestUtil.getTempDir();
398 Migrator.main(TestUtil.args("--path=" + basePath.getAbsolutePath(), "init"));
399 assertNotNull(basePath.list());
400 assertEquals(4, basePath.list().length);
401 File scriptPath = Path.of(basePath.getCanonicalPath(), "scripts").toFile();
402 assertEquals(3, scriptPath.list().length);
403
404 String output = SystemStubs.tapSystemOut(() -> {
405 Migrator.main(TestUtil.args("--path=" + basePath.getAbsolutePath(), "new", "test new migration"));
406 });
407 assertEquals(4, scriptPath.list().length);
408 assertTrue(output
409 .contains("Your migrations configuration did not find your custom template. Using the default template."));
410 assertTrue(TestUtil.deleteDirectory(basePath), "delete temp dir");
411 }
412
413 @Test
414 void shouldSuppressOutputIfQuietOptionEnabled() throws Throwable {
415 System.setProperty("migrationsHome", TestUtil.getTempDir().getAbsolutePath());
416 File basePath = TestUtil.getTempDir();
417 String output = SystemStubs.tapSystemOut(() -> {
418 Migrator.main(TestUtil.args("--path=" + basePath.getAbsolutePath(), "--quiet", "init"));
419 });
420 assertFalse(output.contains("Initializing:"));
421 assertNotNull(basePath.list());
422 assertTrue(TestUtil.deleteDirectory(basePath), "delete temp dir");
423 }
424
425 @Test
426 void shouldColorizeSuccessOutputIfColorOptionEnabled() throws Throwable {
427 System.setProperty("migrationsHome", TestUtil.getTempDir().getAbsolutePath());
428 File basePath = TestUtil.getTempDir();
429 String output = SystemStubs.tapSystemOut(() -> {
430 Migrator.main(TestUtil.args("--path=" + basePath.getAbsolutePath(), "--color", "init"));
431 });
432 assertTrue(output.contains(ConsoleColors.GREEN + "SUCCESS"));
433 assertNotNull(basePath.list());
434 assertTrue(TestUtil.deleteDirectory(basePath), "delete temp dir");
435 }
436
437 @EnabledForJreRange(maxVersion = 23)
438 @Test
439 void shouldColorizeFailureOutputIfColorOptionEnabled() throws Throwable {
440 System.setProperty("migrationsHome", TestUtil.getTempDir().getAbsolutePath());
441 File basePath = TestUtil.getTempDir();
442 String output = SystemStubs.tapSystemOut(() -> {
443 int exitCode = SystemStubs.catchSystemExit(() -> {
444 Migrator.main(TestUtil.args("--path=" + basePath.getAbsolutePath(), "--color", "new"));
445 });
446 assertEquals(1, exitCode);
447 });
448 assertTrue(output.contains(ConsoleColors.RED + "FAILURE"));
449 assertTrue(TestUtil.deleteDirectory(basePath), "delete temp dir");
450 }
451
452 @EnabledForJreRange(maxVersion = 23)
453 @Test
454 void shouldShowErrorOnMissingChangelog() throws Throwable {
455
456 try {
457 System.setProperty("migrations_changelog", "changelog1");
458 String output = SystemStubs.tapSystemOut(() -> {
459 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "up", "1"));
460 });
461 assertFalse(output.contains("FAILURE"));
462
463 System.setProperty("migrations_changelog", "changelog2");
464 output = SystemStubs.tapSystemOut(() -> {
465 int exitCode = SystemStubs.catchSystemExit(() -> {
466 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "pending"));
467 });
468 assertEquals(1, exitCode);
469 });
470 assertTrue(output.contains("FAILURE"));
471 assertTrue(output.contains("Change log doesn't exist, no migrations applied. Try running 'up' instead."));
472 } finally {
473 System.clearProperty("migrations_changelog");
474 }
475 }
476
477 @Test
478 void testInfoCommand() throws Exception {
479 String output = SystemStubs.tapSystemOut(() -> {
480 Migrator.main(TestUtil.args("info"));
481 });
482 assertFalse(output.contains("null"), output);
483 }
484
485 @Test
486 void testInfoWithNonExistentBasePath() throws Exception {
487 File baseDir = TestUtil.getTempDir();
488 assertTrue(baseDir.delete());
489 assertFalse(baseDir.exists(), "directory does not exist");
490 String output = SystemStubs.tapSystemOut(() -> {
491 Migrator.main(TestUtil.args("info", "--path=" + baseDir.getAbsolutePath()));
492 });
493 assertFalse(output.contains("Migrations path must be a directory"), "base path not required for info");
494 assertFalse(output.contains("null"), output);
495 }
496
497 @Test
498 void testInitWithNonExistentBasePath() throws Exception {
499 File baseDir = TestUtil.getTempDir();
500 assertTrue(baseDir.delete());
501 assertFalse(baseDir.exists(), "directory does not exist");
502 String output = SystemStubs
503 .tapSystemOut(() -> Migrator.main(TestUtil.args("init", "--path=" + baseDir.getAbsolutePath())));
504 assertFalse(output.contains("Migrations path must be a directory"), output);
505 assertTrue(Files.exists(Path.of(baseDir.getCanonicalPath(), "README")), "README created");
506 assertTrue(Files.isDirectory(Path.of(baseDir.getCanonicalPath(), "environments")),
507 "environments directory created");
508 assertTrue(TestUtil.deleteDirectory(baseDir), "delete temp dir");
509 }
510
511 private void testStatusContainsPendingMigrations() throws Exception {
512 String output = SystemStubs.tapSystemOut(() -> {
513 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "status"));
514 });
515 assertFalse(output.contains("FAILURE"));
516 assertTrue(output.contains("...pending..."));
517 }
518
519 private void testStatusContainsNoPendingMigrations() throws Exception {
520 String output = SystemStubs.tapSystemOut(() -> {
521 Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "status"));
522 });
523 assertFalse(output.contains("FAILURE"));
524 assertFalse(output.contains("...pending..."));
525 }
526
527 }