refactor(db): greatly simplify database migrations
This commit is contained in:
parent
1823759433
commit
ddf5ed01a2
5 changed files with 75 additions and 1004 deletions
|
@ -4,7 +4,44 @@ from typing import Dict
|
|||
import sqlite3
|
||||
|
||||
|
||||
def migrate_schema_1_to_2(c: sqlite3.Cursor):
|
||||
def migrate_schema_1(c: sqlite3.Cursor):
|
||||
c.execute('''
|
||||
CREATE TABLE users (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
email TEXT DEFAULT NULL,
|
||||
password TEXT NOT NULL,
|
||||
touchkey TEXT DEFAULT NULL,
|
||||
is_admin INTEGER(1) NOT NULL DEFAULT 0,
|
||||
is_member INTEGER(1) NOT NULL DEFAULT 1,
|
||||
balance INTEGER(8) NOT NULL DEFAULT 0,
|
||||
lastchange INTEGER(8) NOT NULL DEFAULT 0
|
||||
)
|
||||
''')
|
||||
c.execute('''
|
||||
CREATE TABLE products (
|
||||
product_id INTEGER PRIMARY KEY,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
stock INTEGER(8) NOT NULL DEFAULT 0,
|
||||
price_member INTEGER(8) NOT NULL,
|
||||
price_non_member INTEGER(8) NOT NULL
|
||||
)
|
||||
''')
|
||||
c.execute('''
|
||||
CREATE TABLE consumption (
|
||||
user_id INTEGER NOT NULL,
|
||||
product_id INTEGER NOT NULL,
|
||||
count INTEGER(8) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (user_id, product_id),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (product_id) REFERENCES products(product_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
)
|
||||
''')
|
||||
|
||||
|
||||
def migrate_schema_2(c: sqlite3.Cursor):
|
||||
# Create missing tables
|
||||
c.execute('''
|
||||
CREATE TABLE transactions (
|
||||
|
@ -115,7 +152,7 @@ def migrate_schema_1_to_2(c: sqlite3.Cursor):
|
|||
c.execute('DROP TABLE consumption')
|
||||
|
||||
|
||||
def migrate_schema_2_to_3(c: sqlite3.Cursor):
|
||||
def migrate_schema_3(c: sqlite3.Cursor):
|
||||
# Add missing columns to users table
|
||||
c.execute('ALTER TABLE users ADD COLUMN receipt_pref INTEGER(1) NOT NULL DEFAULT 0')
|
||||
c.execute('''ALTER TABLE users ADD COLUMN created INTEGER(8) NOT NULL DEFAULT 0''')
|
||||
|
@ -203,7 +240,7 @@ def migrate_schema_2_to_3(c: sqlite3.Cursor):
|
|||
''')
|
||||
|
||||
|
||||
def migrate_schema_3_to_4(c: sqlite3.Cursor):
|
||||
def migrate_schema_4(c: sqlite3.Cursor):
|
||||
# Change receipts schema to allow null for transaction IDs
|
||||
c.execute('''
|
||||
CREATE TEMPORARY TABLE receipts_temp (
|
||||
|
@ -241,7 +278,7 @@ def migrate_schema_3_to_4(c: sqlite3.Cursor):
|
|||
c.execute('DROP TABLE receipts_temp')
|
||||
|
||||
|
||||
def migrate_schema_4_to_5(c: sqlite3.Cursor):
|
||||
def migrate_schema_5(c: sqlite3.Cursor):
|
||||
# Change products schema to allow null for stock and add stockable column
|
||||
c.execute('''
|
||||
CREATE TEMPORARY TABLE products_temp (
|
||||
|
@ -270,7 +307,7 @@ def migrate_schema_4_to_5(c: sqlite3.Cursor):
|
|||
c.execute('DROP TABLE products_temp')
|
||||
|
||||
|
||||
def migrate_schema_5_to_6(c: sqlite3.Cursor):
|
||||
def migrate_schema_6(c: sqlite3.Cursor):
|
||||
# Add custom_price column
|
||||
c.execute('''
|
||||
ALTER TABLE products ADD COLUMN
|
||||
|
@ -278,7 +315,7 @@ def migrate_schema_5_to_6(c: sqlite3.Cursor):
|
|||
''')
|
||||
|
||||
|
||||
def migrate_schema_6_to_7(c: sqlite3.Cursor):
|
||||
def migrate_schema_7(c: sqlite3.Cursor):
|
||||
# Add custom_price column
|
||||
c.execute('''
|
||||
ALTER TABLE users ADD COLUMN
|
||||
|
@ -286,7 +323,7 @@ def migrate_schema_6_to_7(c: sqlite3.Cursor):
|
|||
''')
|
||||
|
||||
|
||||
def migrate_schema_7_to_8(c: sqlite3.Cursor):
|
||||
def migrate_schema_8(c: sqlite3.Cursor):
|
||||
# Add ean column
|
||||
c.execute('''
|
||||
ALTER TABLE products ADD COLUMN ean TEXT DEFAULT NULL
|
||||
|
@ -297,7 +334,7 @@ def migrate_schema_7_to_8(c: sqlite3.Cursor):
|
|||
''')
|
||||
|
||||
|
||||
def migrate_schema_8_to_9(c: sqlite3.Cursor):
|
||||
def migrate_schema_9(c: sqlite3.Cursor):
|
||||
c.execute('''
|
||||
CREATE TABLE tokens (
|
||||
token_id INTEGER PRIMARY KEY,
|
||||
|
@ -311,7 +348,7 @@ def migrate_schema_8_to_9(c: sqlite3.Cursor):
|
|||
''')
|
||||
|
||||
|
||||
def migrate_schema_9_to_10(c: sqlite3.Cursor):
|
||||
def migrate_schema_10(c: sqlite3.Cursor):
|
||||
c.execute('''
|
||||
ALTER TABLE users RENAME TO users_old
|
||||
''')
|
||||
|
|
|
@ -1,764 +0,0 @@
|
|||
from typing import Dict, List
|
||||
|
||||
SCHEMAS: Dict[int, List[str]] = dict()
|
||||
|
||||
SCHEMAS[1] = [
|
||||
'''
|
||||
CREATE TABLE users (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
email TEXT DEFAULT NULL,
|
||||
password TEXT NOT NULL,
|
||||
touchkey TEXT DEFAULT NULL,
|
||||
is_admin INTEGER(1) NOT NULL DEFAULT 0,
|
||||
is_member INTEGER(1) NOT NULL DEFAULT 1,
|
||||
balance INTEGER(8) NOT NULL DEFAULT 0,
|
||||
lastchange INTEGER(8) NOT NULL DEFAULT 0
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE products (
|
||||
product_id INTEGER PRIMARY KEY,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
stock INTEGER(8) NOT NULL DEFAULT 0,
|
||||
price_member INTEGER(8) NOT NULL,
|
||||
price_non_member INTEGER(8) NOT NULL
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE consumption (
|
||||
user_id INTEGER NOT NULL,
|
||||
product_id INTEGER NOT NULL,
|
||||
count INTEGER(8) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (user_id, product_id),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (product_id) REFERENCES products(product_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''']
|
||||
|
||||
SCHEMAS[2] = [
|
||||
'''
|
||||
CREATE TABLE users (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
email TEXT DEFAULT NULL,
|
||||
password TEXT NOT NULL,
|
||||
touchkey TEXT DEFAULT NULL,
|
||||
is_admin INTEGER(1) NOT NULL DEFAULT 0,
|
||||
is_member INTEGER(1) NOT NULL DEFAULT 1,
|
||||
balance INTEGER(8) NOT NULL DEFAULT 0,
|
||||
lastchange INTEGER(8) NOT NULL DEFAULT 0
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE products (
|
||||
product_id INTEGER PRIMARY KEY,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
stock INTEGER(8) NOT NULL DEFAULT 0,
|
||||
price_member INTEGER(8) NOT NULL,
|
||||
price_non_member INTEGER(8) NOT NULL
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE transactions ( -- "superclass" of the following 3 tables
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
value INTEGER(8) NOT NULL,
|
||||
old_balance INTEGER(8) NOT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE consumptions ( -- transactions involving buying a product
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
product_id INTEGER DEFAULT NULL,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (product_id) REFERENCES products(product_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE deposits ( -- transactions involving depositing cash
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE modifications ( -- transactions involving balance modification by an admin
|
||||
ta_id INTEGER NOT NULL,
|
||||
agent_id INTEGER NOT NULL,
|
||||
reason TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (ta_id),
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (agent_id) REFERENCES users(user_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''']
|
||||
|
||||
SCHEMAS[3] = [
|
||||
'''
|
||||
CREATE TABLE users (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
email TEXT DEFAULT NULL,
|
||||
password TEXT NOT NULL,
|
||||
touchkey TEXT DEFAULT NULL,
|
||||
is_admin INTEGER(1) NOT NULL DEFAULT 0,
|
||||
is_member INTEGER(1) NOT NULL DEFAULT 1,
|
||||
balance INTEGER(8) NOT NULL DEFAULT 0,
|
||||
lastchange INTEGER(8) NOT NULL DEFAULT 0,
|
||||
receipt_pref INTEGER(1) NOT NULL DEFAULT 0,
|
||||
created INTEGER(8) NOT NULL DEFAULT 0
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE products (
|
||||
product_id INTEGER PRIMARY KEY,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
stock INTEGER(8) NOT NULL DEFAULT 0,
|
||||
price_member INTEGER(8) NOT NULL,
|
||||
price_non_member INTEGER(8) NOT NULL
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE transactions ( -- "superclass" of the following 3 tables
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER DEFAULT NULL,
|
||||
value INTEGER(8) NOT NULL,
|
||||
old_balance INTEGER(8) NOT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE consumptions ( -- transactions involving buying a product
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
product TEXT NOT NULL,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE deposits ( -- transactions involving depositing cash
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE modifications ( -- transactions involving balance modification by an admin
|
||||
ta_id INTEGER NOT NULL,
|
||||
agent TEXT NOT NULL,
|
||||
reason TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (ta_id),
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE receipts ( -- receipts sent to the users
|
||||
receipt_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
first_ta_id INTEGER NOT NULL,
|
||||
last_ta_id INTEGER NOT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (first_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
FOREIGN KEY (last_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''']
|
||||
|
||||
SCHEMAS[4] = [
|
||||
'''
|
||||
CREATE TABLE users (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
email TEXT DEFAULT NULL,
|
||||
password TEXT NOT NULL,
|
||||
touchkey TEXT DEFAULT NULL,
|
||||
is_admin INTEGER(1) NOT NULL DEFAULT 0,
|
||||
is_member INTEGER(1) NOT NULL DEFAULT 1,
|
||||
balance INTEGER(8) NOT NULL DEFAULT 0,
|
||||
lastchange INTEGER(8) NOT NULL DEFAULT 0,
|
||||
receipt_pref INTEGER(1) NOT NULL DEFAULT 0,
|
||||
created INTEGER(8) NOT NULL DEFAULT 0
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE products (
|
||||
product_id INTEGER PRIMARY KEY,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
stock INTEGER(8) NOT NULL DEFAULT 0,
|
||||
price_member INTEGER(8) NOT NULL,
|
||||
price_non_member INTEGER(8) NOT NULL
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE transactions ( -- "superclass" of the following 3 tables
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER DEFAULT NULL,
|
||||
value INTEGER(8) NOT NULL,
|
||||
old_balance INTEGER(8) NOT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE consumptions ( -- transactions involving buying a product
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
product TEXT NOT NULL,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE deposits ( -- transactions involving depositing cash
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE modifications ( -- transactions involving balance modification by an admin
|
||||
ta_id INTEGER NOT NULL,
|
||||
agent TEXT NOT NULL,
|
||||
reason TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (ta_id),
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE receipts ( -- receipts sent to the users
|
||||
receipt_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
first_ta_id INTEGER DEFAULT NULL,
|
||||
last_ta_id INTEGER DEFAULT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (first_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
FOREIGN KEY (last_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''']
|
||||
|
||||
SCHEMAS[5] = [
|
||||
'''
|
||||
CREATE TABLE users (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
email TEXT DEFAULT NULL,
|
||||
password TEXT NOT NULL,
|
||||
touchkey TEXT DEFAULT NULL,
|
||||
is_admin INTEGER(1) NOT NULL DEFAULT 0,
|
||||
is_member INTEGER(1) NOT NULL DEFAULT 1,
|
||||
balance INTEGER(8) NOT NULL DEFAULT 0,
|
||||
lastchange INTEGER(8) NOT NULL DEFAULT 0,
|
||||
receipt_pref INTEGER(1) NOT NULL DEFAULT 0,
|
||||
created INTEGER(8) NOT NULL DEFAULT 0
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE products (
|
||||
product_id INTEGER PRIMARY KEY,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
stock INTEGER(8) DEFAULT 0,
|
||||
stockable INTEGER(1) DEFAULT 1,
|
||||
price_member INTEGER(8) NOT NULL,
|
||||
price_non_member INTEGER(8) NOT NULL
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE transactions ( -- "superclass" of the following 3 tables
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER DEFAULT NULL,
|
||||
value INTEGER(8) NOT NULL,
|
||||
old_balance INTEGER(8) NOT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE consumptions ( -- transactions involving buying a product
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
product TEXT NOT NULL,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE deposits ( -- transactions involving depositing cash
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE modifications ( -- transactions involving balance modification by an admin
|
||||
ta_id INTEGER NOT NULL,
|
||||
agent TEXT NOT NULL,
|
||||
reason TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (ta_id),
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE receipts ( -- receipts sent to the users
|
||||
receipt_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
first_ta_id INTEGER DEFAULT NULL,
|
||||
last_ta_id INTEGER DEFAULT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (first_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
FOREIGN KEY (last_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''']
|
||||
|
||||
|
||||
SCHEMAS[6] = [
|
||||
'''
|
||||
CREATE TABLE users (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
email TEXT DEFAULT NULL,
|
||||
password TEXT NOT NULL,
|
||||
touchkey TEXT DEFAULT NULL,
|
||||
is_admin INTEGER(1) NOT NULL DEFAULT 0,
|
||||
is_member INTEGER(1) NOT NULL DEFAULT 1,
|
||||
balance INTEGER(8) NOT NULL DEFAULT 0,
|
||||
lastchange INTEGER(8) NOT NULL DEFAULT 0,
|
||||
receipt_pref INTEGER(1) NOT NULL DEFAULT 0,
|
||||
created INTEGER(8) NOT NULL DEFAULT 0
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE products (
|
||||
product_id INTEGER PRIMARY KEY,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
stock INTEGER(8) DEFAULT 0,
|
||||
stockable INTEGER(1) DEFAULT 1,
|
||||
price_member INTEGER(8) NOT NULL,
|
||||
price_non_member INTEGER(8) NOT NULL,
|
||||
custom_price INTEGER(1) DEFAULT 0
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE transactions ( -- "superclass" of the following 3 tables
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER DEFAULT NULL,
|
||||
value INTEGER(8) NOT NULL,
|
||||
old_balance INTEGER(8) NOT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE consumptions ( -- transactions involving buying a product
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
product TEXT NOT NULL,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE deposits ( -- transactions involving depositing cash
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE modifications ( -- transactions involving balance modification by an admin
|
||||
ta_id INTEGER NOT NULL,
|
||||
agent TEXT NOT NULL,
|
||||
reason TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (ta_id),
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE receipts ( -- receipts sent to the users
|
||||
receipt_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
first_ta_id INTEGER DEFAULT NULL,
|
||||
last_ta_id INTEGER DEFAULT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (first_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
FOREIGN KEY (last_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''']
|
||||
|
||||
|
||||
SCHEMAS[7] = [
|
||||
'''
|
||||
CREATE TABLE users (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
email TEXT DEFAULT NULL,
|
||||
password TEXT NOT NULL,
|
||||
touchkey TEXT DEFAULT NULL,
|
||||
is_admin INTEGER(1) NOT NULL DEFAULT 0,
|
||||
is_member INTEGER(1) NOT NULL DEFAULT 1,
|
||||
balance INTEGER(8) NOT NULL DEFAULT 0,
|
||||
lastchange INTEGER(8) NOT NULL DEFAULT 0,
|
||||
receipt_pref INTEGER(1) NOT NULL DEFAULT 0,
|
||||
created INTEGER(8) NOT NULL DEFAULT 0,
|
||||
logout_after_purchase INTEGER(1) DEFAULT 0
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE products (
|
||||
product_id INTEGER PRIMARY KEY,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
stock INTEGER(8) DEFAULT 0,
|
||||
stockable INTEGER(1) DEFAULT 1,
|
||||
price_member INTEGER(8) NOT NULL,
|
||||
price_non_member INTEGER(8) NOT NULL,
|
||||
custom_price INTEGER(1) DEFAULT 0
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE transactions ( -- "superclass" of the following 3 tables
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER DEFAULT NULL,
|
||||
value INTEGER(8) NOT NULL,
|
||||
old_balance INTEGER(8) NOT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE consumptions ( -- transactions involving buying a product
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
product TEXT NOT NULL,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE deposits ( -- transactions involving depositing cash
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE modifications ( -- transactions involving balance modification by an admin
|
||||
ta_id INTEGER NOT NULL,
|
||||
agent TEXT NOT NULL,
|
||||
reason TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (ta_id),
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE receipts ( -- receipts sent to the users
|
||||
receipt_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
first_ta_id INTEGER DEFAULT NULL,
|
||||
last_ta_id INTEGER DEFAULT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (first_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
FOREIGN KEY (last_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''']
|
||||
|
||||
|
||||
SCHEMAS[8] = [
|
||||
'''
|
||||
CREATE TABLE users (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
email TEXT DEFAULT NULL,
|
||||
password TEXT NOT NULL,
|
||||
touchkey TEXT DEFAULT NULL,
|
||||
is_admin INTEGER(1) NOT NULL DEFAULT 0,
|
||||
is_member INTEGER(1) NOT NULL DEFAULT 1,
|
||||
balance INTEGER(8) NOT NULL DEFAULT 0,
|
||||
lastchange INTEGER(8) NOT NULL DEFAULT 0,
|
||||
receipt_pref INTEGER(1) NOT NULL DEFAULT 0,
|
||||
created INTEGER(8) NOT NULL DEFAULT 0,
|
||||
logout_after_purchase INTEGER(1) DEFAULT 0
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE products (
|
||||
product_id INTEGER PRIMARY KEY,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
stock INTEGER(8) DEFAULT 0,
|
||||
stockable INTEGER(1) DEFAULT 1,
|
||||
price_member INTEGER(8) NOT NULL,
|
||||
price_non_member INTEGER(8) NOT NULL,
|
||||
custom_price INTEGER(1) DEFAULT 0,
|
||||
ean TEXT UNIQUE DEFAULT NULL
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE transactions ( -- "superclass" of the following 3 tables
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER DEFAULT NULL,
|
||||
value INTEGER(8) NOT NULL,
|
||||
old_balance INTEGER(8) NOT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE consumptions ( -- transactions involving buying a product
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
product TEXT NOT NULL,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE deposits ( -- transactions involving depositing cash
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE modifications ( -- transactions involving balance modification by an admin
|
||||
ta_id INTEGER NOT NULL,
|
||||
agent TEXT NOT NULL,
|
||||
reason TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (ta_id),
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE receipts ( -- receipts sent to the users
|
||||
receipt_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
first_ta_id INTEGER DEFAULT NULL,
|
||||
last_ta_id INTEGER DEFAULT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (first_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
FOREIGN KEY (last_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''']
|
||||
|
||||
|
||||
SCHEMAS[9] = [
|
||||
'''
|
||||
CREATE TABLE users (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
email TEXT DEFAULT NULL,
|
||||
password TEXT NOT NULL,
|
||||
touchkey TEXT DEFAULT NULL,
|
||||
is_admin INTEGER(1) NOT NULL DEFAULT 0,
|
||||
is_member INTEGER(1) NOT NULL DEFAULT 1,
|
||||
balance INTEGER(8) NOT NULL DEFAULT 0,
|
||||
lastchange INTEGER(8) NOT NULL DEFAULT 0,
|
||||
receipt_pref INTEGER(1) NOT NULL DEFAULT 0,
|
||||
created INTEGER(8) NOT NULL DEFAULT 0,
|
||||
logout_after_purchase INTEGER(1) DEFAULT 0
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE products (
|
||||
product_id INTEGER PRIMARY KEY,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
stock INTEGER(8) DEFAULT 0,
|
||||
stockable INTEGER(1) DEFAULT 1,
|
||||
price_member INTEGER(8) NOT NULL,
|
||||
price_non_member INTEGER(8) NOT NULL,
|
||||
custom_price INTEGER(1) DEFAULT 0,
|
||||
ean TEXT UNIQUE DEFAULT NULL
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE transactions ( -- "superclass" of the following 3 tables
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER DEFAULT NULL,
|
||||
value INTEGER(8) NOT NULL,
|
||||
old_balance INTEGER(8) NOT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE consumptions ( -- transactions involving buying a product
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
product TEXT NOT NULL,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE deposits ( -- transactions involving depositing cash
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE modifications ( -- transactions involving balance modification by an admin
|
||||
ta_id INTEGER NOT NULL,
|
||||
agent TEXT NOT NULL,
|
||||
reason TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (ta_id),
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE receipts ( -- receipts sent to the users
|
||||
receipt_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
first_ta_id INTEGER DEFAULT NULL,
|
||||
last_ta_id INTEGER DEFAULT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (first_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
FOREIGN KEY (last_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE tokens ( -- authentication tokens such as barcodes
|
||||
token_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
token TEXT UNIQUE NOT NULL,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''']
|
||||
|
||||
|
||||
SCHEMAS[10] = [
|
||||
'''
|
||||
CREATE TABLE users (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL COLLATE NOCASE,
|
||||
email TEXT DEFAULT NULL,
|
||||
password TEXT NOT NULL,
|
||||
touchkey TEXT DEFAULT NULL,
|
||||
is_admin INTEGER(1) NOT NULL DEFAULT 0,
|
||||
is_member INTEGER(1) NOT NULL DEFAULT 1,
|
||||
balance INTEGER(8) NOT NULL DEFAULT 0,
|
||||
lastchange INTEGER(8) NOT NULL DEFAULT 0,
|
||||
receipt_pref INTEGER(1) NOT NULL DEFAULT 0,
|
||||
created INTEGER(8) NOT NULL DEFAULT 0,
|
||||
logout_after_purchase INTEGER(1) DEFAULT 0
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE products (
|
||||
product_id INTEGER PRIMARY KEY,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
stock INTEGER(8) DEFAULT 0,
|
||||
stockable INTEGER(1) DEFAULT 1,
|
||||
price_member INTEGER(8) NOT NULL,
|
||||
price_non_member INTEGER(8) NOT NULL,
|
||||
custom_price INTEGER(1) DEFAULT 0,
|
||||
ean TEXT UNIQUE DEFAULT NULL
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE transactions ( -- "superclass" of the following 3 tables
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER DEFAULT NULL,
|
||||
value INTEGER(8) NOT NULL,
|
||||
old_balance INTEGER(8) NOT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE consumptions ( -- transactions involving buying a product
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
product TEXT NOT NULL,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE deposits ( -- transactions involving depositing cash
|
||||
ta_id INTEGER PRIMARY KEY,
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE modifications ( -- transactions involving balance modification by an admin
|
||||
ta_id INTEGER NOT NULL,
|
||||
agent TEXT NOT NULL,
|
||||
reason TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (ta_id),
|
||||
FOREIGN KEY (ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE receipts ( -- receipts sent to the users
|
||||
receipt_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
first_ta_id INTEGER DEFAULT NULL,
|
||||
last_ta_id INTEGER DEFAULT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (first_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
FOREIGN KEY (last_ta_id) REFERENCES transactions(ta_id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE tokens ( -- authentication tokens such as barcodes
|
||||
token_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
token TEXT UNIQUE NOT NULL,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
date INTEGER(8) DEFAULT (STRFTIME('%s', 'now')),
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
''']
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
import unittest
|
||||
from unittest.mock import PropertyMock, patch
|
||||
|
||||
import sqlite3
|
||||
|
||||
from matemat.db import DatabaseWrapper
|
||||
from matemat.db.schemas import SCHEMAS
|
||||
|
||||
|
||||
class TestMigrations(unittest.TestCase):
|
||||
|
@ -15,204 +15,13 @@ class TestMigrations(unittest.TestCase):
|
|||
|
||||
def _initialize_db(self, schema_version: int):
|
||||
self.db._sqlite_db = sqlite3.connect(':memory:')
|
||||
cursor: sqlite3.Cursor = self.db._sqlite_db.cursor()
|
||||
cursor.execute('BEGIN EXCLUSIVE')
|
||||
for cmd in SCHEMAS[schema_version]:
|
||||
cursor.execute(cmd)
|
||||
cursor.execute('COMMIT')
|
||||
self.db._setup()
|
||||
|
||||
def test_downgrade_fail(self):
|
||||
# Test that downgrades are forbidden
|
||||
self.db.SCHEMA_VERSION = 1
|
||||
self.db._sqlite_db = sqlite3.connect(':memory:')
|
||||
self.db._sqlite_db.execute('PRAGMA user_version = 2')
|
||||
with self.assertRaises(RuntimeError):
|
||||
with self.db:
|
||||
pass
|
||||
|
||||
def test_upgrade_1_to_2(self):
|
||||
# Setup test db with example entries covering - hopefully - all cases
|
||||
self._initialize_db(1)
|
||||
cursor: sqlite3.Cursor = self.db._sqlite_db.cursor()
|
||||
cursor.execute('''
|
||||
INSERT INTO users VALUES
|
||||
(1, 'testadmin', 'a@b.c', '$2a$10$herebehashes', NULL, 1, 1, 1337, 0),
|
||||
(2, 'testuser', NULL, '$2a$10$herebehashes', '$2a$10$herebehashes', 0, 1, 4242, 0),
|
||||
(3, 'alien', NULL, '$2a$10$herebehashes', '$2a$10$herebehashes', 0, 0, 1234, 0)
|
||||
''')
|
||||
cursor.execute('''
|
||||
INSERT INTO products VALUES
|
||||
(1, 'Club Mate', 42, 200, 250),
|
||||
(2, 'Flora Power Mate (1/4l)', 10, 100, 150)
|
||||
''')
|
||||
cursor.execute('''
|
||||
INSERT INTO consumption VALUES
|
||||
(1, 1, 5), (1, 2, 3), (2, 2, 10), (3, 1, 3), (3, 2, 4)
|
||||
''')
|
||||
cursor.execute('PRAGMA user_version = 1')
|
||||
|
||||
# Kick off the migration
|
||||
schema_version = self.db.SCHEMA_VERSION
|
||||
self.db.SCHEMA_VERSION = 2
|
||||
self.db._setup()
|
||||
self.db.SCHEMA_VERSION = schema_version
|
||||
|
||||
# Test whether the new tables were created
|
||||
cursor.execute('PRAGMA table_info(transactions)')
|
||||
self.assertNotEqual(0, len(cursor.fetchall()))
|
||||
cursor.execute('PRAGMA table_info(consumptions)')
|
||||
self.assertNotEqual(0, len(cursor.fetchall()))
|
||||
cursor.execute('PRAGMA table_info(deposits)')
|
||||
self.assertNotEqual(0, len(cursor.fetchall()))
|
||||
cursor.execute('PRAGMA table_info(modifications)')
|
||||
self.assertNotEqual(0, len(cursor.fetchall()))
|
||||
# Test whether the old consumption table was dropped
|
||||
cursor.execute('PRAGMA table_info(consumption)')
|
||||
self.assertEqual(0, len(cursor.fetchall()))
|
||||
|
||||
# Test number of entries in the new tables
|
||||
cursor.execute('SELECT COUNT(ta_id) FROM transactions')
|
||||
self.assertEqual(25, cursor.fetchone()[0])
|
||||
cursor.execute('SELECT COUNT(ta_id) FROM consumptions')
|
||||
self.assertEqual(25, cursor.fetchone()[0])
|
||||
cursor.execute('SELECT COUNT(ta_id) FROM deposits')
|
||||
self.assertEqual(0, cursor.fetchone()[0])
|
||||
cursor.execute('SELECT COUNT(ta_id) FROM modifications')
|
||||
self.assertEqual(0, cursor.fetchone()[0])
|
||||
|
||||
# The (user_id=2 x product_id=1) combination should never appear
|
||||
cursor.execute('''
|
||||
SELECT COUNT(t.ta_id)
|
||||
FROM transactions AS t
|
||||
LEFT JOIN consumptions AS c
|
||||
ON t.ta_id = c.ta_id
|
||||
WHERE t.user_id = 2 AND c.product_id = 1''')
|
||||
self.assertEqual(0, cursor.fetchone()[0])
|
||||
|
||||
# Test that one entry per consumption was created, and their values match the negative price
|
||||
cursor.execute('''
|
||||
SELECT COUNT(t.ta_id)
|
||||
FROM transactions AS t
|
||||
LEFT JOIN consumptions AS c
|
||||
ON t.ta_id = c.ta_id
|
||||
WHERE t.user_id = 1 AND c.product_id = 1 AND t.value = -200''')
|
||||
self.assertEqual(5, cursor.fetchone()[0])
|
||||
cursor.execute('''
|
||||
SELECT COUNT(t.ta_id)
|
||||
FROM transactions AS t
|
||||
LEFT JOIN consumptions AS c
|
||||
ON t.ta_id = c.ta_id
|
||||
WHERE t.user_id = 1 AND c.product_id = 2 AND t.value = -100''')
|
||||
self.assertEqual(3, cursor.fetchone()[0])
|
||||
cursor.execute('''
|
||||
SELECT COUNT(t.ta_id)
|
||||
FROM transactions AS t
|
||||
LEFT JOIN consumptions AS c
|
||||
ON t.ta_id = c.ta_id
|
||||
WHERE t.user_id = 2 AND c.product_id = 2 AND t.value = -100''')
|
||||
self.assertEqual(10, cursor.fetchone()[0])
|
||||
cursor.execute('''
|
||||
SELECT COUNT(t.ta_id)
|
||||
FROM transactions AS t
|
||||
LEFT JOIN consumptions AS c
|
||||
ON t.ta_id = c.ta_id
|
||||
WHERE t.user_id = 3 AND c.product_id = 1 AND t.value = -250''')
|
||||
self.assertEqual(3, cursor.fetchone()[0])
|
||||
cursor.execute('''
|
||||
SELECT COUNT(t.ta_id)
|
||||
FROM transactions AS t
|
||||
LEFT JOIN consumptions AS c
|
||||
ON t.ta_id = c.ta_id
|
||||
WHERE t.user_id = 3 AND c.product_id = 2 AND t.value = -150''')
|
||||
self.assertEqual(4, cursor.fetchone()[0])
|
||||
|
||||
def test_upgrade_2_to_3(self):
|
||||
# Setup test db with example entries covering - hopefully - all cases
|
||||
self._initialize_db(2)
|
||||
cursor: sqlite3.Cursor = self.db._sqlite_db.cursor()
|
||||
cursor.execute('''
|
||||
INSERT INTO users VALUES
|
||||
(1, 'testadmin', 'a@b.c', '$2a$10$herebehashes', NULL, 1, 1, 1337, 0),
|
||||
(2, 'testuser', NULL, '$2a$10$herebehashes', '$2a$10$herebehashes', 0, 1, 4242, 0),
|
||||
(3, 'alien', NULL, '$2a$10$herebehashes', '$2a$10$herebehashes', 0, 0, 1234, 0),
|
||||
(4, 'neverused', NULL, '$2a$10$herebehashes', '$2a$10$herebehashes', 0, 0, 1234, 1234)
|
||||
''')
|
||||
cursor.execute('''
|
||||
INSERT INTO products VALUES
|
||||
(1, 'Club Mate', 42, 200, 250),
|
||||
(2, 'Flora Power Mate', 10, 100, 150)
|
||||
''')
|
||||
cursor.execute('''
|
||||
INSERT INTO transactions VALUES
|
||||
(1, 1, 4200, 0, 1000), -- deposit
|
||||
(2, 2, 1337, 0, 1001), -- modification
|
||||
(3, 3, 1337, 0, 1002), -- modification with deleted agent
|
||||
(4, 2, -200, 1337, 1003), -- consumption
|
||||
(5, 3, -200, 1337, 1004) -- consumption with deleted product
|
||||
''')
|
||||
cursor.execute('''INSERT INTO deposits VALUES (1)''')
|
||||
cursor.execute('''
|
||||
INSERT INTO modifications VALUES
|
||||
(2, 1, 'Account migration'),
|
||||
(3, 42, 'You can''t find out who i am... MUAHAHAHA!!!')''')
|
||||
cursor.execute('''INSERT INTO consumptions VALUES (4, 2), (5, 42)''')
|
||||
cursor.execute('''PRAGMA user_version = 2''')
|
||||
|
||||
# Kick off the migration
|
||||
schema_version = self.db.SCHEMA_VERSION
|
||||
self.db.SCHEMA_VERSION = 3
|
||||
self.db._setup()
|
||||
self.db.SCHEMA_VERSION = schema_version
|
||||
|
||||
# Make sure the receipts table was created
|
||||
cursor.execute('''SELECT COUNT(receipt_id) FROM receipts''')
|
||||
self.assertEqual(0, cursor.fetchone()[0])
|
||||
|
||||
# Make sure users.created was populated with the expected values
|
||||
cursor.execute('''SELECT u.created FROM users AS u ORDER BY u.user_id ASC''')
|
||||
self.assertEqual([(940,), (941,), (942,), (1174,)], cursor.fetchall())
|
||||
|
||||
# Make sure the modifications table was changed to contain the username, or a fallback
|
||||
cursor.execute('''SELECT agent FROM modifications WHERE ta_id = 2''')
|
||||
self.assertEqual('testadmin', cursor.fetchone()[0])
|
||||
cursor.execute('''SELECT agent FROM modifications WHERE ta_id = 3''')
|
||||
self.assertEqual('<unknown>', cursor.fetchone()[0])
|
||||
|
||||
# Make sure the consumptions table was changed to contain the product name, or a fallback
|
||||
cursor.execute('''SELECT product FROM consumptions WHERE ta_id = 4''')
|
||||
self.assertEqual('Flora Power Mate', cursor.fetchone()[0])
|
||||
cursor.execute('''SELECT product FROM consumptions WHERE ta_id = 5''')
|
||||
self.assertEqual('<unknown>', cursor.fetchone()[0])
|
||||
|
||||
def test_upgrade_3_to_4(self):
|
||||
# Setup test db with example entries to test schema change
|
||||
self._initialize_db(3)
|
||||
cursor: sqlite3.Cursor = self.db._sqlite_db.cursor()
|
||||
cursor.execute('''
|
||||
INSERT INTO users VALUES
|
||||
(1, 'testadmin', 'a@b.c', '$2a$10$herebehashes', NULL, 1, 1, 1337, 0, 0, 0)
|
||||
''')
|
||||
cursor.execute('''
|
||||
INSERT INTO products VALUES
|
||||
(1, 'Club Mate', 42, 200, 250)
|
||||
''')
|
||||
cursor.execute('''
|
||||
INSERT INTO transactions VALUES (1, 1, 4200, 0, 1000)
|
||||
''')
|
||||
cursor.execute('''
|
||||
INSERT INTO receipts VALUES (1, 1, 1, 1, 1337)
|
||||
''')
|
||||
cursor.execute('PRAGMA user_version = 3')
|
||||
|
||||
# Kick off the migration
|
||||
schema_version = self.db.SCHEMA_VERSION
|
||||
self.db.SCHEMA_VERSION = 4
|
||||
self.db._setup()
|
||||
self.db.SCHEMA_VERSION = schema_version
|
||||
|
||||
# Make sure entries from the receipts table are preserved
|
||||
cursor.execute('''SELECT COUNT(receipt_id) FROM receipts''')
|
||||
self.assertEqual(1, cursor.fetchone()[0])
|
||||
|
||||
# Make sure transaction IDs can be set to NULL
|
||||
cursor.execute('UPDATE receipts SET first_ta_id = NULL, last_ta_id = NULL')
|
||||
with patch('matemat.db.DatabaseWrapper.schema_version', new_callable=PropertyMock(return_value=1)):
|
||||
with self.assertRaises(RuntimeError):
|
||||
with self.db:
|
||||
pass
|
||||
|
|
|
@ -17,7 +17,7 @@ class DatabaseTest(unittest.TestCase):
|
|||
Test creation of database schema in an empty database
|
||||
"""
|
||||
with self.db as db:
|
||||
self.assertEqual(DatabaseWrapper.SCHEMA_VERSION, db._user_version)
|
||||
self.assertEqual(db.schema_version, db._user_version)
|
||||
|
||||
def test_in_transaction(self) -> None:
|
||||
"""
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
|
||||
from typing import Any, Optional
|
||||
|
||||
import sqlite3
|
||||
|
||||
from matemat.exceptions import DatabaseConsistencyError
|
||||
from matemat.db.schemas import SCHEMAS
|
||||
from matemat.db.migrations import *
|
||||
import matemat.db.migrations
|
||||
|
||||
|
||||
class DatabaseTransaction(object):
|
||||
|
@ -40,8 +41,6 @@ class DatabaseTransaction(object):
|
|||
|
||||
class DatabaseWrapper(object):
|
||||
|
||||
SCHEMA_VERSION = 10
|
||||
|
||||
def __init__(self, filename: str) -> None:
|
||||
self._filename: str = filename
|
||||
self._sqlite_db: Optional[sqlite3.Connection] = None
|
||||
|
@ -61,16 +60,11 @@ class DatabaseWrapper(object):
|
|||
def _setup(self) -> None:
|
||||
# Create or update schemas if necessary
|
||||
version: int = self._user_version
|
||||
if version < 1:
|
||||
# Don't use executescript, as it issues a COMMIT first
|
||||
with self.transaction() as c:
|
||||
for command in SCHEMAS[self.SCHEMA_VERSION]:
|
||||
c.execute(command)
|
||||
elif version < self.SCHEMA_VERSION:
|
||||
self._upgrade(from_version=version, to_version=self.SCHEMA_VERSION)
|
||||
elif version > self.SCHEMA_VERSION:
|
||||
if version < self.schema_version:
|
||||
self._upgrade(from_version=version, to_version=self.schema_version)
|
||||
self._user_version = self.schema_version
|
||||
elif version > self.schema_version:
|
||||
raise RuntimeError('Database schema is newer than supported by this version of Matemat.')
|
||||
self._user_version = self.SCHEMA_VERSION
|
||||
|
||||
# Enable foreign key enforcement
|
||||
cursor = self._sqlite_db.cursor()
|
||||
|
@ -79,35 +73,20 @@ class DatabaseWrapper(object):
|
|||
def _upgrade(self, from_version: int, to_version: int) -> None:
|
||||
if from_version >= to_version:
|
||||
return
|
||||
# Create backup before migration
|
||||
if self._filename != ':memory:':
|
||||
bakfile = f'{self._filename}_{from_version}_{to_version}.bak'
|
||||
bak = sqlite3.connect(bakfile)
|
||||
with bak:
|
||||
self._sqlite_db.backup(bak, pages=1)
|
||||
bak.close()
|
||||
# Iterate through migrations, executing them one by one
|
||||
with self.transaction() as c:
|
||||
c.execute('PRAGMA foreign_keys=OFF')
|
||||
c.execute('PRAGMA legacy_alter_table=ON')
|
||||
if from_version <= 1 and to_version >= 2:
|
||||
migrate_schema_1_to_2(c)
|
||||
if from_version <= 2 and to_version >= 3:
|
||||
migrate_schema_2_to_3(c)
|
||||
if from_version <= 3 and to_version >= 4:
|
||||
migrate_schema_3_to_4(c)
|
||||
if from_version <= 4 and to_version >= 5:
|
||||
migrate_schema_4_to_5(c)
|
||||
if from_version <= 5 and to_version >= 6:
|
||||
migrate_schema_5_to_6(c)
|
||||
if from_version <= 6 and to_version >= 7:
|
||||
migrate_schema_6_to_7(c)
|
||||
if from_version <= 7 and to_version >= 8:
|
||||
migrate_schema_7_to_8(c)
|
||||
if from_version <= 7 and to_version >= 8:
|
||||
migrate_schema_7_to_8(c)
|
||||
if from_version <= 8 and to_version >= 9:
|
||||
migrate_schema_8_to_9(c)
|
||||
if from_version <= 9 and to_version >= 10:
|
||||
migrate_schema_9_to_10(c)
|
||||
for i in range(from_version+1, to_version+1):
|
||||
migration = getattr(matemat.db.migrations, f'migrate_schema_{i}')
|
||||
migration(c)
|
||||
c.execute('PRAGMA foreign_key_check')
|
||||
c.execute('PRAGMA foreign_keys=ON')
|
||||
|
||||
|
@ -146,3 +125,13 @@ class DatabaseWrapper(object):
|
|||
raise RuntimeError(f'Database connection to {self._filename} is not established.')
|
||||
cursor = self._sqlite_db.cursor()
|
||||
cursor.execute(f'PRAGMA user_version = {version}')
|
||||
|
||||
@property
|
||||
def schema_version(self) -> int:
|
||||
max_migration = 0
|
||||
for name in dir(matemat.db.migrations):
|
||||
if not name.startswith('migrate_schema_'):
|
||||
continue
|
||||
migration = int(name.split('_')[2])
|
||||
max_migration = max(max_migration, migration)
|
||||
return max_migration
|
||||
|
|
Loading…
Reference in a new issue