From d30c071c5d3d4cf8b2459223f3e64767d44a7069 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 20 Jul 2021 18:32:04 +0300 Subject: [PATCH] reorganized unit tests and fixed bug in cache handling --- README.md | 2 +- app/__init__.py | 24 ++++-------------------- app/decorators.py | 6 +++--- app/scodoc/sco_cache.py | 27 ++++++++++++--------------- app/utils/import_scodoc7_user_db.py | 2 +- app/views/__init__.py | 19 +++++++++++++++++-- config.py | 12 ++++-------- tests/__init__.py | 2 +- tests/conftest.py | 27 ++++++++++++++++++--------- tests/unit/test_users.py | 8 ++------ 10 files changed, 63 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index c61ec9753..f3dba3b90 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ Pour créer un utilisateur "super admin", c'est à dire admin dans tous les dép ## Tests - python -m unittest tests.test_users + pytest tests/unit/test_users.py # TODO diff --git a/app/__init__.py b/app/__init__.py index 714ffbd7f..6ebf1b3d3 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -24,36 +24,20 @@ from config import Config from app.scodoc import notesdb as ndb from app.scodoc import sco_cache -app = Flask(__name__) -app.config.from_object(Config) - -db = SQLAlchemy(app) -migrate = Migrate(app, db) +db = SQLAlchemy() +migrate = Migrate() login = LoginManager() login.login_view = "auth.login" login.login_message = "Please log in to access this page." mail = Mail() -bootstrap = Bootstrap(app) +bootstrap = Bootstrap() moment = Moment() cache = Cache(config={"CACHE_TYPE": "MemcachedCache"}) # XXX TODO: configuration file -@app.before_request -def open_dept_db_connection(): - # current_app.logger.info("open_dept_db_connection") - if hasattr(g, "scodoc_dept") and not hasattr(g, "db_conn") and g.scodoc_dept: - g.db_conn = ndb.open_dept_connection() - - -@app.teardown_request -def close_dept_db_connection(): - # current_app.logger.info("close_dept_db_connection") - if hasattr(g, "db_conn"): - ndb.close_dept_connection() - - def create_app(config_class=Config): + print("create_app") app = Flask(__name__, static_url_path="/ScoDoc/static", static_folder="static") app.logger.setLevel(logging.DEBUG) app.config.from_object(config_class) diff --git a/app/decorators.py b/app/decorators.py index cc637f81e..b28181d68 100644 --- a/app/decorators.py +++ b/app/decorators.py @@ -156,7 +156,7 @@ def scodoc7func(context): elif not hasattr(g, "scodoc_dept"): g.scodoc_dept = None # --- Open DB connection - app.open_dept_db_connection() + app.views.open_dept_db_connection() # --- Emulate Zope's REQUEST REQUEST = ZRequest() g.zrequest = REQUEST @@ -227,10 +227,10 @@ class ScoDoc7Context(object): def __init__(self, name=""): self.name = name - logging.getLogger(__name__).info("created %s" % self) + # logging.getLogger(__name__).info("created %s" % self) def populate(self, globals_dict): - logging.getLogger(__name__).info("populating context %s" % self) + # logging.getLogger(__name__).info("populating context %s" % self) for k in globals_dict: if (not k.startswith("_")) and ( isinstance(globals_dict[k], types.FunctionType) diff --git a/app/scodoc/sco_cache.py b/app/scodoc/sco_cache.py index 826d662d0..e51806b98 100644 --- a/app/scodoc/sco_cache.py +++ b/app/scodoc/sco_cache.py @@ -75,7 +75,7 @@ class ScoDocCache: @classmethod def _get_key(cls, oid): - return g.scodoc_dept + "_" + cls.prefix + oid + return g.scodoc_dept + "_" + cls.prefix + "_" + oid @classmethod def get(cls, oid): @@ -84,16 +84,10 @@ class ScoDocCache: @classmethod def set(cls, oid, value): - """Store evaluation""" - return CACHE.set(cls._get_key(oid), value, timeout=cls.timeout) - - @classmethod - def get_cached(cls, oid): - """get cached object. Clients should use .get() - This function is usefull for tests, because get() may be - overloaded by subclasses. - """ - return ScoDocCache.get(oid) + """Store value""" + key = cls._get_key(oid) + log(f"CACHE key={key}, timeout={cls.timeout}") + return CACHE.set(key, value, timeout=cls.timeout) @classmethod def delete(cls, oid): @@ -103,7 +97,10 @@ class ScoDocCache: @classmethod def delete_many(cls, oids): """Remove multiple keys at once""" - CACHE.delete_many([cls._get_key(oid) for oid in oids]) + # delete_many seems bugged: + # CACHE.delete_many([cls._get_key(oid) for oid in oids]) + for oid in oids: + cls.delete(oid) class EvaluationCache(ScoDocCache): @@ -190,13 +187,13 @@ class NotesTableCache(ScoDocCache): prefix = "NT" @classmethod - def get(cls, formsemestre_id): + def get(cls, formsemestre_id, compute=True): """Returns NotesTable for this formsemestre - If not in cache, build it and cache it. + If not in cache and compute is True, build it and cache it. """ key = cls._get_key(formsemestre_id) nt = CACHE.get(key) - if nt: + if nt or not compute: return nt from app.scodoc import notes_table diff --git a/app/utils/import_scodoc7_user_db.py b/app/utils/import_scodoc7_user_db.py index 0b569929a..de43f9d22 100644 --- a/app/utils/import_scodoc7_user_db.py +++ b/app/utils/import_scodoc7_user_db.py @@ -8,7 +8,7 @@ import psycopg2 import psycopg2.extras from flask import current_app -from app import app, db +from app import db from app.auth.models import User, Role diff --git a/app/views/__init__.py b/app/views/__init__.py index 0948a7890..24972e5a6 100644 --- a/app/views/__init__.py +++ b/app/views/__init__.py @@ -2,6 +2,8 @@ """ScoDoc Flask views """ from flask import Blueprint +from flask import g +from app.scodoc import notesdb as ndb scodoc_bp = Blueprint("scodoc", __name__) scolar_bp = Blueprint("scolar", __name__) @@ -10,6 +12,19 @@ users_bp = Blueprint("users", __name__) absences_bp = Blueprint("absences", __name__) essais_bp = Blueprint("essais", __name__) -from app.views import scodoc, notes, scolar, absences, users, essais -scolar.context.Notes = notes.context # XXX transitoire #sco8 +@scodoc_bp.before_app_request +def open_dept_db_connection(): + # current_app.logger.info("open_dept_db_connection") + if hasattr(g, "scodoc_dept") and not hasattr(g, "db_conn") and g.scodoc_dept: + g.db_conn = ndb.open_dept_connection() + + +@scodoc_bp.teardown_app_request +def close_dept_db_connection(arg): + # current_app.logger.info("close_dept_db_connection") + if hasattr(g, "db_conn"): + ndb.close_dept_connection() + + +from app.views import scodoc, notes, scolar, absences, users, essais diff --git a/config.py b/config.py index 85a4206da..1d0983834 100755 --- a/config.py +++ b/config.py @@ -7,7 +7,7 @@ BASEDIR = os.path.abspath(os.path.dirname(__file__)) load_dotenv(os.path.join(BASEDIR, ".env")) -class ConfigClass(object): +class Config: """General configuration. Mostly loaded from environment via .env""" SECRET_KEY = os.environ.get("SECRET_KEY") or "un-grand-secret-introuvable" @@ -30,6 +30,9 @@ class ConfigClass(object): BOOTSTRAP_SERVE_LOCAL = os.environ.get("BOOTSTRAP_SERVE_LOCAL") # for ScoDoc 7 compat (à changer) INSTANCE_HOME = os.environ.get("INSTANCE_HOME", "/opt/scodoc") + SCODOC_VAR_DIR = os.path.join( + os.environ.get("INSTANCE_HOME", "/opt/scodoc"), "var", "scodoc" + ) # For legacy ScoDoc7 installs: postgresql user SCODOC7_SQL_USER = os.environ.get("SCODOC7_SQL_USER", "www-data") @@ -37,10 +40,3 @@ class ConfigClass(object): # STATIC_URL_PATH = "/ScoDoc/static" # static_folder = "stat" # SERVER_NAME = os.environ.get("SERVER_NAME") - - def __init__(self): - """Used to build some config variable at startup time""" - self.SCODOC_VAR_DIR = os.path.join(self.INSTANCE_HOME, "var", "scodoc") - - -Config = ConfigClass() diff --git a/tests/__init__.py b/tests/__init__.py index adc64cee7..e0ee767fc 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,2 +1,2 @@ # -import tests.test_users +import tests.unit.test_users diff --git a/tests/conftest.py b/tests/conftest.py index daefded81..036c31122 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,20 +1,29 @@ import pytest -from app import app, db +from flask import g + +import app as myapp +from app import db, create_app from app.auth.models import User, Role, Permission +from app.scodoc import sco_bulletins_standard +from app.scodoc import notesdb as ndb -@pytest.fixture +@pytest.fixture() def test_client(): # Setup - app.config["TESTING"] = True - app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite://" + myapp.Config.TESTING = True + myapp.Config.SQLALCHEMY_DATABASE_URI = "sqlite://" + apptest = create_app() # Run tests: - with app.test_client() as client: - with app.app_context(): + with apptest.test_client() as client: + with apptest.app_context(): db.create_all() Role.insert_roles() + g.scodoc_dept = "RT" + g.db_conn = ndb.open_dept_connection() yield client - # Teardown: - db.session.remove() - db.drop_all() \ No newline at end of file + ndb.close_dept_connection() + # Teardown: + db.session.remove() + db.drop_all() diff --git a/tests/unit/test_users.py b/tests/unit/test_users.py index 51d819800..9036bbedd 100644 --- a/tests/unit/test_users.py +++ b/tests/unit/test_users.py @@ -4,14 +4,14 @@ Ré-écriture de test_users avec pytest. -Usage: +Usage: pytest tests/unit/test_users.py """ import pytest from tests.conftest import test_client from flask import current_app -from app import app, db +from app import db from app.auth.models import User, Role, Permission from app.scodoc.sco_roles_default import SCO_ROLES_DEFAULTS @@ -19,10 +19,6 @@ from app.scodoc.sco_roles_default import SCO_ROLES_DEFAULTS DEPT = "XX" -def test_test(test_client): - assert 1 == 1 - - def test_password_hashing(test_client): u = User(user_name="susan") db.session.add(u)