Compare commits

..

7 Commits

Author SHA1 Message Date
Ferdinand Thiessen 2b6472b655 docs(migrations): Some documentation ++
continuous-integration/woodpecker the build failed Details
2021-12-21 22:53:30 +01:00
Ferdinand Thiessen 88718676f8 feat(docs): Add documentation on how to install tables
continuous-integration/woodpecker the build failed Details
2021-12-20 00:56:31 +01:00
Ferdinand Thiessen 3935f9386b fix(db): Remove print statement for debugging 2021-12-20 00:56:31 +01:00
Ferdinand Thiessen 5b1ccc9c07 feat(db): Add initial migrations for core Flaschengeist + balance and pricelist plugins 2021-12-20 00:56:31 +01:00
Ferdinand Thiessen e34dc5dd62 feat(db): Add migrations support to plugins 2021-12-20 00:56:31 +01:00
Ferdinand Thiessen 6ae0bdc3d9 fix(db): Add __repr__ to custom column types, same as done by SQLAlchemy 2021-12-20 00:56:31 +01:00
Ferdinand Thiessen b03df823d7 feat(db): Add database migration support, implements #19
Migrations allow us to keep track of database changes and upgrading databases if needed.
2021-12-20 00:56:31 +01:00
10 changed files with 32 additions and 94 deletions

View File

@ -1,6 +1,6 @@
pipeline: pipeline:
lint: lint:
image: python:slim image: python:alpine
commands: commands:
- pip install black - pip install black
- black --check --line-length 120 --target-version=py37 . - black --check --line-length 120 --target-version=py37 .

View File

@ -1,21 +0,0 @@
pipeline:
install:
image: python:${PYTHON}-slim
commands:
- python -m venv --clear venv
- export PATH=venv/bin:$PATH
- python -m pip install --upgrade pip
- pip install -v ".[tests]"
test:
image: python:${PYTHON}-slim
commands:
- export PATH=venv/bin:$PATH
- python -m pytest
matrix:
PYTHON:
- 3.10
- 3.9
- 3.8
- 3.7

View File

@ -101,7 +101,7 @@ def set_roles(user: User, roles: list[str], create=False):
Raises: Raises:
BadRequest if invalid arguments given or not all roles found while *create* is set to false BadRequest if invalid arguments given or not all roles found while *create* is set to false
""" """
from .roleController import create_role from roleController import create_role
if not isinstance(roles, list) and any([not isinstance(r, str) for r in roles]): if not isinstance(roles, list) and any([not isinstance(r, str) for r in roles]):
raise BadRequest("Invalid role name") raise BadRequest("Invalid role name")
@ -149,7 +149,7 @@ def get_user_by_role(role: Role):
return User.query.join(User.roles_).filter_by(role_id=role.id).all() return User.query.join(User.roles_).filter_by(role_id=role.id).all()
def get_user(uid, deleted=False) -> User: def get_user(uid, deleted=False):
"""Get an user by userid from database """Get an user by userid from database
Args: Args:
uid: Userid to search for uid: Userid to search for

View File

@ -48,7 +48,7 @@ class Serial(TypeDecorator):
"""Same as MariaDB Serial used for IDs""" """Same as MariaDB Serial used for IDs"""
cache_ok = True cache_ok = True
impl = BigInteger().with_variant(mysql.BIGINT(unsigned=True), "mysql").with_variant(sqlite.INTEGER(), "sqlite") impl = BigInteger().with_variant(mysql.BIGINT(unsigned=True), "mysql").with_variant(sqlite.INTEGER, "sqlite")
# https://alembic.sqlalchemy.org/en/latest/autogenerate.html?highlight=custom%20column#affecting-the-rendering-of-types-themselves # https://alembic.sqlalchemy.org/en/latest/autogenerate.html?highlight=custom%20column#affecting-the-rendering-of-types-themselves
def __repr__(self) -> str: def __repr__(self) -> str:

View File

@ -30,14 +30,13 @@ install_requires =
SQLAlchemy >= 1.4.28 SQLAlchemy >= 1.4.28
toml toml
werkzeug >= 2.0 werkzeug >= 2.0
PyMySQL;platform_system=='Windows'
mysqlclient;platform_system!='Windows'
[options.extras_require] [options.extras_require]
argon = argon2-cffi argon = argon2-cffi
ldap = flask_ldapconn; ldap3 ldap = flask_ldapconn; ldap3
tests = pytest; pytest-depends; coverage test = pytest; coverage
mysql =
PyMySQL;platform_system=='Windows'
mysqlclient;platform_system!='Windows'
[options.package_data] [options.package_data]
* = *.toml * = *.toml

View File

@ -3,7 +3,8 @@ import tempfile
import pytest import pytest
from flaschengeist import database from flaschengeist import database
from flaschengeist.app import create_app from flaschengeist.app import create_app, install_all
# read in SQL for populating test data # read in SQL for populating test data
with open(os.path.join(os.path.dirname(__file__), "data.sql"), "r") as f: with open(os.path.join(os.path.dirname(__file__), "data.sql"), "r") as f:
@ -24,14 +25,12 @@ def app():
app = create_app( app = create_app(
{ {
"TESTING": True, "TESTING": True,
"DATABASE": {"engine": "sqlite", "database": f"/{db_path}"}, "DATABASE": {"file_path": f"/{db_path}"},
"LOGGING": {"level": "DEBUG"}, "LOGGING": {"level": "DEBUG"},
} }
) )
with app.app_context(): with app.app_context():
database.db.create_all() install_all()
database.db.session.commit()
engine = database.db.engine engine = database.db.engine
with engine.connect() as connection: with engine.connect() as connection:
for statement in _data_sql: for statement in _data_sql:

View File

@ -1,8 +1,4 @@
INSERT INTO "user" ('userid', 'firstname', 'lastname', 'mail', 'deleted', 'id') VALUES ('user', 'Max', 'Mustermann', 'abc@def.gh', 0, 1); INSERT INTO user ('userid', 'firstname', 'lastname', 'mail', 'id') VALUES ('user', 'Max', 'Mustermann', 'abc@def.gh', 1);
INSERT INTO "user" ('userid', 'firstname', 'lastname', 'mail', 'deleted', 'id') VALUES ('deleted_user', 'John', 'Doe', 'doe@example.com', 1, 2);
-- Password = 1234 -- Password = 1234
INSERT INTO user_attribute VALUES(1,1,'password',X'800495c4000000000000008cc0373731346161336536623932613830366664353038656631323932623134393936393561386463353536623037363761323037623238346264623833313265323333373066376233663462643332666332653766303537333564366335393133366463366234356539633865613835643661643435343931376636626663343163653333643635646530386634396231323061316236386162613164373663663333306564306463303737303733336136353363393538396536343266393865942e'); INSERT INTO user_attribute VALUES(1,1,'password',X'800495c4000000000000008cc0373731346161336536623932613830366664353038656631323932623134393936393561386463353536623037363761323037623238346264623833313265323333373066376233663462643332666332653766303537333564366335393133366463366234356539633865613835643661643435343931376636626663343163653333643635646530386634396231323061316236386162613164373663663333306564306463303737303733336136353363393538396536343266393865942e');
INSERT INTO session ('expires', 'token', 'lifetime', 'id', 'user_id') VALUES ('2999-01-01 00:00:00', 'f4ecbe14be3527ca998143a49200e294', 600, 1, 1); INSERT INTO session ('expires', 'token', 'lifetime', 'id', 'user_id') VALUES ('2999-01-01 00:00:00', 'f4ecbe14be3527ca998143a49200e294', 600, 1, 1);
-- ROLES
INSERT INTO role ('name', 'id') VALUES ('role_1', 1);
INSERT INTO permission ('name', 'id') VALUES ('permission_1', 1);

View File

@ -15,9 +15,9 @@ def test_login(client):
# Login successful # Login successful
assert result.status_code == 201 assert result.status_code == 201
# User set correctly # User set correctly
assert json["userid"] == USERID assert json["user"]["userid"] == USERID
# Token works # Token works
assert client.get("/auth", headers={"Authorization": f"Bearer {json['token']}"}).status_code == 200 assert client.get("/auth", headers={"Authorization": f"Bearer {json['session']['token']}"}).status_code == 200
def test_login_decorator(client): def test_login_decorator(client):

17
tests/test_events.py Normal file
View File

@ -0,0 +1,17 @@
import pytest
from werkzeug.exceptions import BadRequest
import flaschengeist.plugins.events.event_controller as event_controller
from flaschengeist.plugins.events.models import EventType
VALID_TOKEN = "f4ecbe14be3527ca998143a49200e294"
EVENT_TYPE_NAME = "Test Type"
def test_create_event_type(app):
with app.app_context():
type = event_controller.create_event_type(EVENT_TYPE_NAME)
assert isinstance(type, EventType)
with pytest.raises(BadRequest):
event_controller.create_event_type(EVENT_TYPE_NAME)

View File

@ -1,52 +0,0 @@
import pytest
from werkzeug.exceptions import BadRequest, NotFound
from flaschengeist.controller import roleController, userController
from flaschengeist.models.user import User
VALID_TOKEN = "f4ecbe14be3527ca998143a49200e294"
def test_get_user(app):
with app.app_context():
user = userController.get_user("user")
assert user is not None and isinstance(user, User)
assert user.userid == "user"
user = userController.get_user("deleted_user", deleted=True)
assert user is not None and isinstance(user, User)
assert user.userid == "deleted_user"
with pytest.raises(NotFound):
user = userController.get_user("__does_not_exist__")
with pytest.raises(NotFound):
user = userController.get_user("__does_not_exist__", deleted=True)
with pytest.raises(NotFound):
user = userController.get_user("deleted_user")
def test_set_roles(app):
with app.app_context():
user = userController.get_user("user")
userController.set_roles(user, [])
assert user.roles_ == []
userController.set_roles(user, ["role_1"])
assert len(user.roles_) == 1 and user.roles_[0].id == 1
# Test unknown role + no create flag -> raise no changes
with pytest.raises(BadRequest):
userController.set_roles(user, ["__custom__"])
assert len(user.roles_) == 1
userController.set_roles(user, ["__custom__"], create=True)
assert len(user.roles_) == 1 and user.roles_[0].name == "__custom__"
assert roleController.get("__custom__").id == user.roles_[0].id
userController.set_roles(user, ["__custom__"], create=True)
assert len(user.roles_) == 1
userController.set_roles(user, ["__custom__", "role_1"])
assert len(user.roles_) == 2
userController.set_roles(user, [])
assert len(user.roles_) == 0