A scripts/lua/migration/doc/index.html => scripts/lua/migration/doc/index.html +244 -0
@@ 0,0 1,244 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+<head>
+ <title>Reference</title>
+ <link rel="stylesheet" href="ldoc.css" type="text/css" />
+</head>
+<body>
+
+<div id="container">
+
+<div id="product">
+ <div id="product_logo"></div>
+ <div id="product_name"><big><b></b></big></div>
+ <div id="product_description"></div>
+</div> <!-- id="product" -->
+
+
+<div id="main">
+
+
+<!-- Menu -->
+
+<div id="navigation">
+<br/>
+<h1>ldoc</h1>
+
+
+<h2>Contents</h2>
+<ul>
+<li><a href="#Functions">Functions</a></li>
+<li><a href="#Tables">Tables</a></li>
+</ul>
+
+
+<h2>Modules</h2>
+<ul class="nowrap">
+ <li><strong>migration</strong></li>
+</ul>
+
+</div>
+
+<div id="content">
+
+<h1>Module <code>migration</code></h1>
+<p>Database migration scripts</p>
+<p></p>
+
+
+<h2><a href="#Functions">Functions</a></h2>
+<table class="function_list">
+ <tr>
+ <td class="name" nowrap><a href="#up">up (db_path, scripts, target_version)</a></td>
+ <td class="summary">Perform database migration from lower to higher version</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#down">down (db_path, scripts, target_version)</a></td>
+ <td class="summary">Perform database migration from higher to lower version</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#get_db_version">get_db_version (db_path)</a></td>
+ <td class="summary">Read the database version</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#migrate">migrate (db_dir, scripts_dir, db_set)</a></td>
+ <td class="summary">Perform databases migration(up/down) automatically</td>
+ </tr>
+</table>
+<h2><a href="#Tables">Tables</a></h2>
+<table class="function_list">
+ <tr>
+ <td class="name" nowrap><a href="#retcode">retcode</a></td>
+ <td class="summary">Return codes</td>
+ </tr>
+</table>
+
+<br/>
+<br/>
+
+
+ <h2 class="section-header "><a name="Functions"></a>Functions</h2>
+
+ <dl class="function">
+ <dt>
+ <a name = "up"></a>
+ <strong>up (db_path, scripts, target_version)</strong>
+ </dt>
+ <dd>
+ Perform database migration from lower to higher version
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">db_path</span>
+ path to the database we want to perform migration on
+ </li>
+ <li><span class="parameter">scripts</span>
+ array of {""}
+ </li>
+ <li><span class="parameter">target_version</span>
+ database version we want to migrate in to
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <a href="index.html#retcode">retcode</a>
+ </ol>
+
+
+
+
+</dd>
+ <dt>
+ <a name = "down"></a>
+ <strong>down (db_path, scripts, target_version)</strong>
+ </dt>
+ <dd>
+ Perform database migration from higher to lower version
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">db_path</span>
+ path to the database we want to perform migration on
+ </li>
+ <li><span class="parameter">scripts</span>
+ array of {""}
+ </li>
+ <li><span class="parameter">target_version</span>
+ database version we want to migrate in to
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <a href="index.html#retcode">retcode</a>
+ </ol>
+
+
+
+
+</dd>
+ <dt>
+ <a name = "get_db_version"></a>
+ <strong>get_db_version (db_path)</strong>
+ </dt>
+ <dd>
+ Read the database version
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">db_path</span>
+ path to the database
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ database version number
+ </ol>
+
+
+
+
+</dd>
+ <dt>
+ <a name = "migrate"></a>
+ <strong>migrate (db_dir, scripts_dir, db_set)</strong>
+ </dt>
+ <dd>
+ Perform databases migration(up/down) automatically
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">db_dir</span>
+ location of the databases
+ </li>
+ <li><span class="parameter">scripts_dir</span>
+ location of the database migration scripts
+ </li>
+ <li><span class="parameter">db_set</span>
+ array of {<"database_name"> = <db_target_version>} entries
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <a href="index.html#retcode">retcode</a>
+ </ol>
+
+
+
+
+</dd>
+</dl>
+ <h2 class="section-header "><a name="Tables"></a>Tables</h2>
+
+ <dl class="function">
+ <dt>
+ <a name = "retcode"></a>
+ <strong>retcode</strong>
+ </dt>
+ <dd>
+ Return codes
+
+
+ <h3>Fields:</h3>
+ <ul>
+ <li><span class="parameter">OK</span>
+
+ </li>
+ <li><span class="parameter">ALREADY_UP_TO_DATE</span>
+
+ </li>
+ <li><span class="parameter">WRONG_TARGET_VERSION</span>
+
+ </li>
+ </ul>
+
+
+
+
+
+</dd>
+</dl>
+
+
+</div> <!-- id="content" -->
+</div> <!-- id="main" -->
+<div id="about">
+<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
+<i style="float:right;">Last updated 2022-11-24 08:40:54 </i>
+</div> <!-- id="about" -->
+</div> <!-- id="container" -->
+</body>
+</html>
A scripts/lua/migration/doc/ldoc.css => scripts/lua/migration/doc/ldoc.css +303 -0
@@ 0,0 1,303 @@
+/* BEGIN RESET
+
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.8.2r1
+*/
+html {
+ color: #000;
+ background: #FFF;
+}
+body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td {
+ margin: 0;
+ padding: 0;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+fieldset,img {
+ border: 0;
+}
+address,caption,cite,code,dfn,em,strong,th,var,optgroup {
+ font-style: inherit;
+ font-weight: inherit;
+}
+del,ins {
+ text-decoration: none;
+}
+li {
+ margin-left: 20px;
+}
+caption,th {
+ text-align: left;
+}
+h1,h2,h3,h4,h5,h6 {
+ font-size: 100%;
+ font-weight: bold;
+}
+q:before,q:after {
+ content: '';
+}
+abbr,acronym {
+ border: 0;
+ font-variant: normal;
+}
+sup {
+ vertical-align: baseline;
+}
+sub {
+ vertical-align: baseline;
+}
+legend {
+ color: #000;
+}
+input,button,textarea,select,optgroup,option {
+ font-family: inherit;
+ font-size: inherit;
+ font-style: inherit;
+ font-weight: inherit;
+}
+input,button,textarea,select {*font-size:100%;
+}
+/* END RESET */
+
+body {
+ margin-left: 1em;
+ margin-right: 1em;
+ font-family: arial, helvetica, geneva, sans-serif;
+ background-color: #ffffff; margin: 0px;
+}
+
+code, tt { font-family: monospace; font-size: 1.1em; }
+span.parameter { font-family:monospace; }
+span.parameter:after { content:":"; }
+span.types:before { content:"("; }
+span.types:after { content:")"; }
+.type { font-weight: bold; font-style:italic }
+
+body, p, td, th { font-size: .95em; line-height: 1.2em;}
+
+p, ul { margin: 10px 0 0 0px;}
+
+strong { font-weight: bold;}
+
+em { font-style: italic;}
+
+h1 {
+ font-size: 1.5em;
+ margin: 20px 0 20px 0;
+}
+h2, h3, h4 { margin: 15px 0 10px 0; }
+h2 { font-size: 1.25em; }
+h3 { font-size: 1.15em; }
+h4 { font-size: 1.06em; }
+
+a:link { font-weight: bold; color: #004080; text-decoration: none; }
+a:visited { font-weight: bold; color: #006699; text-decoration: none; }
+a:link:hover { text-decoration: underline; }
+
+hr {
+ color:#cccccc;
+ background: #00007f;
+ height: 1px;
+}
+
+blockquote { margin-left: 3em; }
+
+ul { list-style-type: disc; }
+
+p.name {
+ font-family: "Andale Mono", monospace;
+ padding-top: 1em;
+}
+
+pre {
+ background-color: rgb(245, 245, 245);
+ border: 1px solid #C0C0C0; /* silver */
+ padding: 10px;
+ margin: 10px 0 10px 0;
+ overflow: auto;
+ font-family: "Andale Mono", monospace;
+}
+
+pre.example {
+ font-size: .85em;
+}
+
+table.index { border: 1px #00007f; }
+table.index td { text-align: left; vertical-align: top; }
+
+#container {
+ margin-left: 1em;
+ margin-right: 1em;
+ background-color: #f0f0f0;
+}
+
+#product {
+ text-align: center;
+ border-bottom: 1px solid #cccccc;
+ background-color: #ffffff;
+}
+
+#product big {
+ font-size: 2em;
+}
+
+#main {
+ background-color: #f0f0f0;
+ border-left: 2px solid #cccccc;
+}
+
+#navigation {
+ float: left;
+ width: 14em;
+ vertical-align: top;
+ background-color: #f0f0f0;
+ overflow: visible;
+}
+
+#navigation h2 {
+ background-color:#e7e7e7;
+ font-size:1.1em;
+ color:#000000;
+ text-align: left;
+ padding:0.2em;
+ border-top:1px solid #dddddd;
+ border-bottom:1px solid #dddddd;
+}
+
+#navigation ul
+{
+ font-size:1em;
+ list-style-type: none;
+ margin: 1px 1px 10px 1px;
+}
+
+#navigation li {
+ text-indent: -1em;
+ display: block;
+ margin: 3px 0px 0px 22px;
+}
+
+#navigation li li a {
+ margin: 0px 3px 0px -1em;
+}
+
+#content {
+ margin-left: 14em;
+ padding: 1em;
+ width: 700px;
+ border-left: 2px solid #cccccc;
+ border-right: 2px solid #cccccc;
+ background-color: #ffffff;
+}
+
+#about {
+ clear: both;
+ padding: 5px;
+ border-top: 2px solid #cccccc;
+ background-color: #ffffff;
+}
+
+@media print {
+ body {
+ font: 12pt "Times New Roman", "TimeNR", Times, serif;
+ }
+ a { font-weight: bold; color: #004080; text-decoration: underline; }
+
+ #main {
+ background-color: #ffffff;
+ border-left: 0px;
+ }
+
+ #container {
+ margin-left: 2%;
+ margin-right: 2%;
+ background-color: #ffffff;
+ }
+
+ #content {
+ padding: 1em;
+ background-color: #ffffff;
+ }
+
+ #navigation {
+ display: none;
+ }
+ pre.example {
+ font-family: "Andale Mono", monospace;
+ font-size: 10pt;
+ page-break-inside: avoid;
+ }
+}
+
+table.module_list {
+ border-width: 1px;
+ border-style: solid;
+ border-color: #cccccc;
+ border-collapse: collapse;
+}
+table.module_list td {
+ border-width: 1px;
+ padding: 3px;
+ border-style: solid;
+ border-color: #cccccc;
+}
+table.module_list td.name { background-color: #f0f0f0; min-width: 200px; }
+table.module_list td.summary { width: 100%; }
+
+
+table.function_list {
+ border-width: 1px;
+ border-style: solid;
+ border-color: #cccccc;
+ border-collapse: collapse;
+}
+table.function_list td {
+ border-width: 1px;
+ padding: 3px;
+ border-style: solid;
+ border-color: #cccccc;
+}
+table.function_list td.name { background-color: #f0f0f0; min-width: 200px; }
+table.function_list td.summary { width: 100%; }
+
+ul.nowrap {
+ overflow:auto;
+ white-space:nowrap;
+}
+
+dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
+dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
+dl.table h3, dl.function h3 {font-size: .95em;}
+
+/* stop sublists from having initial vertical space */
+ul ul { margin-top: 0px; }
+ol ul { margin-top: 0px; }
+ol ol { margin-top: 0px; }
+ul ol { margin-top: 0px; }
+
+/* make the target distinct; helps when we're navigating to a function */
+a:target + * {
+ background-color: #FF9;
+}
+
+
+/* styles for prettification of source */
+pre .comment { color: #558817; }
+pre .constant { color: #a8660d; }
+pre .escape { color: #844631; }
+pre .keyword { color: #aa5050; font-weight: bold; }
+pre .library { color: #0e7c6b; }
+pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
+pre .string { color: #8080ff; }
+pre .number { color: #f8660d; }
+pre .operator { color: #2239a8; font-weight: bold; }
+pre .preprocessor, pre .prepro { color: #a33243; }
+pre .global { color: #800080; }
+pre .user-keyword { color: #800080; }
+pre .prompt { color: #558817; }
+pre .url { color: #272fc2; text-decoration: underline; }
+
A scripts/lua/migration/migration.lua => scripts/lua/migration/migration.lua +169 -0
@@ 0,0 1,169 @@
+-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+--- Database migration scripts
+-- @module migration
+local sqlite3 = require("lsqlite3complete")
+local lfs = require("lfs")
+local helpers = require("helpers")
+
+local up_script = "up.sql"
+local down_script = "down.sql"
+
+migration = {}
+
+--- Return codes
+-- @table retcode
+migration.retcode = {
+ OK = 0,
+ ALREADY_UP_TO_DATE = 1,
+ WRONG_TARGET_VERSION = 2
+}
+
+local function build_script_array(path, db_name, filename)
+ local scripts = {}
+ for file in lfs.dir(path .. "/" .. db_name) do
+ if file ~= "." and file ~= ".." and tonumber(file) then
+ local f = path .. '/' .. db_name .. '/' .. file .. '/' .. filename
+ local attr = lfs.attributes(f)
+ assert(type(attr) == "table")
+ scripts[tonumber(file)] = helpers.read_whole_file(f)
+ end
+ end
+ return scripts
+end
+
+local function build_database_path(path, db_name)
+ return path .. "/" .. db_name .. ".db"
+end
+
+local function db_exec(file, script, version)
+ local db = assert(sqlite3.open(file), string.format("file: %s", file))
+ assert(db:exec(script) == sqlite3.OK, string.format("script:\n%s\n", script))
+ assert(db:exec(string.format("PRAGMA user_version=%u;", version)) == sqlite3.OK,
+ string.format("version: %d", version))
+ db:close()
+end
+
+local function read_db_version(file)
+ local db = assert(sqlite3.open(file))
+ local stmt = assert(db:prepare("PRAGMA user_version;"))
+ local ret = {}
+ for v in stmt:urows() do
+ ret = v
+ end
+ db:close()
+ return ret
+end
+
+local function db_migrate_up(db_path, scripts, target_version)
+ current_version = read_db_version(db_path)
+
+ if current_version == target_version then
+ return migration.retcode.ALREADY_UP_TO_DATE
+ end
+
+ if current_version > target_version then
+ return migration.retcode.WRONG_TARGET_VERSION
+ end
+
+ for v = current_version, target_version - 1 do
+ db_exec(db_path, scripts[v + 1], v + 1)
+ end
+ return migration.retcode.OK
+end
+
+local function db_migrate_down(db_path, scripts, target_version)
+ current_version = read_db_version(db_path)
+
+ if current_version == target_version then
+ return migration.retcode.ALREADY_UP_TO_DATE
+ end
+
+ if current_version < target_version then
+ return migration.retcode.WRONG_TARGET_VERSION
+ end
+
+ for v = current_version, target_version + 1, -1 do
+ db_exec(db_path, scripts[v], v - 1)
+ end
+ return migration.retcode.OK
+end
+
+local function print_db_set(db_set)
+ print("database set:")
+ for name, version in pairs(db_set) do
+ print("\tname: " .. name)
+ print("\ttarget version: " .. version)
+ end
+end
+
+local function validate_inputs(migration_dir, db_dir)
+ assert(helpers.exists(migration_dir), "Migrations directory does not exist")
+ assert(helpers.exists(db_dir), "Databases directory does not exist")
+ return true
+end
+
+local function migrate(db_path, scripts_up, scripts_down, target_version)
+ local db_version = read_db_version(db_path)
+ if db_version > target_version then
+ return migration.down(db_path, scripts_down, target_version)
+ end
+ if db_version < target_version then
+ return migration.up(db_path, scripts_up, target_version)
+ end
+ return migration.retcode.OK
+end
+
+--- Perform database migration from lower to higher version
+-- @function up
+-- @param db_path path to the database we want to perform migration on
+-- @param scripts array of {""}
+-- @param target_version database version we want to migrate in to
+-- @return @{retcode}
+function migration.up(db_path, scripts, target_version)
+ return db_migrate_up(db_path, scripts, target_version)
+end
+
+--- Perform database migration from higher to lower version
+-- @function down
+-- @param db_path path to the database we want to perform migration on
+-- @param scripts array of {""}
+-- @param target_version database version we want to migrate in to
+-- @return @{retcode}
+function migration.down(db_path, scripts, target_version)
+ return db_migrate_down(db_path, scripts, target_version)
+end
+
+--- Read the database version
+-- @function get_db_version
+-- @param db_path path to the database
+-- @return database version number
+function migration.get_db_version(db_path)
+ return read_db_version(db_path)
+end
+
+--- Perform databases migration(up/down) automatically
+-- @function migrate
+-- @param db_dir location of the databases
+-- @param scripts_dir location of the database migration scripts
+-- @param db_set array of {<"database_name"> = <db_target_version>} entries
+-- @return @{retcode}
+function migration.migrate(db_dir, scripts_dir, db_set)
+ print(string.format("migrations scripts directory: '%s'", scripts_dir))
+ print(string.format("databases directory: '%s'", db_dir))
+ print_db_set(db_set)
+
+ for name, version in pairs(db_set) do
+ local database_path = build_database_path(db_dir, name)
+ local scripts_up = build_script_array(scripts_dir, name, up_script)
+ local scripts_down = build_script_array(scripts_dir, name, down_script)
+ local ret = migrate(database_path, scripts_up, scripts_down, version)
+ if ret ~= migration.retcode.OK then
+ return ret
+ end
+ end
+
+ return migration.retcode.OK
+end
+
+return migration
A scripts/lua/migration/test/install_dependencies.sh => scripts/lua/migration/test/install_dependencies.sh +16 -0
@@ 0,0 1,16 @@
+#!/usr/bin/env bash
+# Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+function if_cmd_installed() {
+ if ! command -v "$1" &> /dev/null; then
+ echo "$1 command could not be found. Please install it using system package manager"
+ exit 1
+ fi
+}
+
+if_cmd_installed "luarocks"
+
+luarocks install lsqlite3complete
+luarocks install lfs
+luarocks install luaunit<
\ No newline at end of file
A scripts/lua/migration/test/test.lua => scripts/lua/migration/test/test.lua +142 -0
@@ 0,0 1,142 @@
+--Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+--For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+package.path = package.path .. ";../?.lua;../../share/?.lua;../../share/?/?.lua"
+local lu = require("luaunit")
+local sqlite = require("lsqlite3complete")
+local lfs = require("lfs")
+local migration = require("migration")
+
+local test_db_schema = [[
+ CREATE TABLE IF NOT EXISTS test_1
+ (
+ _id INTEGER PRIMARY KEY,
+ timestamp TEXT,
+ duration INTEGER
+ );"
+ ]]
+
+function spawn_db(name, version, schema)
+ os.remove(name)
+ local db = sqlite.open(name)
+ db:exec(schema)
+ db:exec(string.format("PRAGMA user_version=%u;", version))
+ db:close()
+end
+
+function spawn_script(name, dir, db_name, version, body)
+ lfs.mkdir(dir .. "/" .. db_name)
+ lfs.mkdir(dir .. "/" .. db_name .. "/" .. version)
+ local fd = io.open(dir .. "/" .. db_name .. "/" .. version .. "/" .. name, "w")
+ fd:write(body)
+ fd:close()
+end
+
+function test_migration_up_success()
+ scripts = {}
+ scripts[1] = "ALTER TABLE test_1 ADD new_column TEXT;"
+ scripts[2] = "ALTER TABLE test_1 ADD new_column2 TEXT;"
+ scripts[3] = "ALTER TABLE test_1 ADD new_column3 TEXT;"
+ scripts[4] = "ALTER TABLE test_1 ADD new_column4 TEXT;"
+ scripts[5] = "ALTER TABLE test_1 ADD new_column5 TEXT;"
+
+ --- Migrate from version 0 to 1
+ spawn_db("test_1.sql", 0, test_db_schema)
+ lu.assertEquals(migration.up("test_1.sql", scripts, 1), migration.retcode.OK)
+ lu.assertEquals(migration.get_db_version("test_1.sql"), 1)
+
+ -- Migrate from version 1 to 2
+ lu.assertEquals(migration.up("test_1.sql", scripts, 2), migration.retcode.OK)
+ lu.assertEquals(migration.get_db_version("test_1.sql"), 2)
+
+ --- Migrate from version 0 to 5
+ spawn_db("test_1.sql", 0, test_db_schema)
+ lu.assertEquals(migration.up("test_1.sql", scripts, 5), migration.retcode.OK)
+ lu.assertEquals(migration.get_db_version("test_1.sql"), 5)
+end
+
+function test_migration_up_db_errors()
+ scripts = {}
+
+ --- Trigger DB error by trying to operate on empty database
+ spawn_db("test_1.sql", 1, "")
+ scripts[1] = "ALTER TABLE test_1 ADD new_column TEXT;"
+ lu.assertError(migration.up, "test_1.sql", scripts, 2)
+
+ --- Target version set to the same value as the current DB version
+ spawn_db("test_1.sql", 1, test_db_schema)
+ lu.assertEquals(migration.up("test_1.sql", scripts, 1), migration.retcode.ALREADY_UP_TO_DATE)
+
+ --- Target version set to the lower number than the current DB version
+ spawn_db("test_1.sql", 2, test_db_schema)
+ lu.assertEquals(migration.up("test_1.sql", scripts, 1), migration.retcode.WRONG_TARGET_VERSION)
+end
+
+function test_migration_down_success()
+ scripts = {}
+ scripts[1] = "ALTER TABLE test_1 ADD new_column1 TEXT;"
+ scripts[2] = "ALTER TABLE test_1 ADD new_column2 TEXT;"
+ scripts[3] = "ALTER TABLE test_1 ADD new_column3 TEXT;"
+ scripts[4] = "ALTER TABLE test_1 ADD new_column4 TEXT;"
+ scripts[5] = "ALTER TABLE test_1 ADD new_column5 TEXT;"
+
+ --- Migrate from version 2 to 1
+ spawn_db("test_1.sql", 2, test_db_schema)
+ lu.assertEquals(migration.down("test_1.sql", scripts, 1), migration.retcode.OK)
+ lu.assertEquals(migration.get_db_version("test_1.sql"), 1)
+
+ -- Migrate from version 1 to 0
+ lu.assertEquals(migration.down("test_1.sql", scripts, 0), migration.retcode.OK)
+ lu.assertEquals(migration.get_db_version("test_1.sql"), 0)
+
+ --- Migrate from version 5 to 0
+ spawn_db("test_1.sql", 5, test_db_schema)
+ lu.assertEquals(migration.down("test_1.sql", scripts, 0), migration.retcode.OK)
+ lu.assertEquals(migration.get_db_version("test_1.sql"), 0)
+end
+
+function test_migration_down_errors()
+ scripts = {}
+
+ --- Trigger DB error by trying to operate on empty database
+ spawn_db("test_1.sql", 2, "")
+ scripts[1] = "ALTER TABLE test_1 ADD new_column TEXT;"
+ lu.assertError(migration.down, "test_1.sql", scripts, 1)
+
+ --- Target version set to the same value as the current DB version
+ spawn_db("test_1.sql", 1, test_db_schema)
+ lu.assertEquals(migration.down("test_1.sql", scripts, 1), migration.retcode.ALREADY_UP_TO_DATE)
+
+ --- Target version set to the higher number than the current DB version
+ spawn_db("test_1.sql", 2, test_db_schema)
+ lu.assertEquals(migration.down("test_1.sql", scripts, 3), migration.retcode.WRONG_TARGET_VERSION)
+end
+
+function test_automatic_migration()
+ spawn_db("databases/test_1.db", 0, test_db_schema)
+ spawn_db("databases/test_2.db", 0, test_db_schema)
+ spawn_script("up.sql", "migrations", "test_1", 1, "insert into test_1 (duration) values(100);")
+ spawn_script("up.sql", "migrations", "test_1", 2, "insert into test_1 (duration) values(101);")
+ spawn_script("up.sql", "migrations", "test_2", 1, "insert into test_1 (duration) values(100);")
+ spawn_script("up.sql", "migrations", "test_2", 2, "insert into test_1 (duration) values(101);")
+ spawn_script("down.sql", "migrations", "test_1", 1, "delete from test_1 where _id=1;")
+ spawn_script("down.sql", "migrations", "test_1", 2, "delete from test_1 where _id=2;")
+ spawn_script("down.sql", "migrations", "test_2", 1, "delete from test_1 where _id=1;")
+ spawn_script("down.sql", "migrations", "test_2", 2, "delete from test_1 where _id=2;")
+
+ migration.migrate("databases", "migrations", {
+ test_1 = 2,
+ test_2 = 2
+ })
+ lu.assertEquals(migration.get_db_version("databases/test_1.db"), 2)
+ lu.assertEquals(migration.get_db_version("databases/test_2.db"), 2)
+
+ migration.migrate("databases", "migrations", {
+ test_1 = 1,
+ test_2 = 1
+ })
+ lu.assertEquals(migration.get_db_version("databases/test_1.db"), 1)
+ lu.assertEquals(migration.get_db_version("databases/test_2.db"), 1)
+end
+
+os.exit(lu.LuaUnit.run())