From e51855b398d01753749147f049701f6307576e8f Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Wed, 26 Feb 2020 14:52:58 -0500 Subject: [PATCH 01/29] bugfixing sqlalchemy func call --- python/sdssdb/sqlalchemy/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/sdssdb/sqlalchemy/__init__.py b/python/sdssdb/sqlalchemy/__init__.py index 91717263..d3273c79 100644 --- a/python/sdssdb/sqlalchemy/__init__.py +++ b/python/sdssdb/sqlalchemy/__init__.py @@ -99,11 +99,11 @@ def cone_search(self, ra, dec, a, b=None, pa=None, ra_col='ra', dec_col='dec'): dec_attr = getattr(self, dec_col) if b is None: - return fn.q3c_radial_query(ra_attr, dec_attr, ra, dec, a) + return func.q3c_radial_query(ra_attr, dec_attr, ra, dec, a) else: pa = pa or 0.0 ratio = b / a - return fn.q3c_ellipse_query(ra_attr, dec_attr, ra, dec, a, ratio, pa) + return func.q3c_ellipse_query(ra_attr, dec_attr, ra, dec, a, ratio, pa) @cone_search.expression def cone_search(cls, ra, dec, a, b=None, pa=None, ra_col='ra', dec_col='dec'): From 43c589ceeef67cb5ebea7fd2bb1831a2b3bd1b86 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Wed, 26 Feb 2020 15:23:16 -0500 Subject: [PATCH 02/29] adding sdss lore profile --- python/sdssdb/etc/sdssdb.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/sdssdb/etc/sdssdb.yml b/python/sdssdb/etc/sdssdb.yml index 4484d1c9..1b08b2d3 100644 --- a/python/sdssdb/etc/sdssdb.yml +++ b/python/sdssdb/etc/sdssdb.yml @@ -51,3 +51,9 @@ utahdb: host: db.sdss.utah.edu port: 5432 domain: db.sdss.utah.edu + +slore: + user: sdss + host: lore.sdss.utah.edu + port: 5432 + domain: lore.sdss.utah.edu \ No newline at end of file From b8743fc9a8ee210b9bfe352d27cdabd98a555fbe Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Wed, 26 Feb 2020 15:23:38 -0500 Subject: [PATCH 03/29] adding option for specifying a database version --- python/sdssdb/connection.py | 21 +++++++++++++++++++- python/sdssdb/sqlalchemy/archive/__init__.py | 3 ++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/python/sdssdb/connection.py b/python/sdssdb/connection.py index 871051f8..9498c537 100644 --- a/python/sdssdb/connection.py +++ b/python/sdssdb/connection.py @@ -59,18 +59,25 @@ class DatabaseConnection(six.with_metaclass(abc.ABCMeta)): autoconnect : bool Whether to autoconnect to the database using the profile parameters. Requites `.dbname` to be set. + dbversion : str + A database version. If specified, appends to dbname as "dbname_dbversion" + and becomes the dbname used for connection strings. """ #: The database name. dbname = None + dbversion = None - def __init__(self, dbname=None, profile=None, autoconnect=True): + def __init__(self, dbname=None, profile=None, autoconnect=True, dbversion=None): #: Reports whether the connection is active. self.connected = False self.profile = None self.dbname = dbname if dbname else self.dbname + self.dbversion = dbversion or self.dbversion + if self.dbversion: + self.dbname = f'{self.dbname}_{self.dbversion}' self.set_profile(profile=profile, connect=autoconnect) @@ -303,6 +310,18 @@ def become_user(self): self.become(user) + def change_version(self, dbversion): + ''' Change database version and attempt to reconnect + + Parameters: + dbversion (str): + A database version + ''' + self.dbversion = dbversion + dbname, *dbver = self.dbname.split('_') + self.dbname = f'{dbname}_{self.dbversion}' + self.connect(dbname=self.dbname, silent_on_fail=True) + if _peewee: diff --git a/python/sdssdb/sqlalchemy/archive/__init__.py b/python/sdssdb/sqlalchemy/archive/__init__.py index bdfc930c..882e7b49 100644 --- a/python/sdssdb/sqlalchemy/archive/__init__.py +++ b/python/sdssdb/sqlalchemy/archive/__init__.py @@ -19,7 +19,8 @@ class ArchiveDatabaseConnection(SQLADatabaseConnection): - dbname = 'archive_20190507' + dbname = 'archive' + dbversion = '20190711' base = ArchiveBase From 11b464fb08a9ee65e28d2a9744883d201f69f0e8 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 10 Mar 2020 10:27:10 -0400 Subject: [PATCH 04/29] adding option to change database version --- python/sdssdb/connection.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/python/sdssdb/connection.py b/python/sdssdb/connection.py index 9498c537..a65f2cc4 100644 --- a/python/sdssdb/connection.py +++ b/python/sdssdb/connection.py @@ -310,16 +310,16 @@ def become_user(self): self.become(user) - def change_version(self, dbversion): + def change_version(self, dbversion=None): ''' Change database version and attempt to reconnect Parameters: dbversion (str): - A database version + A database version ''' self.dbversion = dbversion dbname, *dbver = self.dbname.split('_') - self.dbname = f'{dbname}_{self.dbversion}' + self.dbname = f'{dbname}_{self.dbversion}' if dbversion else dbname self.connect(dbname=self.dbname, silent_on_fail=True) @@ -455,6 +455,7 @@ def _conn(self, dbname, silent_on_fail=False, **params): def reset_engine(self): ''' Reset the engine, metadata, and session ''' + self.bases = [] if self.engine: self.engine.dispose() self.engine = None From 54a93ae226832ce7842f0820b53d38db0522db74 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 10 Mar 2020 10:27:38 -0400 Subject: [PATCH 05/29] making sas.File.name property a hybrid one --- python/sdssdb/sqlalchemy/archive/sas.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/python/sdssdb/sqlalchemy/archive/sas.py b/python/sdssdb/sqlalchemy/archive/sas.py index dc56c018..0577beec 100644 --- a/python/sdssdb/sqlalchemy/archive/sas.py +++ b/python/sdssdb/sqlalchemy/archive/sas.py @@ -13,6 +13,8 @@ from sdssdb.sqlalchemy.archive import database, ArchiveBase from sqlalchemy.ext.declarative import AbstractConcreteBase, declared_attr from sqlalchemy.orm import relationship +from sqlalchemy.ext.hybrid import hybrid_property +from sqlalchemy import func SCHEMA = 'sas' @@ -50,10 +52,14 @@ class File(Base): __tablename__ = 'file' print_fields = ['name'] - @property + @hybrid_property def name(self): return self.location.rsplit('/', 1)[-1] + @name.expression + def name(cls): + return func.reverse(func.split_part(func.reverse(cls.location), '/', 1)) + class SymlinkFile(Base): __tablename__ = 'symlink_file' From 2b247d9b39692baa7f3308cd258d8e4929edc21e Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 10 Mar 2020 10:28:26 -0400 Subject: [PATCH 06/29] adding primary key to new catalogdb table --- python/sdssdb/sqlalchemy/sdss5db/catalogdb.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/sdssdb/sqlalchemy/sdss5db/catalogdb.py b/python/sdssdb/sqlalchemy/sdss5db/catalogdb.py index 72a145af..5614c0e0 100644 --- a/python/sdssdb/sqlalchemy/sdss5db/catalogdb.py +++ b/python/sdssdb/sqlalchemy/sdss5db/catalogdb.py @@ -193,6 +193,9 @@ class TwoMassCleanNoNeighbor(Base): class DR14QV44(Base): __tablename__ = 'dr14q_v4_4' + sdss_name = Column(String, primary_key=True) + + def define_relations(): From 23358527b054f57155393a8a9d361d5b0572e2e4 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 10 Mar 2020 10:30:11 -0400 Subject: [PATCH 07/29] making default archive latest version --- python/sdssdb/sqlalchemy/archive/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/sdssdb/sqlalchemy/archive/__init__.py b/python/sdssdb/sqlalchemy/archive/__init__.py index 882e7b49..e95771c5 100644 --- a/python/sdssdb/sqlalchemy/archive/__init__.py +++ b/python/sdssdb/sqlalchemy/archive/__init__.py @@ -20,8 +20,8 @@ class ArchiveDatabaseConnection(SQLADatabaseConnection): dbname = 'archive' - dbversion = '20190711' + dbversion = '20200203' base = ArchiveBase -database = ArchiveDatabaseConnection(autoconnect=True) +database = ArchiveDatabaseConnection(autoconnect=True, profile='local') From 92737a6316af115a9eca9a9a2f1e7a23488cebb1 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 10 Mar 2020 13:38:35 -0400 Subject: [PATCH 08/29] fixing archive db version --- python/sdssdb/sqlalchemy/archive/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/sdssdb/sqlalchemy/archive/__init__.py b/python/sdssdb/sqlalchemy/archive/__init__.py index e95771c5..2d2de7ad 100644 --- a/python/sdssdb/sqlalchemy/archive/__init__.py +++ b/python/sdssdb/sqlalchemy/archive/__init__.py @@ -20,7 +20,7 @@ class ArchiveDatabaseConnection(SQLADatabaseConnection): dbname = 'archive' - dbversion = '20200203' + dbversion = '20200302' base = ArchiveBase From f8d58b3d6f5c9f6572d61e5cdbade59ba5fc9eb5 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Mon, 23 Mar 2020 13:28:10 -0400 Subject: [PATCH 09/29] setting up initial infrastructure for testing databases --- python/sdssdb/connection.py | 2 + python/sdssdb/tests/conftest.py | 59 ++++++++++--- python/sdssdb/tests/pwdbs/__init__.py | 31 +++++++ python/sdssdb/tests/pwdbs/test_sdss5db.py | 14 ++++ python/sdssdb/tests/sqladbs/__init__.py | 84 +++++++++++++++++++ python/sdssdb/tests/sqladbs/conftest.py | 28 +++++++ python/sdssdb/tests/sqladbs/factories.py | 46 ++++++++++ python/sdssdb/tests/sqladbs/models.py | 25 ++++++ python/sdssdb/tests/sqladbs/test_archivedb.py | 27 ++++++ python/sdssdb/tests/sqladbs/test_factory.py | 50 +++++++++++ python/sdssdb/tests/sqladbs/test_mangadb.py | 71 ++++++++++++++++ python/sdssdb/tests/sqladbs/test_sdss5db.py | 25 ++++++ python/sdssdb/tests/test_sqlalchemy.py | 49 +++++++++++ 13 files changed, 500 insertions(+), 11 deletions(-) create mode 100644 python/sdssdb/tests/pwdbs/__init__.py create mode 100644 python/sdssdb/tests/pwdbs/test_sdss5db.py create mode 100644 python/sdssdb/tests/sqladbs/__init__.py create mode 100644 python/sdssdb/tests/sqladbs/conftest.py create mode 100644 python/sdssdb/tests/sqladbs/factories.py create mode 100644 python/sdssdb/tests/sqladbs/models.py create mode 100644 python/sdssdb/tests/sqladbs/test_archivedb.py create mode 100644 python/sdssdb/tests/sqladbs/test_factory.py create mode 100644 python/sdssdb/tests/sqladbs/test_mangadb.py create mode 100644 python/sdssdb/tests/sqladbs/test_sdss5db.py create mode 100644 python/sdssdb/tests/test_sqlalchemy.py diff --git a/python/sdssdb/connection.py b/python/sdssdb/connection.py index a65f2cc4..0b2ffa6c 100644 --- a/python/sdssdb/connection.py +++ b/python/sdssdb/connection.py @@ -445,6 +445,8 @@ def _conn(self, dbname, silent_on_fail=False, **params): self.engine.dispose() self.engine = None self.connected = False + self.Session = None + self.metadata = None else: self.connected = True self.dbname = dbname diff --git a/python/sdssdb/tests/conftest.py b/python/sdssdb/tests/conftest.py index 71b51e11..289d6147 100644 --- a/python/sdssdb/tests/conftest.py +++ b/python/sdssdb/tests/conftest.py @@ -3,17 +3,54 @@ # conftest.py # +from __future__ import print_function, division, absolute_import, unicode_literals -from __future__ import division -from __future__ import print_function -from __future__ import absolute_import -from __future__ import unicode_literals +import pytest +import os +import importlib +import sdssdb.tests.sqladbs as sqladbs +from sdssdb.tests.sqladbs import prepare_testdb +from pytest_postgresql.factories import DatabaseJanitor +# from pytest_factoryboy import register +# from sdssdb.tests.sqladbs.testdb import TableFactory +# register(TableFactory) -""" -Here you can add fixtures that will be used for all the tests in this -directory. You can also add conftest.py files in underlying subdirectories. -Those conftest.py will only be applies to the tests in that subdirectory and -underlying directories. See https://docs.pytest.org/en/2.7.3/plugins.html for -more information. -""" + +@pytest.fixture(scope='session', autouse=True) +def skipdb(database): + ''' fixture to skip database tests if the db does not exist ''' + if database.connected is False: + pytest.skip(f'no {database.dbname} found') + database = None + + +@pytest.fixture(scope='session') +def dropdb(): + janitor = DatabaseJanitor('postgres', 'localhost', 5432, 'test', '11.4') + janitor.drop() + + +@pytest.fixture(scope='session') +def database(dropdb, request): + if hasattr(request, 'param'): + # yield a real database + yield request.param + else: + # initialize the test database + janitor = DatabaseJanitor('postgres', 'localhost', 5432, 'test', '11.4') + janitor.init() + db = prepare_testdb() + yield db + db = None + janitor.drop() + + +@pytest.fixture(scope='function') +def session(database): + ''' SQLA session fixture. set autouse=True to ensure persistence ''' + session = database.Session() + session.begin() + yield session + session.rollback() + session.close() diff --git a/python/sdssdb/tests/pwdbs/__init__.py b/python/sdssdb/tests/pwdbs/__init__.py new file mode 100644 index 00000000..5ca56534 --- /dev/null +++ b/python/sdssdb/tests/pwdbs/__init__.py @@ -0,0 +1,31 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: __init__.py +# Project: peewee +# Author: Brian Cherinka +# Created: Sunday, 1st March 2020 1:47:12 pm +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Sunday, 1st March 2020 1:48:16 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import + +from sdssdb.connection import PeeweeDatabaseConnection +from sdssdb.peewee import BaseModel + + +class TmpDatabaseConnection(PeeweeDatabaseConnection): + dbname = 'test' + + +database = TmpDatabaseConnection(autoconnect=False, profile='local') + + +# Create a new base temp model class +class TmpModel(BaseModel): + + class Meta: + database = database diff --git a/python/sdssdb/tests/pwdbs/test_sdss5db.py b/python/sdssdb/tests/pwdbs/test_sdss5db.py new file mode 100644 index 00000000..c539265b --- /dev/null +++ b/python/sdssdb/tests/pwdbs/test_sdss5db.py @@ -0,0 +1,14 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: test_sdss5db.py +# Project: peewee_dbs +# Author: Brian Cherinka +# Created: Sunday, 1st March 2020 2:56:48 pm +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Sunday, 1st March 2020 2:56:49 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import diff --git a/python/sdssdb/tests/sqladbs/__init__.py b/python/sdssdb/tests/sqladbs/__init__.py new file mode 100644 index 00000000..895640a1 --- /dev/null +++ b/python/sdssdb/tests/sqladbs/__init__.py @@ -0,0 +1,84 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: __init__.py +# Project: sqla +# Author: Brian Cherinka +# Created: Sunday, 1st March 2020 11:46:32 am +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Monday, 23rd March 2020 1:24:38 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import + +import six +import inspect +import importlib +from sqlalchemy.ext.declarative import declarative_base, DeferredReflection +from sdssdb.connection import SQLADatabaseConnection +from sdssdb.sqlalchemy import BaseModel + + +TmpBase = declarative_base(cls=(DeferredReflection, BaseModel,)) + + +class TmpDatabaseConnection(SQLADatabaseConnection): + dbname = 'test' + base = TmpBase + + +database = TmpDatabaseConnection(autoconnect=False, profile='local') + + +def prepare_testdb(): + ''' connect and set up test models after db initialization ''' + database.reset_engine() + database.connect() + database.base.metadata.create_all(database.engine) + database.add_base(TmpBase) + database.prepare_bases() + return database + + +def get_model_from_database(database, modelname): + ''' get a model from a database module + + Loads a module of Model Classes for a given database. If modelname specifies + a specific model class, will return the individual model class instead of entire + module. + + Parameters: + database (DatabaseConnection): + An sdssdb DatabaseConnection + modelname (str): + The module name containing the Model Base classes, e.g. datadb + + Returns: + The module of Model Classes or an individual Model Class + + Example: + >>> # load the datadb module of ModelClass for mangadb + >>> get_model_from_database(mangadb, 'datadb') + >>> + >>> # load only the datadb.Wavelength Model class + >>> get_model_from_database(mangadb, 'datadb.Wavelength') + ''' + # if no database, return nothing + if database.connected is False: + return None + + # extract the module the database is from + dbmod = inspect.getmodule(database) + + # load the model module or an indvidual model class + assert isinstance(modelname, six.string_types) + if '.' in modelname: + model_mod, model_class = modelname.split('.') + model = importlib.import_module(dbmod.__name__ + '.' + model_mod) + return getattr(model, model_class, None) + + model = importlib.import_module(dbmod.__name__ + '.' + modelname) + + return model diff --git a/python/sdssdb/tests/sqladbs/conftest.py b/python/sdssdb/tests/sqladbs/conftest.py new file mode 100644 index 00000000..d8365286 --- /dev/null +++ b/python/sdssdb/tests/sqladbs/conftest.py @@ -0,0 +1,28 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: conftest.py +# Project: sqladbs +# Author: Brian Cherinka +# Created: Sunday, 1st March 2020 5:46:55 pm +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Sunday, 1st March 2020 6:27:01 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import +import factory +import inspect +from pytest_factoryboy import register +from . import factories + + +# register all xxxFactory classes +# have to register manually in conftest, instead of with @register class decorator, when +# factories organized in separate files compared to models and tests +for item in dir(factories): + attr = getattr(factories, item) + if inspect.isclass(attr) and issubclass(attr, factory.Factory): + register(attr) + diff --git a/python/sdssdb/tests/sqladbs/factories.py b/python/sdssdb/tests/sqladbs/factories.py new file mode 100644 index 00000000..c40ba34c --- /dev/null +++ b/python/sdssdb/tests/sqladbs/factories.py @@ -0,0 +1,46 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: factories.py +# Project: sqladbs +# Author: Brian Cherinka +# Created: Sunday, 1st March 2020 5:45:43 pm +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Monday, 23rd March 2020 1:25:18 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import +import factory +from sdssdb.tests.sqladbs import models, database as db, get_model_from_database +from sdssdb.sqlalchemy.mangadb import database as mangadb +from sdssdb.sqlalchemy.archive import database as archive + +faker = factory.faker.faker.Factory().create() + +# need to load Model Classes this way for cases where the database does not exist for a test +datadb = get_model_from_database(mangadb, 'datadb') +sas = get_model_from_database(archive, 'sas') + + +class UserFactory(factory.alchemy.SQLAlchemyModelFactory): + ''' factory for testdb user table''' + class Meta: + model = models.User + sqlalchemy_session = db.Session # the SQLAlchemy session object + + id = factory.Sequence(lambda n: n) + name = factory.Faker('first_name') + essence = 'human' + + +class WaveFactory(factory.alchemy.SQLAlchemyModelFactory): + ''' factory for mangadb wavelength table ''' + class Meta: + model = datadb.Wavelength + sqlalchemy_session = mangadb.Session # the SQLAlchemy session object + + pk = factory.Sequence(lambda n: n) + wavelength = faker.pylist(10, False, 'float') + bintype = 'NAN' diff --git a/python/sdssdb/tests/sqladbs/models.py b/python/sdssdb/tests/sqladbs/models.py new file mode 100644 index 00000000..040c5367 --- /dev/null +++ b/python/sdssdb/tests/sqladbs/models.py @@ -0,0 +1,25 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: models.py +# Project: sqladbs +# Author: Brian Cherinka +# Created: Sunday, 1st March 2020 5:45:37 pm +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Monday, 2nd March 2020 8:42:51 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import + +from sqlalchemy import Column, Integer, String +from sdssdb.tests.sqladbs import TmpBase + + +class User(TmpBase): + ''' model for user on test database ''' + __tablename__ = 'user' + id = Column(Integer, primary_key=True) + name = Column(String(80)) + essence = Column(String(10)) diff --git a/python/sdssdb/tests/sqladbs/test_archivedb.py b/python/sdssdb/tests/sqladbs/test_archivedb.py new file mode 100644 index 00000000..f681e577 --- /dev/null +++ b/python/sdssdb/tests/sqladbs/test_archivedb.py @@ -0,0 +1,27 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: test_archivedb.py +# Project: sqladbs +# Author: Brian Cherinka +# Created: Monday, 2nd March 2020 1:24:15 pm +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Sunday, 22nd March 2020 4:44:00 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import +from sdssdb.sqlalchemy.archive import database +if database.connected: + from sdssdb.sqlalchemy.archive import sas +import pytest + +pytestmark = pytest.mark.nodb + +@pytest.mark.parametrize('database', [database], indirect=True) +class TestArchiveDb(object): + + def test_tree_list(self, session): + tree_count = session.query(sas.Tree).count() + assert tree_count > 0 diff --git a/python/sdssdb/tests/sqladbs/test_factory.py b/python/sdssdb/tests/sqladbs/test_factory.py new file mode 100644 index 00000000..49fbd7e0 --- /dev/null +++ b/python/sdssdb/tests/sqladbs/test_factory.py @@ -0,0 +1,50 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: test_testdb.py +# Project: sqladbs +# Author: Brian Cherinka +# Created: Sunday, 1st March 2020 3:17:17 pm +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Thursday, 12th March 2020 12:02:21 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import + +import pytest +from sdssdb.tests.sqladbs import models, database + + +@pytest.fixture(scope='function', autouse=True) +def fix_session(user_factory): + ''' fixture to replace factory session with real db session after initialization ''' + user_factory._meta.sqlalchemy_session = database.Session + + +@pytest.fixture() +def batchit(user_factory): + user_factory.create_batch(10) + + +class TestFactory(object): + + def test_factory_fixture(self, session, user_factory): + ''' test the factory can create new entries ''' + print('tf', user_factory._meta.sqlalchemy_session, database.Session) + table = user_factory(name="Test Human") + assert table.id == 0 + assert table.name == "Test Human" + assert table.essence == 'human' + + def test_a_transaction(self, session, batchit): + rows = session.query(models.User).all() + print('rows', rows) + assert len(rows) == 10 + + def test_model_fixture(self, user): + ''' test a single new instance of model Table is created ''' + assert isinstance(user, models.User) + assert user.essence == 'human' + assert user.name != 'Test Human' diff --git a/python/sdssdb/tests/sqladbs/test_mangadb.py b/python/sdssdb/tests/sqladbs/test_mangadb.py new file mode 100644 index 00000000..77328b52 --- /dev/null +++ b/python/sdssdb/tests/sqladbs/test_mangadb.py @@ -0,0 +1,71 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: test_mangadb.py +# Project: sqlalchemy +# Author: Brian Cherinka +# Created: Friday, 23rd August 2019 12:35:00 pm +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2019 Brian Cherinka +# Last Modified: Sunday, 22nd March 2020 4:44:35 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import + +# from sqlalchemy import Column, Integer, String, Float +# from sqlalchemy.orm import deferred, relationship +# #from ..conftest import TmpBase +# from sqlalchemy.dialects.postgresql import ARRAY +import factory +from pytest_factoryboy import register +from sdssdb.sqlalchemy.mangadb import database +if database.connected: + from sdssdb.sqlalchemy.mangadb import datadb +import pytest + +faker = factory.faker.faker.Factory().create() + + +@pytest.fixture(scope='session', autouse=True) +def skipdb(): + if database.connected is False: + pytest.skip('no mangadb found') + + +# class Wavelength(TmpBase): +# __tablename__ = 'wavelength' +# __table_args__ = {'schema': 'mangadatadb'} +# pk = Column(Integer, primary_key=True) +# wavelength = deferred(Column(ARRAY(Float, zero_indexes=True))) +# bintype = Column(String(10)) + + +# @register +# class WaveFactory(factory.alchemy.SQLAlchemyModelFactory): +# class Meta: +# model = datadb.Wavelength +# sqlalchemy_session = mangadb.Session # the SQLAlchemy session object + +# pk = factory.Sequence(lambda n: n) +# wavelength = faker.pylist(10, False, 'float') +# bintype = 'NAN' + + +@pytest.mark.parametrize('database', [database], indirect=True) +class TestMangaDB(object): + + def test_added_wavelength(self, session, wave_factory): + ''' test that we can add fake rows to real dbs that are undone ''' + wave_factory.create() + rows = session.query(datadb.Wavelength).all() + assert len(rows) == 2 + assert rows[0].bintype == 'LOG' + assert rows[0].wavelength[0] == 3621.6 + assert rows[1].bintype == 'NAN' + assert rows[1].wavelength[0] != 3621.6 + + def test_cube_count(self, session): + ''' test of a simple table count ''' + cc = session.query(datadb.Cube).count() + assert cc > 1 diff --git a/python/sdssdb/tests/sqladbs/test_sdss5db.py b/python/sdssdb/tests/sqladbs/test_sdss5db.py new file mode 100644 index 00000000..7fedd42d --- /dev/null +++ b/python/sdssdb/tests/sqladbs/test_sdss5db.py @@ -0,0 +1,25 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: test_sdss5db.py +# Project: sqladbs +# Author: Brian Cherinka +# Created: Wednesday, 4th March 2020 3:01:10 pm +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Sunday, 22nd March 2020 4:44:53 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import +from sdssdb.sqlalchemy.sdss5db import database +if database.connected: + from sdssdb.sqlalchemy.sdss5db import catalogdb +import pytest + + +@pytest.mark.parametrize('database', [database], indirect=True) +class TestSDSS5Db(object): + + def test_allwise_list(self, session): + aw_count = session.query(catalogdb.AllWise).count() diff --git a/python/sdssdb/tests/test_sqlalchemy.py b/python/sdssdb/tests/test_sqlalchemy.py new file mode 100644 index 00000000..8b9aa740 --- /dev/null +++ b/python/sdssdb/tests/test_sqlalchemy.py @@ -0,0 +1,49 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: test_sqlalchemy.py +# Project: tests +# Author: Brian Cherinka +# Created: Sunday, 1st March 2020 11:48:23 am +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Monday, 2nd March 2020 1:54:48 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import + + +class TestSQLAConnection(object): + + def _assert_testdb(self, database): + assert database.dbname == 'test' + assert database.connected is True + assert database.profile == 'local' + assert database.dbversion is None + + def test_db_connected(self, database): + ''' test connection to testdb ''' + self._assert_testdb(database) + + def test_list_profiles(self, database): + ''' test profiles ''' + profs = database.list_profiles() + assert 'local' in profs + assert 'manga' in profs + assert 'apo' in profs + + def test_change_version(self, database): + ''' test to change db versions ''' + self._assert_testdb(database) + + # change to + database.change_version('v1') + assert database.dbname == 'test_v1' + assert database.dbversion == 'v1' + assert database.connected is False + + # change back + database.change_version() + self._assert_testdb(database) + From 43d83eb96cf2f3ce1f17932273bb273f69956492 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Mon, 23 Mar 2020 18:29:00 -0400 Subject: [PATCH 10/29] adding initial peewee tests --- python/sdssdb/tests/conftest.py | 55 ++++++++++++++--- python/sdssdb/tests/pwdbs/__init__.py | 11 +++- python/sdssdb/tests/pwdbs/conftest.py | 29 +++++++++ python/sdssdb/tests/pwdbs/factories.py | 29 +++++++++ python/sdssdb/tests/pwdbs/factoryboy.py | 60 +++++++++++++++++++ python/sdssdb/tests/pwdbs/models.py | 24 ++++++++ python/sdssdb/tests/pwdbs/test_factory.py | 43 +++++++++++++ python/sdssdb/tests/pwdbs/test_sdss5db.py | 16 ++++- python/sdssdb/tests/sqladbs/conftest.py | 5 +- python/sdssdb/tests/sqladbs/factories.py | 35 ++++++++--- python/sdssdb/tests/sqladbs/test_archivedb.py | 3 +- python/sdssdb/tests/sqladbs/test_factory.py | 4 +- python/sdssdb/tests/test_peewee.py | 22 +++---- 13 files changed, 301 insertions(+), 35 deletions(-) create mode 100644 python/sdssdb/tests/pwdbs/conftest.py create mode 100644 python/sdssdb/tests/pwdbs/factories.py create mode 100644 python/sdssdb/tests/pwdbs/factoryboy.py create mode 100644 python/sdssdb/tests/pwdbs/models.py create mode 100644 python/sdssdb/tests/pwdbs/test_factory.py diff --git a/python/sdssdb/tests/conftest.py b/python/sdssdb/tests/conftest.py index 289d6147..8f724d33 100644 --- a/python/sdssdb/tests/conftest.py +++ b/python/sdssdb/tests/conftest.py @@ -5,11 +5,12 @@ from __future__ import print_function, division, absolute_import, unicode_literals +import re import pytest -import os import importlib -import sdssdb.tests.sqladbs as sqladbs -from sdssdb.tests.sqladbs import prepare_testdb +import inspect +from sdssdb.tests.sqladbs import prepare_testdb as sqla_prepdb +from sdssdb.tests.pwdbs import prepare_testdb as pw_prepdb from pytest_postgresql.factories import DatabaseJanitor # from pytest_factoryboy import register @@ -17,7 +18,42 @@ # register(TableFactory) -@pytest.fixture(scope='session', autouse=True) +# def pytest_addoption(parser): +# """ Add new options to the pytest command-line """ +# # only run peewee tests +# parser.addoption('--peewee', action='store_true', default=False, +# help='Only run tests for peewee dbs') +# # only run sqla tests +# parser.addoption('--sqla', action='store_true', default=False, +# help='Only run tests for sqlalchemy dbs') + + +def pytest_ignore_collect(path, config): + ''' pytest hook to identify tests to be ignored during collection + + Looks through all test_xxx.py files in pwdbs and sqladbs and determines + which ones have databases that fail to connect and ignores them. + + ''' + + # identify and ignore test modules where no local + # database is set up for those tests + if re.search('test_[a-z]+.py', str(path)): + # get module name + modname = inspect.getmodulename(path) + # find and load the underlying module + spec = importlib.util.spec_from_file_location(modname, path) + foo = importlib.util.module_from_spec(spec) + # execute load + spec.loader.exec_module(foo) + # get the database from the module + db = getattr(foo, 'database', None) + # check if db is connected + if db and db.connected is False and db.dbname != 'test': + return True + + +@pytest.fixture(scope='module', autouse=True) def skipdb(database): ''' fixture to skip database tests if the db does not exist ''' if database.connected is False: @@ -25,28 +61,31 @@ def skipdb(database): database = None -@pytest.fixture(scope='session') +@pytest.fixture(scope='module') def dropdb(): janitor = DatabaseJanitor('postgres', 'localhost', 5432, 'test', '11.4') janitor.drop() -@pytest.fixture(scope='session') +@pytest.fixture(scope='module') def database(dropdb, request): + ''' Module fixture to initialize a real database or a test postgresql database ''' if hasattr(request, 'param'): # yield a real database yield request.param else: + # check if request is coming from a sqla db or peewee db + issqla = 'sqladbs' in request.module.__name__ # initialize the test database janitor = DatabaseJanitor('postgres', 'localhost', 5432, 'test', '11.4') janitor.init() - db = prepare_testdb() + db = sqla_prepdb() if issqla else pw_prepdb() yield db db = None janitor.drop() -@pytest.fixture(scope='function') +@pytest.fixture(scope='module') def session(database): ''' SQLA session fixture. set autouse=True to ensure persistence ''' session = database.Session() diff --git a/python/sdssdb/tests/pwdbs/__init__.py b/python/sdssdb/tests/pwdbs/__init__.py index 5ca56534..5a77efaa 100644 --- a/python/sdssdb/tests/pwdbs/__init__.py +++ b/python/sdssdb/tests/pwdbs/__init__.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 1:47:12 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Sunday, 1st March 2020 1:48:16 pm +# Last Modified: Monday, 23rd March 2020 5:03:45 pm # Modified By: Brian Cherinka @@ -29,3 +29,12 @@ class TmpModel(BaseModel): class Meta: database = database + + +def prepare_testdb(): + ''' connect and set up test models after db initialization ''' + models = TmpModel.__subclasses__() + database.bind(models, bind_refs=False, bind_backrefs=False) + database.connect() + database.create_tables(models) + return database diff --git a/python/sdssdb/tests/pwdbs/conftest.py b/python/sdssdb/tests/pwdbs/conftest.py new file mode 100644 index 00000000..8c1dd792 --- /dev/null +++ b/python/sdssdb/tests/pwdbs/conftest.py @@ -0,0 +1,29 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: conftest.py +# Project: pwdbs +# Author: Brian Cherinka +# Created: Monday, 23rd March 2020 4:41:32 pm +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Monday, 23rd March 2020 5:18:29 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import +import factory +import inspect +from pytest_factoryboy import register +from . import factories + + +# register all xxxFactory classes +# have to register manually in conftest, instead of with @register class decorator, when +# factories organized in separate files compared to models and tests +for item in dir(factories): + if item == 'PeeweeModelFactory': + continue + attr = getattr(factories, item) + if inspect.isclass(attr) and issubclass(attr, factory.Factory): + register(attr) diff --git a/python/sdssdb/tests/pwdbs/factories.py b/python/sdssdb/tests/pwdbs/factories.py new file mode 100644 index 00000000..f7e292f6 --- /dev/null +++ b/python/sdssdb/tests/pwdbs/factories.py @@ -0,0 +1,29 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: factories.py +# Project: pwdbs +# Author: Brian Cherinka +# Created: Monday, 23rd March 2020 4:35:14 pm +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Monday, 23rd March 2020 4:40:31 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import +import factory +from sdssdb.tests.pwdbs import models, database +from .factoryboy import PeeweeModelFactory + +faker = factory.faker.faker.Factory().create() + + +class UserFactory(PeeweeModelFactory): + class Meta: + model = models.User + database = database + + id = factory.Sequence(lambda n: n) + name = factory.Faker('first_name') + essence = 'human' diff --git a/python/sdssdb/tests/pwdbs/factoryboy.py b/python/sdssdb/tests/pwdbs/factoryboy.py new file mode 100644 index 00000000..5b8ddf1b --- /dev/null +++ b/python/sdssdb/tests/pwdbs/factoryboy.py @@ -0,0 +1,60 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: peewee_factory.py +# Project: pwdbs +# Author: Brian Cherinka +# Created: Monday, 23rd March 2020 4:32:17 pm +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Monday, 23rd March 2020 5:34:56 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import + +import peewee +from factory import base + +# +# This code, copied from https://github.com/cam-stitt/factory_boy-peewee, +# implements a factory_boy Model Factory for Peewee ORM models since +# factory_boy does not support the peewee ORM +# + + +class PeeweeOptions(base.FactoryOptions): + def _build_default_options(self): + return super(PeeweeOptions, self)._build_default_options() + [ + base.OptionDefault('database', None, inherit=True), + ] + + +class PeeweeModelFactory(base.Factory): + """Factory for peewee models. """ + + _options_class = PeeweeOptions + + class Meta: + abstract = True + + @classmethod + def _setup_next_sequence(cls, *args, **kwargs): + """Compute the next available PK, based on the 'pk' database field.""" + db = cls._meta.database + model = cls._meta.model + pk = getattr(model, model._meta.primary_key.name) + max_pk = (model.select(peewee.fn.Max(pk).alias('maxpk')) + .limit(1).order_by().execute()) + max_pk = [mp.maxpk for mp in max_pk][0] + if isinstance(max_pk, int): + return max_pk + 1 if max_pk else 1 + else: + return 1 + + @classmethod + def _create(cls, target_class, *args, **kwargs): + """Create an instance of the model, and save it to the database.""" + db = cls._meta.database + obj = target_class.create(**kwargs) + return obj diff --git a/python/sdssdb/tests/pwdbs/models.py b/python/sdssdb/tests/pwdbs/models.py new file mode 100644 index 00000000..259c398a --- /dev/null +++ b/python/sdssdb/tests/pwdbs/models.py @@ -0,0 +1,24 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: models.py +# Project: pwdbs +# Author: Brian Cherinka +# Created: Monday, 23rd March 2020 4:35:07 pm +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Monday, 23rd March 2020 4:57:34 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import +from peewee import IntegerField, CharField, AutoField +from sdssdb.tests.pwdbs import TmpModel + + +class User(TmpModel): + ''' model for user on test database ''' + __tablename__ = 'user' + id = AutoField() + name = CharField() + essence = CharField() diff --git a/python/sdssdb/tests/pwdbs/test_factory.py b/python/sdssdb/tests/pwdbs/test_factory.py new file mode 100644 index 00000000..75ca88a7 --- /dev/null +++ b/python/sdssdb/tests/pwdbs/test_factory.py @@ -0,0 +1,43 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: test_factory.py +# Project: pwdbs +# Author: Brian Cherinka +# Created: Monday, 23rd March 2020 4:20:08 pm +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Monday, 23rd March 2020 6:21:03 pm +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import +from sdssdb.tests.pwdbs import models, database +import pytest + + +@pytest.fixture() +def batchit(user_factory): + user_factory.create_batch(10) + + +class TestFactory(object): + + def test_factory_fixture(self, user_factory): + ''' test the factory can create new entries ''' + print('tf', user_factory) + user = user_factory(name="Test Human") + assert user.id == 1 + assert user.name == "Test Human" + assert user.essence == 'human' + + def test_a_transaction(self, batchit): + rows = list(models.User.select()) + print('rows', rows) + assert len(rows) == 11 + + def test_model_fixture(self, user): + ''' test a single new instance of model Table is created ''' + assert isinstance(user, models.User) + assert user.essence == 'human' + assert user.name != 'Test Human' diff --git a/python/sdssdb/tests/pwdbs/test_sdss5db.py b/python/sdssdb/tests/pwdbs/test_sdss5db.py index c539265b..e595c398 100644 --- a/python/sdssdb/tests/pwdbs/test_sdss5db.py +++ b/python/sdssdb/tests/pwdbs/test_sdss5db.py @@ -7,8 +7,22 @@ # Created: Sunday, 1st March 2020 2:56:48 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Sunday, 1st March 2020 2:56:49 pm +# Last Modified: Monday, 23rd March 2020 6:27:34 pm # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import +import pytest +from sdssdb.peewee.sdss5db import database, catalogdb, targetdb + + +@pytest.mark.parametrize('database', [database], indirect=True) +class TestSDSS5Db(object): + + def test_allwise_list(self): + aw_count = catalogdb.AllWise.select().count() + assert aw_count > 0 + + def test_targetdb_count(self): + nt = targetdb.Target.select().count() + assert nt > 0 diff --git a/python/sdssdb/tests/sqladbs/conftest.py b/python/sdssdb/tests/sqladbs/conftest.py index d8365286..0ce69cb3 100644 --- a/python/sdssdb/tests/sqladbs/conftest.py +++ b/python/sdssdb/tests/sqladbs/conftest.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 5:46:55 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Sunday, 1st March 2020 6:27:01 pm +# Last Modified: Monday, 23rd March 2020 6:13:45 pm # Modified By: Brian Cherinka @@ -15,6 +15,9 @@ import factory import inspect from pytest_factoryboy import register + +# pytest_factoryboy registers fixtures in directory where it is called +# need to import them all into sqla conftest.py from . import factories diff --git a/python/sdssdb/tests/sqladbs/factories.py b/python/sdssdb/tests/sqladbs/factories.py index c40ba34c..5e902ef5 100644 --- a/python/sdssdb/tests/sqladbs/factories.py +++ b/python/sdssdb/tests/sqladbs/factories.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 5:45:43 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Monday, 23rd March 2020 1:25:18 pm +# Last Modified: Monday, 23rd March 2020 6:13:02 pm # Modified By: Brian Cherinka @@ -16,6 +16,8 @@ from sdssdb.tests.sqladbs import models, database as db, get_model_from_database from sdssdb.sqlalchemy.mangadb import database as mangadb from sdssdb.sqlalchemy.archive import database as archive +#from pytest_factoryboy import register + faker = factory.faker.faker.Factory().create() @@ -24,6 +26,7 @@ sas = get_model_from_database(archive, 'sas') +#@register class UserFactory(factory.alchemy.SQLAlchemyModelFactory): ''' factory for testdb user table''' class Meta: @@ -35,12 +38,26 @@ class Meta: essence = 'human' -class WaveFactory(factory.alchemy.SQLAlchemyModelFactory): - ''' factory for mangadb wavelength table ''' - class Meta: - model = datadb.Wavelength - sqlalchemy_session = mangadb.Session # the SQLAlchemy session object +if datadb: + #@register + class WaveFactory(factory.alchemy.SQLAlchemyModelFactory): + ''' factory for mangadb wavelength table ''' + class Meta: + model = datadb.Wavelength + sqlalchemy_session = mangadb.Session # the SQLAlchemy session object + + pk = factory.Sequence(lambda n: n) + wavelength = faker.pylist(10, False, 'float') + bintype = 'NAN' + + +if sas: + #@register + class TreeFactory(factory.alchemy.SQLAlchemyModelFactory): + ''' factory for archive db tree table ''' + class Meta: + model = sas.Tree + sqlalchemy_session = archive.Session - pk = factory.Sequence(lambda n: n) - wavelength = faker.pylist(10, False, 'float') - bintype = 'NAN' + id = factory.Sequence(lambda n: n) + version = factory.Sequence(lambda n: 'dr{0}'.format(30 + n)) diff --git a/python/sdssdb/tests/sqladbs/test_archivedb.py b/python/sdssdb/tests/sqladbs/test_archivedb.py index f681e577..00669d48 100644 --- a/python/sdssdb/tests/sqladbs/test_archivedb.py +++ b/python/sdssdb/tests/sqladbs/test_archivedb.py @@ -7,7 +7,7 @@ # Created: Monday, 2nd March 2020 1:24:15 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Sunday, 22nd March 2020 4:44:00 pm +# Last Modified: Monday, 23rd March 2020 3:33:54 pm # Modified By: Brian Cherinka @@ -17,7 +17,6 @@ from sdssdb.sqlalchemy.archive import sas import pytest -pytestmark = pytest.mark.nodb @pytest.mark.parametrize('database', [database], indirect=True) class TestArchiveDb(object): diff --git a/python/sdssdb/tests/sqladbs/test_factory.py b/python/sdssdb/tests/sqladbs/test_factory.py index 49fbd7e0..6e4df301 100644 --- a/python/sdssdb/tests/sqladbs/test_factory.py +++ b/python/sdssdb/tests/sqladbs/test_factory.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 3:17:17 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Thursday, 12th March 2020 12:02:21 pm +# Last Modified: Monday, 23rd March 2020 6:19:07 pm # Modified By: Brian Cherinka @@ -41,7 +41,7 @@ def test_factory_fixture(self, session, user_factory): def test_a_transaction(self, session, batchit): rows = session.query(models.User).all() print('rows', rows) - assert len(rows) == 10 + assert len(rows) == 11 def test_model_fixture(self, user): ''' test a single new instance of model Table is created ''' diff --git a/python/sdssdb/tests/test_peewee.py b/python/sdssdb/tests/test_peewee.py index 1bfe41e8..80764ad9 100644 --- a/python/sdssdb/tests/test_peewee.py +++ b/python/sdssdb/tests/test_peewee.py @@ -8,21 +8,21 @@ from __future__ import absolute_import from __future__ import unicode_literals -import sdssdb.peewee.sdss5db.targetdb as targetdb +#import sdssdb.peewee.sdss5db.targetdb as targetdb import pytest -@pytest.fixture(scope='session', autouse=True) -def skipdb(): - if targetdb.database.connected is False: - pytest.skip('no targetdb found') +# @pytest.fixture(scope='session', autouse=True) +# def skipdb(): +# if targetdb.database.connected is False: +# pytest.skip('no targetdb found') -class TestPeewee(object): - """Tests peewee db utils""" +# class TestPeewee(object): +# """Tests peewee db utils""" - def test_connection(self): - """Tests peewee db connection""" +# def test_connection(self): +# """Tests peewee db connection""" - nt = targetdb.Target.select().count() - assert nt > 0 +# nt = targetdb.Target.select().count() +# assert nt > 0 From 20fbcffb44372979feb72a7a35077bae320f2043 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 13:26:46 -0400 Subject: [PATCH 11/29] rejiggering general connection tests --- python/sdssdb/tests/pwdbs/test_connection.py | 30 ++++++++++++++++ .../sdssdb/tests/sqladbs/test_connection.py | 33 ++++++++++++++++++ ...{test_sqlalchemy.py => test_connection.py} | 34 +++++++++++-------- python/sdssdb/tests/test_peewee.py | 28 --------------- 4 files changed, 82 insertions(+), 43 deletions(-) create mode 100644 python/sdssdb/tests/pwdbs/test_connection.py create mode 100644 python/sdssdb/tests/sqladbs/test_connection.py rename python/sdssdb/tests/{test_sqlalchemy.py => test_connection.py} (60%) delete mode 100644 python/sdssdb/tests/test_peewee.py diff --git a/python/sdssdb/tests/pwdbs/test_connection.py b/python/sdssdb/tests/pwdbs/test_connection.py new file mode 100644 index 00000000..d279ea66 --- /dev/null +++ b/python/sdssdb/tests/pwdbs/test_connection.py @@ -0,0 +1,30 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: test_connection.py +# Project: pwdbs +# Author: Brian Cherinka +# Created: Tuesday, 24th March 2020 11:25:08 am +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Tuesday, 24th March 2020 11:25:36 am +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import +import sdssdb +import pytest + + +def assert_testdb(database): + assert database.dbname == 'test' + assert database.connected is True + assert database.profile == 'local' + assert database.dbversion is None + + +class TestPeeweeDatabaseConnection(object): + def test_db_connected(self, database): + ''' test connection to testdb ''' + assert_testdb(database) + assert isinstance(database, sdssdb.connection.PeeweeDatabaseConnection) diff --git a/python/sdssdb/tests/sqladbs/test_connection.py b/python/sdssdb/tests/sqladbs/test_connection.py new file mode 100644 index 00000000..d2beedc4 --- /dev/null +++ b/python/sdssdb/tests/sqladbs/test_connection.py @@ -0,0 +1,33 @@ +# !/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Filename: test_connection.py +# Project: sqladbs +# Author: Brian Cherinka +# Created: Tuesday, 24th March 2020 11:24:38 am +# License: BSD 3-clause "New" or "Revised" License +# Copyright (c) 2020 Brian Cherinka +# Last Modified: Tuesday, 24th March 2020 11:25:43 am +# Modified By: Brian Cherinka + + +from __future__ import print_function, division, absolute_import +from __future__ import print_function, division, absolute_import +import sdssdb +import pytest + + +def assert_testdb(database): + assert database.dbname == 'test' + assert database.connected is True + assert database.profile == 'local' + assert database.dbversion is None + + +class TestSQLADatabaseConnection(object): + + def test_db_connected(self, database): + ''' test connection to testdb ''' + assert_testdb(database) + assert isinstance(database, sdssdb.connection.SQLADatabaseConnection) + diff --git a/python/sdssdb/tests/test_sqlalchemy.py b/python/sdssdb/tests/test_connection.py similarity index 60% rename from python/sdssdb/tests/test_sqlalchemy.py rename to python/sdssdb/tests/test_connection.py index 8b9aa740..f71b9d18 100644 --- a/python/sdssdb/tests/test_sqlalchemy.py +++ b/python/sdssdb/tests/test_connection.py @@ -1,30 +1,35 @@ # !/usr/bin/env python # -*- coding: utf-8 -*- # -# Filename: test_sqlalchemy.py +# Filename: test_connection.py # Project: tests # Author: Brian Cherinka -# Created: Sunday, 1st March 2020 11:48:23 am +# Created: Tuesday, 24th March 2020 11:14:22 am # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Monday, 2nd March 2020 1:54:48 pm +# Last Modified: Tuesday, 24th March 2020 11:42:35 am # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import +import sdssdb +import pytest +from sdssdb.tests.sqladbs import database as sqladb +from sdssdb.tests.pwdbs import database as pwdb -class TestSQLAConnection(object): - - def _assert_testdb(self, database): - assert database.dbname == 'test' - assert database.connected is True - assert database.profile == 'local' - assert database.dbversion is None +def assert_testdb(database): + assert database.dbname == 'test' + assert database.connected is True + assert database.profile == 'local' + assert database.dbversion is None + + +class TestGenericDatabaseConnection(object): def test_db_connected(self, database): ''' test connection to testdb ''' - self._assert_testdb(database) + assert_testdb(database) def test_list_profiles(self, database): ''' test profiles ''' @@ -35,8 +40,8 @@ def test_list_profiles(self, database): def test_change_version(self, database): ''' test to change db versions ''' - self._assert_testdb(database) - + assert_testdb(database) + # change to database.change_version('v1') assert database.dbname == 'test_v1' @@ -45,5 +50,4 @@ def test_change_version(self, database): # change back database.change_version() - self._assert_testdb(database) - + assert_testdb(database) diff --git a/python/sdssdb/tests/test_peewee.py b/python/sdssdb/tests/test_peewee.py deleted file mode 100644 index 80764ad9..00000000 --- a/python/sdssdb/tests/test_peewee.py +++ /dev/null @@ -1,28 +0,0 @@ -# encoding: utf-8 -# -# main.py - - -from __future__ import division -from __future__ import print_function -from __future__ import absolute_import -from __future__ import unicode_literals - -#import sdssdb.peewee.sdss5db.targetdb as targetdb -import pytest - - -# @pytest.fixture(scope='session', autouse=True) -# def skipdb(): -# if targetdb.database.connected is False: -# pytest.skip('no targetdb found') - - -# class TestPeewee(object): -# """Tests peewee db utils""" - -# def test_connection(self): -# """Tests peewee db connection""" - -# nt = targetdb.Target.select().count() -# assert nt > 0 From 5e5ca2445b1f97851a4ce3bd49a9cc6013d95bbb Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 13:29:14 -0400 Subject: [PATCH 12/29] moving session/transaction fixtures; adding dynamic scope to session/transaction --- python/sdssdb/tests/conftest.py | 37 +++++++++++++------------ python/sdssdb/tests/pwdbs/conftest.py | 12 +++++++- python/sdssdb/tests/sqladbs/conftest.py | 16 ++++++++++- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/python/sdssdb/tests/conftest.py b/python/sdssdb/tests/conftest.py index 8f724d33..3418eb07 100644 --- a/python/sdssdb/tests/conftest.py +++ b/python/sdssdb/tests/conftest.py @@ -9,6 +9,7 @@ import pytest import importlib import inspect +import sdssdb from sdssdb.tests.sqladbs import prepare_testdb as sqla_prepdb from sdssdb.tests.pwdbs import prepare_testdb as pw_prepdb from pytest_postgresql.factories import DatabaseJanitor @@ -18,14 +19,18 @@ # register(TableFactory) -# def pytest_addoption(parser): -# """ Add new options to the pytest command-line """ -# # only run peewee tests -# parser.addoption('--peewee', action='store_true', default=False, -# help='Only run tests for peewee dbs') -# # only run sqla tests -# parser.addoption('--sqla', action='store_true', default=False, -# help='Only run tests for sqlalchemy dbs') +def pytest_addoption(parser): + """ Add new options to the pytest command-line """ + # # only run peewee tests + # parser.addoption('--peewee', action='store_true', default=False, + # help='Only run tests for peewee dbs') + # # only run sqla tests + # parser.addoption('--sqla', action='store_true', default=False, + # help='Only run tests for sqlalchemy dbs') + + # persist the sqla session and peewee transaction + parser.addoption('--persist-sessions', action='store_true', default=False, + help='Switch session and transaction fixtures to module scope') def pytest_ignore_collect(path, config): @@ -75,7 +80,7 @@ def database(dropdb, request): yield request.param else: # check if request is coming from a sqla db or peewee db - issqla = 'sqladbs' in request.module.__name__ + issqla = 'sqladbs' in request.module.__name__ or 'sqlalchemy' in request.module.__name__ # initialize the test database janitor = DatabaseJanitor('postgres', 'localhost', 5432, 'test', '11.4') janitor.init() @@ -85,11 +90,9 @@ def database(dropdb, request): janitor.drop() -@pytest.fixture(scope='module') -def session(database): - ''' SQLA session fixture. set autouse=True to ensure persistence ''' - session = database.Session() - session.begin() - yield session - session.rollback() - session.close() +def determine_scope(fixture_name, config): + ''' determine the scope of the session and transaction fixtures ''' + if config.getoption("--persist-sessions", None): + return "module" + return "function" + diff --git a/python/sdssdb/tests/pwdbs/conftest.py b/python/sdssdb/tests/pwdbs/conftest.py index 8c1dd792..4f60314e 100644 --- a/python/sdssdb/tests/pwdbs/conftest.py +++ b/python/sdssdb/tests/pwdbs/conftest.py @@ -7,15 +7,17 @@ # Created: Monday, 23rd March 2020 4:41:32 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Monday, 23rd March 2020 5:18:29 pm +# Last Modified: Tuesday, 24th March 2020 10:16:06 am # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import import factory import inspect +import pytest from pytest_factoryboy import register from . import factories +from ..conftest import determine_scope # register all xxxFactory classes @@ -27,3 +29,11 @@ attr = getattr(factories, item) if inspect.isclass(attr) and issubclass(attr, factory.Factory): register(attr) + + +@pytest.fixture(scope=determine_scope, autouse=True) +def transaction(database): + ''' Peewee transaction fixture. set autouse=True to ensure persistence ''' + with database.transaction() as txn: + yield txn + txn.rollback() diff --git a/python/sdssdb/tests/sqladbs/conftest.py b/python/sdssdb/tests/sqladbs/conftest.py index 0ce69cb3..8e02fa75 100644 --- a/python/sdssdb/tests/sqladbs/conftest.py +++ b/python/sdssdb/tests/sqladbs/conftest.py @@ -7,14 +7,16 @@ # Created: Sunday, 1st March 2020 5:46:55 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Monday, 23rd March 2020 6:13:45 pm +# Last Modified: Tuesday, 24th March 2020 11:46:29 am # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import import factory import inspect +import pytest from pytest_factoryboy import register +from ..conftest import determine_scope # pytest_factoryboy registers fixtures in directory where it is called # need to import them all into sqla conftest.py @@ -22,6 +24,9 @@ # register all xxxFactory classes +# +# pytest_factoryboy @register decorator registers fixtures in directory where it is +# called. To make available to tests, need to either import them # have to register manually in conftest, instead of with @register class decorator, when # factories organized in separate files compared to models and tests for item in dir(factories): @@ -29,3 +34,12 @@ if inspect.isclass(attr) and issubclass(attr, factory.Factory): register(attr) + +@pytest.fixture(scope=determine_scope, autouse=True) +def session(database): + ''' SQLA session fixture. set autouse=True to ensure persistence ''' + session = database.Session() + session.begin() + yield session + session.rollback() + session.close() From c0834061e6443f96acd2e385438aed46b8b07144 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 13:30:42 -0400 Subject: [PATCH 13/29] adding transactions to peewee tests --- python/sdssdb/tests/pwdbs/factories.py | 24 ++++++++++++++++++--- python/sdssdb/tests/pwdbs/test_factory.py | 10 ++++----- python/sdssdb/tests/pwdbs/test_sdss5db.py | 11 ++++------ python/sdssdb/tests/sqladbs/factories.py | 6 ++++-- python/sdssdb/tests/sqladbs/models.py | 15 +++++++++++-- python/sdssdb/tests/sqladbs/test_factory.py | 4 ++-- 6 files changed, 49 insertions(+), 21 deletions(-) diff --git a/python/sdssdb/tests/pwdbs/factories.py b/python/sdssdb/tests/pwdbs/factories.py index f7e292f6..3fcb78c6 100644 --- a/python/sdssdb/tests/pwdbs/factories.py +++ b/python/sdssdb/tests/pwdbs/factories.py @@ -7,23 +7,41 @@ # Created: Monday, 23rd March 2020 4:35:14 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Monday, 23rd March 2020 4:40:31 pm +# Last Modified: Tuesday, 24th March 2020 10:44:44 am # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import import factory -from sdssdb.tests.pwdbs import models, database +from sdssdb.tests.pwdbs import models, database as testdb +from sdssdb.peewee.sdss5db import database as sdss5db, catalogdb, targetdb from .factoryboy import PeeweeModelFactory +# can use factory.Faker to create simple fake items +# or more general faker object to create more complicated items like python lists, +# sets, or dicts. See sqladbs.factories.WavelengthFactory. faker = factory.faker.faker.Factory().create() class UserFactory(PeeweeModelFactory): class Meta: model = models.User - database = database + database = testdb id = factory.Sequence(lambda n: n) name = factory.Faker('first_name') essence = 'human' + + +class TargetTypeFactory(PeeweeModelFactory): + ''' factory for sdss5db targetdb target_type table ''' + class Meta: + model = targetdb.TargetType + database = sdss5db + + pk = factory.Sequence(lambda n: n) + label = factory.Faker('word') + # catalogid = factory.Faker('pyint') + # ra = factory.Faker('pyfloat', min_value=0, max_value=360) + # dec = factory.Faker('pyfloat', min_value=0, max_value=90) + # field_pk = factory.Sequence(lambda n: n) diff --git a/python/sdssdb/tests/pwdbs/test_factory.py b/python/sdssdb/tests/pwdbs/test_factory.py index 75ca88a7..7f888abd 100644 --- a/python/sdssdb/tests/pwdbs/test_factory.py +++ b/python/sdssdb/tests/pwdbs/test_factory.py @@ -7,7 +7,7 @@ # Created: Monday, 23rd March 2020 4:20:08 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Monday, 23rd March 2020 6:21:03 pm +# Last Modified: Tuesday, 24th March 2020 9:23:21 am # Modified By: Brian Cherinka @@ -23,7 +23,7 @@ def batchit(user_factory): class TestFactory(object): - def test_factory_fixture(self, user_factory): + def test_factory_fixture(self, transaction, user_factory): ''' test the factory can create new entries ''' print('tf', user_factory) user = user_factory(name="Test Human") @@ -31,12 +31,12 @@ def test_factory_fixture(self, user_factory): assert user.name == "Test Human" assert user.essence == 'human' - def test_a_transaction(self, batchit): + def test_a_transaction(self, transaction, batchit): rows = list(models.User.select()) print('rows', rows) - assert len(rows) == 11 + assert len(rows) >= 10 - def test_model_fixture(self, user): + def test_model_fixture(self, transaction, user): ''' test a single new instance of model Table is created ''' assert isinstance(user, models.User) assert user.essence == 'human' diff --git a/python/sdssdb/tests/pwdbs/test_sdss5db.py b/python/sdssdb/tests/pwdbs/test_sdss5db.py index e595c398..df711b78 100644 --- a/python/sdssdb/tests/pwdbs/test_sdss5db.py +++ b/python/sdssdb/tests/pwdbs/test_sdss5db.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 2:56:48 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Monday, 23rd March 2020 6:27:34 pm +# Last Modified: Tuesday, 24th March 2020 10:43:29 am # Modified By: Brian Cherinka @@ -19,10 +19,7 @@ @pytest.mark.parametrize('database', [database], indirect=True) class TestSDSS5Db(object): - def test_allwise_list(self): - aw_count = catalogdb.AllWise.select().count() - assert aw_count > 0 - - def test_targetdb_count(self): - nt = targetdb.Target.select().count() + def test_target_type_count(self, target_type_factory): + target_type_factory.create_batch(10) + nt = targetdb.TargetType.select().count() assert nt > 0 diff --git a/python/sdssdb/tests/sqladbs/factories.py b/python/sdssdb/tests/sqladbs/factories.py index 5e902ef5..e92a9d9a 100644 --- a/python/sdssdb/tests/sqladbs/factories.py +++ b/python/sdssdb/tests/sqladbs/factories.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 5:45:43 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Monday, 23rd March 2020 6:13:02 pm +# Last Modified: Tuesday, 24th March 2020 10:44:57 am # Modified By: Brian Cherinka @@ -18,7 +18,9 @@ from sdssdb.sqlalchemy.archive import database as archive #from pytest_factoryboy import register - +# can use factory.Faker to create simple fake items +# or more general faker object to create more complicated items like python lists, +# sets, or dicts. See sqladbs.factories.WavelengthFactory. faker = factory.faker.faker.Factory().create() # need to load Model Classes this way for cases where the database does not exist for a test diff --git a/python/sdssdb/tests/sqladbs/models.py b/python/sdssdb/tests/sqladbs/models.py index 040c5367..5d0d65d9 100644 --- a/python/sdssdb/tests/sqladbs/models.py +++ b/python/sdssdb/tests/sqladbs/models.py @@ -7,15 +7,18 @@ # Created: Sunday, 1st March 2020 5:45:37 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Monday, 2nd March 2020 8:42:51 pm +# Last Modified: Tuesday, 24th March 2020 11:48:25 am # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import -from sqlalchemy import Column, Integer, String +from sqlalchemy import Column, Integer, String, Float from sdssdb.tests.sqladbs import TmpBase +from sqlalchemy.orm import deferred, relationship +from sqlalchemy.dialects.postgresql import ARRAY + class User(TmpBase): ''' model for user on test database ''' @@ -23,3 +26,11 @@ class User(TmpBase): id = Column(Integer, primary_key=True) name = Column(String(80)) essence = Column(String(10)) + + +class Wavelength(TmpBase): + __tablename__ = 'wavelength' + __table_args__ = {'schema': 'mangadatadb'} + pk = Column(Integer, primary_key=True) + wavelength = deferred(Column(ARRAY(Float, zero_indexes=True))) + bintype = Column(String(10)) diff --git a/python/sdssdb/tests/sqladbs/test_factory.py b/python/sdssdb/tests/sqladbs/test_factory.py index 6e4df301..2126f010 100644 --- a/python/sdssdb/tests/sqladbs/test_factory.py +++ b/python/sdssdb/tests/sqladbs/test_factory.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 3:17:17 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Monday, 23rd March 2020 6:19:07 pm +# Last Modified: Tuesday, 24th March 2020 9:23:14 am # Modified By: Brian Cherinka @@ -41,7 +41,7 @@ def test_factory_fixture(self, session, user_factory): def test_a_transaction(self, session, batchit): rows = session.query(models.User).all() print('rows', rows) - assert len(rows) == 11 + assert len(rows) >= 10 def test_model_fixture(self, user): ''' test a single new instance of model Table is created ''' From 16755afb167f4917d9973b2c2dbcc0d2c308399d Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 14:03:11 -0400 Subject: [PATCH 14/29] cleaning up a bit --- python/sdssdb/tests/pwdbs/models.py | 4 ++-- python/sdssdb/tests/pwdbs/test_connection.py | 3 +-- python/sdssdb/tests/pwdbs/test_factory.py | 4 ++-- python/sdssdb/tests/pwdbs/test_sdss5db.py | 4 ++-- python/sdssdb/tests/sqladbs/models.py | 12 +----------- python/sdssdb/tests/sqladbs/test_archivedb.py | 4 ++-- python/sdssdb/tests/sqladbs/test_connection.py | 9 ++++----- python/sdssdb/tests/sqladbs/test_mangadb.py | 4 ++-- python/sdssdb/tests/sqladbs/test_sdss5db.py | 4 ++-- python/sdssdb/tests/test_connection.py | 6 +----- 10 files changed, 19 insertions(+), 35 deletions(-) diff --git a/python/sdssdb/tests/pwdbs/models.py b/python/sdssdb/tests/pwdbs/models.py index 259c398a..b1ee290d 100644 --- a/python/sdssdb/tests/pwdbs/models.py +++ b/python/sdssdb/tests/pwdbs/models.py @@ -7,12 +7,12 @@ # Created: Monday, 23rd March 2020 4:35:07 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Monday, 23rd March 2020 4:57:34 pm +# Last Modified: Tuesday, 24th March 2020 2:02:52 pm # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import -from peewee import IntegerField, CharField, AutoField +from peewee import CharField, AutoField from sdssdb.tests.pwdbs import TmpModel diff --git a/python/sdssdb/tests/pwdbs/test_connection.py b/python/sdssdb/tests/pwdbs/test_connection.py index d279ea66..4a602f8d 100644 --- a/python/sdssdb/tests/pwdbs/test_connection.py +++ b/python/sdssdb/tests/pwdbs/test_connection.py @@ -7,13 +7,12 @@ # Created: Tuesday, 24th March 2020 11:25:08 am # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 11:25:36 am +# Last Modified: Tuesday, 24th March 2020 2:02:40 pm # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import import sdssdb -import pytest def assert_testdb(database): diff --git a/python/sdssdb/tests/pwdbs/test_factory.py b/python/sdssdb/tests/pwdbs/test_factory.py index 7f888abd..fb37b665 100644 --- a/python/sdssdb/tests/pwdbs/test_factory.py +++ b/python/sdssdb/tests/pwdbs/test_factory.py @@ -7,12 +7,12 @@ # Created: Monday, 23rd March 2020 4:20:08 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 9:23:21 am +# Last Modified: Tuesday, 24th March 2020 2:02:28 pm # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import -from sdssdb.tests.pwdbs import models, database +from sdssdb.tests.pwdbs import models import pytest diff --git a/python/sdssdb/tests/pwdbs/test_sdss5db.py b/python/sdssdb/tests/pwdbs/test_sdss5db.py index df711b78..b7d71997 100644 --- a/python/sdssdb/tests/pwdbs/test_sdss5db.py +++ b/python/sdssdb/tests/pwdbs/test_sdss5db.py @@ -7,13 +7,13 @@ # Created: Sunday, 1st March 2020 2:56:48 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 10:43:29 am +# Last Modified: Tuesday, 24th March 2020 2:02:15 pm # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import import pytest -from sdssdb.peewee.sdss5db import database, catalogdb, targetdb +from sdssdb.peewee.sdss5db import database, targetdb @pytest.mark.parametrize('database', [database], indirect=True) diff --git a/python/sdssdb/tests/sqladbs/models.py b/python/sdssdb/tests/sqladbs/models.py index 5d0d65d9..cbfff75a 100644 --- a/python/sdssdb/tests/sqladbs/models.py +++ b/python/sdssdb/tests/sqladbs/models.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 5:45:37 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 11:48:25 am +# Last Modified: Tuesday, 24th March 2020 2:00:29 pm # Modified By: Brian Cherinka @@ -16,9 +16,6 @@ from sqlalchemy import Column, Integer, String, Float from sdssdb.tests.sqladbs import TmpBase -from sqlalchemy.orm import deferred, relationship -from sqlalchemy.dialects.postgresql import ARRAY - class User(TmpBase): ''' model for user on test database ''' @@ -27,10 +24,3 @@ class User(TmpBase): name = Column(String(80)) essence = Column(String(10)) - -class Wavelength(TmpBase): - __tablename__ = 'wavelength' - __table_args__ = {'schema': 'mangadatadb'} - pk = Column(Integer, primary_key=True) - wavelength = deferred(Column(ARRAY(Float, zero_indexes=True))) - bintype = Column(String(10)) diff --git a/python/sdssdb/tests/sqladbs/test_archivedb.py b/python/sdssdb/tests/sqladbs/test_archivedb.py index 00669d48..8534313f 100644 --- a/python/sdssdb/tests/sqladbs/test_archivedb.py +++ b/python/sdssdb/tests/sqladbs/test_archivedb.py @@ -7,15 +7,15 @@ # Created: Monday, 2nd March 2020 1:24:15 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Monday, 23rd March 2020 3:33:54 pm +# Last Modified: Tuesday, 24th March 2020 2:00:40 pm # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import +import pytest from sdssdb.sqlalchemy.archive import database if database.connected: from sdssdb.sqlalchemy.archive import sas -import pytest @pytest.mark.parametrize('database', [database], indirect=True) diff --git a/python/sdssdb/tests/sqladbs/test_connection.py b/python/sdssdb/tests/sqladbs/test_connection.py index d2beedc4..987952c7 100644 --- a/python/sdssdb/tests/sqladbs/test_connection.py +++ b/python/sdssdb/tests/sqladbs/test_connection.py @@ -7,14 +7,14 @@ # Created: Tuesday, 24th March 2020 11:24:38 am # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 11:25:43 am +# Last Modified: Tuesday, 24th March 2020 2:00:52 pm # Modified By: Brian Cherinka -from __future__ import print_function, division, absolute_import -from __future__ import print_function, division, absolute_import -import sdssdb +from __future__ import absolute_import, division, print_function + import pytest +import sdssdb def assert_testdb(database): @@ -30,4 +30,3 @@ def test_db_connected(self, database): ''' test connection to testdb ''' assert_testdb(database) assert isinstance(database, sdssdb.connection.SQLADatabaseConnection) - diff --git a/python/sdssdb/tests/sqladbs/test_mangadb.py b/python/sdssdb/tests/sqladbs/test_mangadb.py index 77328b52..c7c5d2cb 100644 --- a/python/sdssdb/tests/sqladbs/test_mangadb.py +++ b/python/sdssdb/tests/sqladbs/test_mangadb.py @@ -7,7 +7,7 @@ # Created: Friday, 23rd August 2019 12:35:00 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2019 Brian Cherinka -# Last Modified: Sunday, 22nd March 2020 4:44:35 pm +# Last Modified: Tuesday, 24th March 2020 2:01:11 pm # Modified By: Brian Cherinka @@ -18,11 +18,11 @@ # #from ..conftest import TmpBase # from sqlalchemy.dialects.postgresql import ARRAY import factory +import pytest from pytest_factoryboy import register from sdssdb.sqlalchemy.mangadb import database if database.connected: from sdssdb.sqlalchemy.mangadb import datadb -import pytest faker = factory.faker.faker.Factory().create() diff --git a/python/sdssdb/tests/sqladbs/test_sdss5db.py b/python/sdssdb/tests/sqladbs/test_sdss5db.py index 7fedd42d..84fd49f7 100644 --- a/python/sdssdb/tests/sqladbs/test_sdss5db.py +++ b/python/sdssdb/tests/sqladbs/test_sdss5db.py @@ -7,15 +7,15 @@ # Created: Wednesday, 4th March 2020 3:01:10 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Sunday, 22nd March 2020 4:44:53 pm +# Last Modified: Tuesday, 24th March 2020 2:01:27 pm # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import +import pytest from sdssdb.sqlalchemy.sdss5db import database if database.connected: from sdssdb.sqlalchemy.sdss5db import catalogdb -import pytest @pytest.mark.parametrize('database', [database], indirect=True) diff --git a/python/sdssdb/tests/test_connection.py b/python/sdssdb/tests/test_connection.py index f71b9d18..e06c155d 100644 --- a/python/sdssdb/tests/test_connection.py +++ b/python/sdssdb/tests/test_connection.py @@ -7,15 +7,11 @@ # Created: Tuesday, 24th March 2020 11:14:22 am # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 11:42:35 am +# Last Modified: Tuesday, 24th March 2020 2:01:54 pm # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import -import sdssdb -import pytest -from sdssdb.tests.sqladbs import database as sqladb -from sdssdb.tests.pwdbs import database as pwdb def assert_testdb(database): From 7c14cb329bcdf2321a100520cc9f402d65071ee4 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 14:13:45 -0400 Subject: [PATCH 15/29] adding option to only run peewee or sqla tests --- python/sdssdb/tests/conftest.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/python/sdssdb/tests/conftest.py b/python/sdssdb/tests/conftest.py index 3418eb07..33c99faa 100644 --- a/python/sdssdb/tests/conftest.py +++ b/python/sdssdb/tests/conftest.py @@ -21,12 +21,13 @@ def pytest_addoption(parser): """ Add new options to the pytest command-line """ - # # only run peewee tests - # parser.addoption('--peewee', action='store_true', default=False, - # help='Only run tests for peewee dbs') - # # only run sqla tests - # parser.addoption('--sqla', action='store_true', default=False, - # help='Only run tests for sqlalchemy dbs') + # only run peewee tests + parser.addoption('--peewee', action='store_true', default=False, + help='Only run tests for peewee dbs') + + # only run sqla tests + parser.addoption('--sqla', action='store_true', default=False, + help='Only run tests for sqlalchemy dbs') # persist the sqla session and peewee transaction parser.addoption('--persist-sessions', action='store_true', default=False, @@ -40,6 +41,15 @@ def pytest_ignore_collect(path, config): which ones have databases that fail to connect and ignores them. ''' + only_peewee = config.getoption("--peewee", None) + only_sqla = config.getoption("--sqla", None) + assert not all([only_peewee, only_sqla]), 'both --peewee and --sqla options cannot be set' + if only_peewee: + if 'sqladbs' in str(path): + return True + if only_sqla: + if 'pwdbs' in str(path): + return True # identify and ignore test modules where no local # database is set up for those tests From 6da88de84932a2516b7d8a6a64b65a95dfa1f643 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 14:36:25 -0400 Subject: [PATCH 16/29] cleaning up a bit more --- python/sdssdb/tests/conftest.py | 6 +---- python/sdssdb/tests/pwdbs/conftest.py | 8 +++++-- python/sdssdb/tests/pwdbs/factories.py | 6 +++-- python/sdssdb/tests/pwdbs/models.py | 7 +++++- python/sdssdb/tests/pwdbs/test_factory.py | 4 +--- python/sdssdb/tests/pwdbs/test_sdss5db.py | 7 +++++- python/sdssdb/tests/sqladbs/conftest.py | 5 ++-- python/sdssdb/tests/sqladbs/factories.py | 6 +++-- python/sdssdb/tests/sqladbs/models.py | 9 +++++-- .../sdssdb/tests/sqladbs/test_connection.py | 4 +--- python/sdssdb/tests/sqladbs/test_factory.py | 4 +--- python/sdssdb/tests/sqladbs/test_mangadb.py | 24 ++----------------- 12 files changed, 42 insertions(+), 48 deletions(-) diff --git a/python/sdssdb/tests/conftest.py b/python/sdssdb/tests/conftest.py index 33c99faa..85ca598b 100644 --- a/python/sdssdb/tests/conftest.py +++ b/python/sdssdb/tests/conftest.py @@ -9,15 +9,10 @@ import pytest import importlib import inspect -import sdssdb from sdssdb.tests.sqladbs import prepare_testdb as sqla_prepdb from sdssdb.tests.pwdbs import prepare_testdb as pw_prepdb from pytest_postgresql.factories import DatabaseJanitor -# from pytest_factoryboy import register -# from sdssdb.tests.sqladbs.testdb import TableFactory -# register(TableFactory) - def pytest_addoption(parser): """ Add new options to the pytest command-line """ @@ -92,6 +87,7 @@ def database(dropdb, request): # check if request is coming from a sqla db or peewee db issqla = 'sqladbs' in request.module.__name__ or 'sqlalchemy' in request.module.__name__ # initialize the test database + # uses https://github.com/ClearcodeHQ/pytest-postgresql janitor = DatabaseJanitor('postgres', 'localhost', 5432, 'test', '11.4') janitor.init() db = sqla_prepdb() if issqla else pw_prepdb() diff --git a/python/sdssdb/tests/pwdbs/conftest.py b/python/sdssdb/tests/pwdbs/conftest.py index 4f60314e..ebbac3f5 100644 --- a/python/sdssdb/tests/pwdbs/conftest.py +++ b/python/sdssdb/tests/pwdbs/conftest.py @@ -7,7 +7,7 @@ # Created: Monday, 23rd March 2020 4:41:32 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 10:16:06 am +# Last Modified: Tuesday, 24th March 2020 2:30:02 pm # Modified By: Brian Cherinka @@ -21,8 +21,12 @@ # register all xxxFactory classes +# +# pytest_factoryboy @register decorator registers fixtures in directory where it is +# called. To make available to tests, need to either import them # have to register manually in conftest, instead of with @register class decorator, when -# factories organized in separate files compared to models and tests +# factories organized in separate files compared to models and tests. For docs on +# pytest_factoryboy, see https://pytest-factoryboy.readthedocs.io/en/latest for item in dir(factories): if item == 'PeeweeModelFactory': continue diff --git a/python/sdssdb/tests/pwdbs/factories.py b/python/sdssdb/tests/pwdbs/factories.py index 3fcb78c6..0250d7c0 100644 --- a/python/sdssdb/tests/pwdbs/factories.py +++ b/python/sdssdb/tests/pwdbs/factories.py @@ -7,7 +7,7 @@ # Created: Monday, 23rd March 2020 4:35:14 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 10:44:44 am +# Last Modified: Tuesday, 24th March 2020 2:30:20 pm # Modified By: Brian Cherinka @@ -19,7 +19,9 @@ # can use factory.Faker to create simple fake items # or more general faker object to create more complicated items like python lists, -# sets, or dicts. See sqladbs.factories.WavelengthFactory. +# sets, or dicts. See sqladbs.factories.WavelengthFactory. For a list of available fake +# providers, see https://faker.readthedocs.io/en/master/providers.html. To see the available +# factory declarations, see https://factoryboy.readthedocs.io/en/latest/reference.html#declarations faker = factory.faker.faker.Factory().create() diff --git a/python/sdssdb/tests/pwdbs/models.py b/python/sdssdb/tests/pwdbs/models.py index b1ee290d..8141b16d 100644 --- a/python/sdssdb/tests/pwdbs/models.py +++ b/python/sdssdb/tests/pwdbs/models.py @@ -7,7 +7,7 @@ # Created: Monday, 23rd March 2020 4:35:07 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:02:52 pm +# Last Modified: Tuesday, 24th March 2020 2:25:39 pm # Modified By: Brian Cherinka @@ -15,6 +15,11 @@ from peewee import CharField, AutoField from sdssdb.tests.pwdbs import TmpModel +# +# This file contains models used by the temporary test database. +# New test models can be defined by subclassing from TmpModel +# + class User(TmpModel): ''' model for user on test database ''' diff --git a/python/sdssdb/tests/pwdbs/test_factory.py b/python/sdssdb/tests/pwdbs/test_factory.py index fb37b665..ac6deea9 100644 --- a/python/sdssdb/tests/pwdbs/test_factory.py +++ b/python/sdssdb/tests/pwdbs/test_factory.py @@ -7,7 +7,7 @@ # Created: Monday, 23rd March 2020 4:20:08 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:02:28 pm +# Last Modified: Tuesday, 24th March 2020 2:35:58 pm # Modified By: Brian Cherinka @@ -25,7 +25,6 @@ class TestFactory(object): def test_factory_fixture(self, transaction, user_factory): ''' test the factory can create new entries ''' - print('tf', user_factory) user = user_factory(name="Test Human") assert user.id == 1 assert user.name == "Test Human" @@ -33,7 +32,6 @@ def test_factory_fixture(self, transaction, user_factory): def test_a_transaction(self, transaction, batchit): rows = list(models.User.select()) - print('rows', rows) assert len(rows) >= 10 def test_model_fixture(self, transaction, user): diff --git a/python/sdssdb/tests/pwdbs/test_sdss5db.py b/python/sdssdb/tests/pwdbs/test_sdss5db.py index b7d71997..e6378d15 100644 --- a/python/sdssdb/tests/pwdbs/test_sdss5db.py +++ b/python/sdssdb/tests/pwdbs/test_sdss5db.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 2:56:48 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:02:15 pm +# Last Modified: Tuesday, 24th March 2020 2:32:32 pm # Modified By: Brian Cherinka @@ -20,6 +20,11 @@ class TestSDSS5Db(object): def test_target_type_count(self, target_type_factory): + ''' test to count target_type select results + + creates an additional (or initial) batch of 10 rows + in the target_type table + ''' target_type_factory.create_batch(10) nt = targetdb.TargetType.select().count() assert nt > 0 diff --git a/python/sdssdb/tests/sqladbs/conftest.py b/python/sdssdb/tests/sqladbs/conftest.py index 8e02fa75..f294f72d 100644 --- a/python/sdssdb/tests/sqladbs/conftest.py +++ b/python/sdssdb/tests/sqladbs/conftest.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 5:46:55 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 11:46:29 am +# Last Modified: Tuesday, 24th March 2020 2:29:49 pm # Modified By: Brian Cherinka @@ -28,7 +28,8 @@ # pytest_factoryboy @register decorator registers fixtures in directory where it is # called. To make available to tests, need to either import them # have to register manually in conftest, instead of with @register class decorator, when -# factories organized in separate files compared to models and tests +# factories organized in separate files compared to models and tests. For docs on +# pytest_factoryboy, see https://pytest-factoryboy.readthedocs.io/en/latest for item in dir(factories): attr = getattr(factories, item) if inspect.isclass(attr) and issubclass(attr, factory.Factory): diff --git a/python/sdssdb/tests/sqladbs/factories.py b/python/sdssdb/tests/sqladbs/factories.py index e92a9d9a..7cb24eae 100644 --- a/python/sdssdb/tests/sqladbs/factories.py +++ b/python/sdssdb/tests/sqladbs/factories.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 5:45:43 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 10:44:57 am +# Last Modified: Tuesday, 24th March 2020 2:29:23 pm # Modified By: Brian Cherinka @@ -20,7 +20,9 @@ # can use factory.Faker to create simple fake items # or more general faker object to create more complicated items like python lists, -# sets, or dicts. See sqladbs.factories.WavelengthFactory. +# sets, or dicts. See sqladbs.factories.WavelengthFactory. For a list of available fake +# providers, see https://faker.readthedocs.io/en/master/providers.html. To see the available +# factory declarations, see https://factoryboy.readthedocs.io/en/latest/reference.html#declarations faker = factory.faker.faker.Factory().create() # need to load Model Classes this way for cases where the database does not exist for a test diff --git a/python/sdssdb/tests/sqladbs/models.py b/python/sdssdb/tests/sqladbs/models.py index cbfff75a..896120ea 100644 --- a/python/sdssdb/tests/sqladbs/models.py +++ b/python/sdssdb/tests/sqladbs/models.py @@ -7,15 +7,20 @@ # Created: Sunday, 1st March 2020 5:45:37 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:00:29 pm +# Last Modified: Tuesday, 24th March 2020 2:33:53 pm # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import -from sqlalchemy import Column, Integer, String, Float +from sqlalchemy import Column, Integer, String from sdssdb.tests.sqladbs import TmpBase +# +# This file contains models used by the temporary test database. +# New test models can be defined by subclassing from TmpBase +# + class User(TmpBase): ''' model for user on test database ''' diff --git a/python/sdssdb/tests/sqladbs/test_connection.py b/python/sdssdb/tests/sqladbs/test_connection.py index 987952c7..ff2c03a8 100644 --- a/python/sdssdb/tests/sqladbs/test_connection.py +++ b/python/sdssdb/tests/sqladbs/test_connection.py @@ -7,13 +7,11 @@ # Created: Tuesday, 24th March 2020 11:24:38 am # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:00:52 pm +# Last Modified: Tuesday, 24th March 2020 2:33:31 pm # Modified By: Brian Cherinka from __future__ import absolute_import, division, print_function - -import pytest import sdssdb diff --git a/python/sdssdb/tests/sqladbs/test_factory.py b/python/sdssdb/tests/sqladbs/test_factory.py index 2126f010..52e97114 100644 --- a/python/sdssdb/tests/sqladbs/test_factory.py +++ b/python/sdssdb/tests/sqladbs/test_factory.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 3:17:17 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 9:23:14 am +# Last Modified: Tuesday, 24th March 2020 2:35:42 pm # Modified By: Brian Cherinka @@ -32,7 +32,6 @@ class TestFactory(object): def test_factory_fixture(self, session, user_factory): ''' test the factory can create new entries ''' - print('tf', user_factory._meta.sqlalchemy_session, database.Session) table = user_factory(name="Test Human") assert table.id == 0 assert table.name == "Test Human" @@ -40,7 +39,6 @@ def test_factory_fixture(self, session, user_factory): def test_a_transaction(self, session, batchit): rows = session.query(models.User).all() - print('rows', rows) assert len(rows) >= 10 def test_model_fixture(self, user): diff --git a/python/sdssdb/tests/sqladbs/test_mangadb.py b/python/sdssdb/tests/sqladbs/test_mangadb.py index c7c5d2cb..cafce2d8 100644 --- a/python/sdssdb/tests/sqladbs/test_mangadb.py +++ b/python/sdssdb/tests/sqladbs/test_mangadb.py @@ -7,7 +7,7 @@ # Created: Friday, 23rd August 2019 12:35:00 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2019 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:01:11 pm +# Last Modified: Tuesday, 24th March 2020 2:33:11 pm # Modified By: Brian Cherinka @@ -19,7 +19,6 @@ # from sqlalchemy.dialects.postgresql import ARRAY import factory import pytest -from pytest_factoryboy import register from sdssdb.sqlalchemy.mangadb import database if database.connected: from sdssdb.sqlalchemy.mangadb import datadb @@ -32,26 +31,7 @@ def skipdb(): if database.connected is False: pytest.skip('no mangadb found') - -# class Wavelength(TmpBase): -# __tablename__ = 'wavelength' -# __table_args__ = {'schema': 'mangadatadb'} -# pk = Column(Integer, primary_key=True) -# wavelength = deferred(Column(ARRAY(Float, zero_indexes=True))) -# bintype = Column(String(10)) - - -# @register -# class WaveFactory(factory.alchemy.SQLAlchemyModelFactory): -# class Meta: -# model = datadb.Wavelength -# sqlalchemy_session = mangadb.Session # the SQLAlchemy session object - -# pk = factory.Sequence(lambda n: n) -# wavelength = faker.pylist(10, False, 'float') -# bintype = 'NAN' - - + @pytest.mark.parametrize('database', [database], indirect=True) class TestMangaDB(object): From 809df765cf04de059d78324d89a7cdce77bd0854 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 14:53:30 -0400 Subject: [PATCH 17/29] cleaning up a bit more --- python/sdssdb/tests/pwdbs/conftest.py | 10 +++++----- python/sdssdb/tests/pwdbs/factories.py | 9 +++------ python/sdssdb/tests/pwdbs/test_sdss5db.py | 8 ++++---- python/sdssdb/tests/sqladbs/conftest.py | 14 +++++--------- python/sdssdb/tests/sqladbs/factories.py | 10 +++++----- 5 files changed, 22 insertions(+), 29 deletions(-) diff --git a/python/sdssdb/tests/pwdbs/conftest.py b/python/sdssdb/tests/pwdbs/conftest.py index ebbac3f5..9490a462 100644 --- a/python/sdssdb/tests/pwdbs/conftest.py +++ b/python/sdssdb/tests/pwdbs/conftest.py @@ -7,7 +7,7 @@ # Created: Monday, 23rd March 2020 4:41:32 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:30:02 pm +# Last Modified: Tuesday, 24th March 2020 2:53:13 pm # Modified By: Brian Cherinka @@ -23,10 +23,10 @@ # register all xxxFactory classes # # pytest_factoryboy @register decorator registers fixtures in directory where it is -# called. To make available to tests, need to either import them -# have to register manually in conftest, instead of with @register class decorator, when -# factories organized in separate files compared to models and tests. For docs on -# pytest_factoryboy, see https://pytest-factoryboy.readthedocs.io/en/latest +# called. To make available to tests, need to either import them into conftest or register +# manually in conftest. The below code registers them manually instead of with +# @register class decorator. For docs on pytest_factoryboy, +# see https://pytest-factoryboy.readthedocs.io/en/latest for item in dir(factories): if item == 'PeeweeModelFactory': continue diff --git a/python/sdssdb/tests/pwdbs/factories.py b/python/sdssdb/tests/pwdbs/factories.py index 0250d7c0..3c8f266f 100644 --- a/python/sdssdb/tests/pwdbs/factories.py +++ b/python/sdssdb/tests/pwdbs/factories.py @@ -7,14 +7,14 @@ # Created: Monday, 23rd March 2020 4:35:14 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:30:20 pm +# Last Modified: Tuesday, 24th March 2020 2:38:07 pm # Modified By: Brian Cherinka from __future__ import print_function, division, absolute_import import factory from sdssdb.tests.pwdbs import models, database as testdb -from sdssdb.peewee.sdss5db import database as sdss5db, catalogdb, targetdb +from sdssdb.peewee.sdss5db import database as sdss5db, targetdb from .factoryboy import PeeweeModelFactory # can use factory.Faker to create simple fake items @@ -43,7 +43,4 @@ class Meta: pk = factory.Sequence(lambda n: n) label = factory.Faker('word') - # catalogid = factory.Faker('pyint') - # ra = factory.Faker('pyfloat', min_value=0, max_value=360) - # dec = factory.Faker('pyfloat', min_value=0, max_value=90) - # field_pk = factory.Sequence(lambda n: n) + diff --git a/python/sdssdb/tests/pwdbs/test_sdss5db.py b/python/sdssdb/tests/pwdbs/test_sdss5db.py index e6378d15..eb7539f5 100644 --- a/python/sdssdb/tests/pwdbs/test_sdss5db.py +++ b/python/sdssdb/tests/pwdbs/test_sdss5db.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 2:56:48 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:32:32 pm +# Last Modified: Tuesday, 24th March 2020 2:37:28 pm # Modified By: Brian Cherinka @@ -19,11 +19,11 @@ @pytest.mark.parametrize('database', [database], indirect=True) class TestSDSS5Db(object): - def test_target_type_count(self, target_type_factory): + def test_target_type_fake_count(self, target_type_factory): ''' test to count target_type select results - creates an additional (or initial) batch of 10 rows - in the target_type table + creates an additional (or initial) batch of 10 fake + rows in the target_type table ''' target_type_factory.create_batch(10) nt = targetdb.TargetType.select().count() diff --git a/python/sdssdb/tests/sqladbs/conftest.py b/python/sdssdb/tests/sqladbs/conftest.py index f294f72d..342d49e5 100644 --- a/python/sdssdb/tests/sqladbs/conftest.py +++ b/python/sdssdb/tests/sqladbs/conftest.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 5:46:55 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:29:49 pm +# Last Modified: Tuesday, 24th March 2020 2:53:02 pm # Modified By: Brian Cherinka @@ -17,19 +17,15 @@ import pytest from pytest_factoryboy import register from ..conftest import determine_scope - -# pytest_factoryboy registers fixtures in directory where it is called -# need to import them all into sqla conftest.py from . import factories - # register all xxxFactory classes # # pytest_factoryboy @register decorator registers fixtures in directory where it is -# called. To make available to tests, need to either import them -# have to register manually in conftest, instead of with @register class decorator, when -# factories organized in separate files compared to models and tests. For docs on -# pytest_factoryboy, see https://pytest-factoryboy.readthedocs.io/en/latest +# called. To make available to tests, need to either import them into conftest or register +# manually in conftest. The below code registers them manually instead of with +# @register class decorator. For docs on pytest_factoryboy, +# see https://pytest-factoryboy.readthedocs.io/en/latest for item in dir(factories): attr = getattr(factories, item) if inspect.isclass(attr) and issubclass(attr, factory.Factory): diff --git a/python/sdssdb/tests/sqladbs/factories.py b/python/sdssdb/tests/sqladbs/factories.py index 7cb24eae..4ae19d01 100644 --- a/python/sdssdb/tests/sqladbs/factories.py +++ b/python/sdssdb/tests/sqladbs/factories.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 5:45:43 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:29:23 pm +# Last Modified: Tuesday, 24th March 2020 2:39:38 pm # Modified By: Brian Cherinka @@ -16,7 +16,7 @@ from sdssdb.tests.sqladbs import models, database as db, get_model_from_database from sdssdb.sqlalchemy.mangadb import database as mangadb from sdssdb.sqlalchemy.archive import database as archive -#from pytest_factoryboy import register +from pytest_factoryboy import register # can use factory.Faker to create simple fake items # or more general faker object to create more complicated items like python lists, @@ -30,7 +30,7 @@ sas = get_model_from_database(archive, 'sas') -#@register +@register class UserFactory(factory.alchemy.SQLAlchemyModelFactory): ''' factory for testdb user table''' class Meta: @@ -43,7 +43,7 @@ class Meta: if datadb: - #@register + @register class WaveFactory(factory.alchemy.SQLAlchemyModelFactory): ''' factory for mangadb wavelength table ''' class Meta: @@ -56,7 +56,7 @@ class Meta: if sas: - #@register + @register class TreeFactory(factory.alchemy.SQLAlchemyModelFactory): ''' factory for archive db tree table ''' class Meta: From 35be7a82d90c005a0d8be54abc076f114149e318 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 15:07:08 -0400 Subject: [PATCH 18/29] updating reqs --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index cd23693e..ff41ec18 100644 --- a/setup.cfg +++ b/setup.cfg @@ -58,6 +58,9 @@ dev = pytest-cov>=2.4.0 pytest-sugar>=0.8.0 pydot>=1.4.1 + pytest-postgresql>=2.2.1 + factory_boy>=2.12.0 + pytest-factoryboy>=2.0.3 docs = Sphinx>=1.8.0 sphinx_bootstrap_theme>=0.4.12 From 79cb5606a76a23a54abbba5e56adc7ed3f23709c Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 15:39:56 -0400 Subject: [PATCH 19/29] updating sdss5db example from old target_type to category; updating some text --- python/sdssdb/tests/pwdbs/factories.py | 8 ++++---- python/sdssdb/tests/pwdbs/test_factory.py | 4 ++-- python/sdssdb/tests/pwdbs/test_sdss5db.py | 12 ++++++------ python/sdssdb/tests/sqladbs/test_factory.py | 12 ++++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/python/sdssdb/tests/pwdbs/factories.py b/python/sdssdb/tests/pwdbs/factories.py index 3c8f266f..c49ed7fb 100644 --- a/python/sdssdb/tests/pwdbs/factories.py +++ b/python/sdssdb/tests/pwdbs/factories.py @@ -7,7 +7,7 @@ # Created: Monday, 23rd March 2020 4:35:14 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:38:07 pm +# Last Modified: Tuesday, 24th March 2020 3:37:42 pm # Modified By: Brian Cherinka @@ -35,10 +35,10 @@ class Meta: essence = 'human' -class TargetTypeFactory(PeeweeModelFactory): - ''' factory for sdss5db targetdb target_type table ''' +class CategoryFactory(PeeweeModelFactory): + ''' factory for sdss5db targetdb category table ''' class Meta: - model = targetdb.TargetType + model = targetdb.Category database = sdss5db pk = factory.Sequence(lambda n: n) diff --git a/python/sdssdb/tests/pwdbs/test_factory.py b/python/sdssdb/tests/pwdbs/test_factory.py index ac6deea9..b00bbcff 100644 --- a/python/sdssdb/tests/pwdbs/test_factory.py +++ b/python/sdssdb/tests/pwdbs/test_factory.py @@ -7,7 +7,7 @@ # Created: Monday, 23rd March 2020 4:20:08 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:35:58 pm +# Last Modified: Tuesday, 24th March 2020 3:34:48 pm # Modified By: Brian Cherinka @@ -35,7 +35,7 @@ def test_a_transaction(self, transaction, batchit): assert len(rows) >= 10 def test_model_fixture(self, transaction, user): - ''' test a single new instance of model Table is created ''' + ''' test a single new instance of model User is created ''' assert isinstance(user, models.User) assert user.essence == 'human' assert user.name != 'Test Human' diff --git a/python/sdssdb/tests/pwdbs/test_sdss5db.py b/python/sdssdb/tests/pwdbs/test_sdss5db.py index eb7539f5..7b8a7fcb 100644 --- a/python/sdssdb/tests/pwdbs/test_sdss5db.py +++ b/python/sdssdb/tests/pwdbs/test_sdss5db.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 2:56:48 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:37:28 pm +# Last Modified: Tuesday, 24th March 2020 3:38:17 pm # Modified By: Brian Cherinka @@ -19,12 +19,12 @@ @pytest.mark.parametrize('database', [database], indirect=True) class TestSDSS5Db(object): - def test_target_type_fake_count(self, target_type_factory): - ''' test to count target_type select results + def test_category_fake_count(self, category_factory): + ''' test to count category select results creates an additional (or initial) batch of 10 fake - rows in the target_type table + rows in the category table ''' - target_type_factory.create_batch(10) - nt = targetdb.TargetType.select().count() + category_factory.create_batch(10) + nt = targetdb.Category.select().count() assert nt > 0 diff --git a/python/sdssdb/tests/sqladbs/test_factory.py b/python/sdssdb/tests/sqladbs/test_factory.py index 52e97114..d40c4429 100644 --- a/python/sdssdb/tests/sqladbs/test_factory.py +++ b/python/sdssdb/tests/sqladbs/test_factory.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 3:17:17 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:35:42 pm +# Last Modified: Tuesday, 24th March 2020 3:34:36 pm # Modified By: Brian Cherinka @@ -32,17 +32,17 @@ class TestFactory(object): def test_factory_fixture(self, session, user_factory): ''' test the factory can create new entries ''' - table = user_factory(name="Test Human") - assert table.id == 0 - assert table.name == "Test Human" - assert table.essence == 'human' + user = user_factory(name="Test Human") + assert user.id == 0 + assert user.name == "Test Human" + assert user.essence == 'human' def test_a_transaction(self, session, batchit): rows = session.query(models.User).all() assert len(rows) >= 10 def test_model_fixture(self, user): - ''' test a single new instance of model Table is created ''' + ''' test a single new instance of model User is created ''' assert isinstance(user, models.User) assert user.essence == 'human' assert user.name != 'Test Human' From 47c27a9948be4f65bbb4ecd040cc2130a9f85db5 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 15:43:34 -0400 Subject: [PATCH 20/29] adding some text and cleaning up --- python/sdssdb/tests/pwdbs/factories.py | 8 +++++++- python/sdssdb/tests/sqladbs/factories.py | 12 +++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/python/sdssdb/tests/pwdbs/factories.py b/python/sdssdb/tests/pwdbs/factories.py index c49ed7fb..801d03c3 100644 --- a/python/sdssdb/tests/pwdbs/factories.py +++ b/python/sdssdb/tests/pwdbs/factories.py @@ -7,7 +7,7 @@ # Created: Monday, 23rd March 2020 4:35:14 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 3:37:42 pm +# Last Modified: Tuesday, 24th March 2020 3:42:31 pm # Modified By: Brian Cherinka @@ -24,6 +24,12 @@ # factory declarations, see https://factoryboy.readthedocs.io/en/latest/reference.html#declarations faker = factory.faker.faker.Factory().create() +# +# This file contains factories used to generate fake data when needed. Each factory has a db, a Model +# or a db.Session applied to it. All columns in the table must be replaced with some fake data +# generator using faker or factory.Faker. +# + class UserFactory(PeeweeModelFactory): class Meta: diff --git a/python/sdssdb/tests/sqladbs/factories.py b/python/sdssdb/tests/sqladbs/factories.py index 4ae19d01..0cacfe5f 100644 --- a/python/sdssdb/tests/sqladbs/factories.py +++ b/python/sdssdb/tests/sqladbs/factories.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 5:45:43 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:39:38 pm +# Last Modified: Tuesday, 24th March 2020 3:43:06 pm # Modified By: Brian Cherinka @@ -16,7 +16,6 @@ from sdssdb.tests.sqladbs import models, database as db, get_model_from_database from sdssdb.sqlalchemy.mangadb import database as mangadb from sdssdb.sqlalchemy.archive import database as archive -from pytest_factoryboy import register # can use factory.Faker to create simple fake items # or more general faker object to create more complicated items like python lists, @@ -29,8 +28,13 @@ datadb = get_model_from_database(mangadb, 'datadb') sas = get_model_from_database(archive, 'sas') +# +# This file contains factories used to generate fake data when needed. Each factory has a db, a Model +# or a db.Session applied to it. All columns in the table must be replaced with some fake data +# generator using faker or factory.Faker. +# + -@register class UserFactory(factory.alchemy.SQLAlchemyModelFactory): ''' factory for testdb user table''' class Meta: @@ -43,7 +47,6 @@ class Meta: if datadb: - @register class WaveFactory(factory.alchemy.SQLAlchemyModelFactory): ''' factory for mangadb wavelength table ''' class Meta: @@ -56,7 +59,6 @@ class Meta: if sas: - @register class TreeFactory(factory.alchemy.SQLAlchemyModelFactory): ''' factory for archive db tree table ''' class Meta: From 670ed9e066381c788aa3a2c2d02f36ac656f546a Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 15:50:38 -0400 Subject: [PATCH 21/29] testing new travis install --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3bb44138..a00f9135 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,7 @@ install: - pip install pytest - pip install pytest-coverage - pip install coveralls -- python setup.py install +- pip install .[all] script: - pytest python/sdssdb/tests --cov python/sdssdb --cov-report html From 39582893063d86198fc61b67c1775d358e228f19 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 15:55:51 -0400 Subject: [PATCH 22/29] bugfix on travis pip install extras --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a00f9135..52cfcddd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,7 @@ install: - pip install pytest - pip install pytest-coverage - pip install coveralls -- pip install .[all] +- pip install .[all,dev] script: - pytest python/sdssdb/tests --cov python/sdssdb --cov-report html From 49cd91859ac020e3ce44b09df21ae60365ff8159 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 15:59:25 -0400 Subject: [PATCH 23/29] updating pytest req to >5.2 --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index ff41ec18..8463fe28 100644 --- a/setup.cfg +++ b/setup.cfg @@ -54,7 +54,7 @@ all = sqlalchemy>=1.3.6 progressbar2>=3.46.1 dev = - pytest>=3.0.7 + pytest>=5.2 pytest-cov>=2.4.0 pytest-sugar>=0.8.0 pydot>=1.4.1 From d139c60c59032e92edde6a85bc288ef15d2eb415 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 16:02:23 -0400 Subject: [PATCH 24/29] turning off pytest-sugar in travis for now --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 52cfcddd..de3d056d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,7 +37,7 @@ install: - pip install .[all,dev] script: -- pytest python/sdssdb/tests --cov python/sdssdb --cov-report html +- pytest -p no:sugar python/sdssdb/tests --cov python/sdssdb --cov-report html after_success: - coveralls From 83d5ce43af08c08a298853d517c1010871488850 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 16:07:20 -0400 Subject: [PATCH 25/29] adding postgresql to travis services --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index de3d056d..0e8f9dc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,9 @@ cache: sudo: false +services: + - postgresql + python: - '2.7' - '3.5' From 18d3f422ec7f744fb9ec4e918e4795a34f079819 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Tue, 24 Mar 2020 17:55:37 -0400 Subject: [PATCH 26/29] removing unnecessary fixture --- python/sdssdb/tests/sqladbs/test_mangadb.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/python/sdssdb/tests/sqladbs/test_mangadb.py b/python/sdssdb/tests/sqladbs/test_mangadb.py index cafce2d8..4df2d7a1 100644 --- a/python/sdssdb/tests/sqladbs/test_mangadb.py +++ b/python/sdssdb/tests/sqladbs/test_mangadb.py @@ -7,7 +7,7 @@ # Created: Friday, 23rd August 2019 12:35:00 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2019 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:33:11 pm +# Last Modified: Tuesday, 24th March 2020 5:55:03 pm # Modified By: Brian Cherinka @@ -25,12 +25,6 @@ faker = factory.faker.faker.Factory().create() - -@pytest.fixture(scope='session', autouse=True) -def skipdb(): - if database.connected is False: - pytest.skip('no mangadb found') - @pytest.mark.parametrize('database', [database], indirect=True) class TestMangaDB(object): From 60803319909b07c388b6ae026cac7b7003cb0a52 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Wed, 25 Mar 2020 12:10:28 -0400 Subject: [PATCH 27/29] updating changelog --- CHANGELOG.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ccac47d6..15fbcef3 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,12 @@ Changelog This document records the main changes to the ``sdssdb`` code. +* Test suite only runs where existing local databases found. Optionally run only `peewee` or `sqlalchemy` tests. +* Adds ability to generate fake data based on real database models for tests +* Adds ability to test against real or fake databases +* Write tests either for `peewee` or `sqlalchemy` databases +* :feature:`-` New framework for writing tests against databases + * :release:`0.3.2 <2020-03-10>` * Change ``operations-test`` profile to ``operations`` using the new machine hostname. * New schema and models for ``sdss5db.targetdb``. From d907357be8fa6aed479cab8607dd0857101555b8 Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Wed, 25 Mar 2020 13:16:09 -0400 Subject: [PATCH 28/29] renaming test classes to match db schema testing against --- python/sdssdb/tests/pwdbs/test_sdss5db.py | 4 ++-- python/sdssdb/tests/sqladbs/test_archivedb.py | 4 ++-- python/sdssdb/tests/sqladbs/test_mangadb.py | 6 +++--- python/sdssdb/tests/sqladbs/test_sdss5db.py | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/python/sdssdb/tests/pwdbs/test_sdss5db.py b/python/sdssdb/tests/pwdbs/test_sdss5db.py index 7b8a7fcb..b977a9c5 100644 --- a/python/sdssdb/tests/pwdbs/test_sdss5db.py +++ b/python/sdssdb/tests/pwdbs/test_sdss5db.py @@ -7,7 +7,7 @@ # Created: Sunday, 1st March 2020 2:56:48 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 3:38:17 pm +# Last Modified: Wednesday, 25th March 2020 1:14:34 pm # Modified By: Brian Cherinka @@ -17,7 +17,7 @@ @pytest.mark.parametrize('database', [database], indirect=True) -class TestSDSS5Db(object): +class TestTargetDb(object): def test_category_fake_count(self, category_factory): ''' test to count category select results diff --git a/python/sdssdb/tests/sqladbs/test_archivedb.py b/python/sdssdb/tests/sqladbs/test_archivedb.py index 8534313f..2d3a0da4 100644 --- a/python/sdssdb/tests/sqladbs/test_archivedb.py +++ b/python/sdssdb/tests/sqladbs/test_archivedb.py @@ -7,7 +7,7 @@ # Created: Monday, 2nd March 2020 1:24:15 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:00:40 pm +# Last Modified: Wednesday, 25th March 2020 1:14:13 pm # Modified By: Brian Cherinka @@ -19,7 +19,7 @@ @pytest.mark.parametrize('database', [database], indirect=True) -class TestArchiveDb(object): +class TestSas(object): def test_tree_list(self, session): tree_count = session.query(sas.Tree).count() diff --git a/python/sdssdb/tests/sqladbs/test_mangadb.py b/python/sdssdb/tests/sqladbs/test_mangadb.py index 4df2d7a1..1f483882 100644 --- a/python/sdssdb/tests/sqladbs/test_mangadb.py +++ b/python/sdssdb/tests/sqladbs/test_mangadb.py @@ -7,7 +7,7 @@ # Created: Friday, 23rd August 2019 12:35:00 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2019 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 5:55:03 pm +# Last Modified: Wednesday, 25th March 2020 1:13:53 pm # Modified By: Brian Cherinka @@ -25,9 +25,9 @@ faker = factory.faker.faker.Factory().create() - + @pytest.mark.parametrize('database', [database], indirect=True) -class TestMangaDB(object): +class TestDataDB(object): def test_added_wavelength(self, session, wave_factory): ''' test that we can add fake rows to real dbs that are undone ''' diff --git a/python/sdssdb/tests/sqladbs/test_sdss5db.py b/python/sdssdb/tests/sqladbs/test_sdss5db.py index 84fd49f7..16d588b3 100644 --- a/python/sdssdb/tests/sqladbs/test_sdss5db.py +++ b/python/sdssdb/tests/sqladbs/test_sdss5db.py @@ -7,7 +7,7 @@ # Created: Wednesday, 4th March 2020 3:01:10 pm # License: BSD 3-clause "New" or "Revised" License # Copyright (c) 2020 Brian Cherinka -# Last Modified: Tuesday, 24th March 2020 2:01:27 pm +# Last Modified: Wednesday, 25th March 2020 1:13:59 pm # Modified By: Brian Cherinka @@ -19,7 +19,7 @@ @pytest.mark.parametrize('database', [database], indirect=True) -class TestSDSS5Db(object): +class TestCatalogDb(object): def test_allwise_list(self, session): aw_count = session.query(catalogdb.AllWise).count() From 53ff5dc44866f5a8c182924cf7770a232019489e Mon Sep 17 00:00:00 2001 From: Brian Cherinka Date: Thu, 26 Mar 2020 12:56:57 -0400 Subject: [PATCH 29/29] updating some documentation --- docs/sphinx/contributing.rst | 257 ++++++++++++++++++++++++++++++++++- 1 file changed, 254 insertions(+), 3 deletions(-) diff --git a/docs/sphinx/contributing.rst b/docs/sphinx/contributing.rst index 82e4b3fe..1be8ad42 100644 --- a/docs/sphinx/contributing.rst +++ b/docs/sphinx/contributing.rst @@ -32,7 +32,11 @@ In addition to improvements to the code, you can contribute database connections | |__ schema1.py | |__ schema2.py -Let's imagine you want to create files for a database called ``awesomedb`` which has two schemas: ``amazing`` and ``stupendous``. Depending on whether you are creating model classes for peewee or sqlalchemy (or both), you will need to create a directory called ``awesomedb`` under the correct library directory with a ``__init__.py`` file and two ``amazing.py`` and ``stupendous.py`` files. The following sections will show you how to fill out those files depending on the library used. +Let's imagine you want to create files for a database called ``awesomedb`` which has two schemas: ``amazing`` +and ``stupendous``. Depending on whether you are creating model classes for peewee or sqlalchemy (or both), +you will need to create a directory called ``awesomedb`` under the correct library directory with a +``__init__.py`` file and two ``amazing.py`` and ``stupendous.py`` files. The following sections will show you +how to fill out those files depending on the library used. Peewee @@ -131,10 +135,257 @@ For the model classes you will need to write the files manually but there is no database.add_base(Base) -In this example we have two tables, ``user`` and ``address`` that we model as ``User`` and ``Address`` respectively. Note that we don't need to specify any column at this point, just the ``__tablename__`` metadata property. All model classes need to subclass from ``Base``, which in turn subclasses from `~sqlalchemy.ext.declarative.AbstractConcreteBase` and ``AwesomedbBase``. We can use the special attribute ``print_fields`` to define a list of fields that will be output in the standard representation of the model instances (primary keys and ``label`` fields are always output). +In this example we have two tables, ``user`` and ``address`` that we model as ``User`` and ``Address`` +respectively. Note that we don't need to specify any column at this point, just the ``__tablename__`` +metadata property. All model classes need to subclass from ``Base``, which in turn subclasses from +`~sqlalchemy.ext.declarative.AbstractConcreteBase` and ``AwesomedbBase``. We can use the special attribute +``print_fields`` to define a list of fields that will be output in the standard representation of the model +instances (primary keys and ``label`` fields are always output). -The ``define_relations`` function must contain all the foreign key relationships for this model. In this case there only one relationship that allows to retrieve the address for a given ``User`` (and its back reference). We need to encapsulate the relationships in a function so that they can be recreated if we change the database connection to point to a different database. Finally, we add the ``database.add_base(Base)`` statement to bind the base to the database connection. +The ``define_relations`` function must contain all the foreign key relationships for this model. In this +case there only one relationship that allows to retrieve the address for a given ``User`` (and its +back reference). We need to encapsulate the relationships in a function so that they can be recreated if +we change the database connection to point to a different database. Finally, we add the +``database.add_base(Base)`` statement to bind the base to the database connection. +Testing Your New Database +------------------------- + +After creating your database, you will want to ensure its stability and robustness as you expand its +capabilities over time. This can be done by writing tests against your database. The testing directory system +is very similar to the `sdssdb` database directory, with test database files located within separate library +folders for ``peewee`` databases (``pwdbs``) or ``sqlalchemy`` databases (``sqladbs``). + +.. code-block:: none + + tests + | + |__ pwdbs + | | + | |__ __init__.py + | |__ conftest.py + | |__ models.py + | |__ factories.py + | | + | |__ test_database1.py + | |__ test_database2.py + | + |__ sqladbs + | | + | |__ __init__.py + | |__ conftest.py + | |__ models.py + | |__ factories.py + | | + | |__ test_database1.py + | |__ test_database2.py + | + |__ conftest.py + |__ test_generic_items.py + +Most Python testing frameworks look for tests in files named ``test_xxxx.py``. Under each library we create a +``test_xxxx`` file for each new database we want to test. Since we've created a new ``awesomedb`` database, our +testing file will be ``test_awesomedb.py``. This file gets placed under either the ``pwdbs`` or ``sqladbs`` (or both) +depending on if your database is using ``peeewee`` or ``sqlalchemy``. + +``sdssdb`` uses `pytest `_ as its testing framework, and assumes user +familiarity with pytest. The test directories contain ``conftest.py`` files which are files used for sharing +fixture functions between tests. See `here `_ +for more details. You will also see files called ``models`` and ``factories``. We will come back to these later. + +Peewee +^^^^^^ + +Let's see what an example ``test_awesomedb.py`` might look like +:: + + import pytest + from sdssdb.peewee.awesomedb import database, stupendous + + + @pytest.mark.parametrize('database', [database], indirect=True) + class TestStupdendous(object): + + def test_user_count(self): + ''' test that count of user table returns results ''' + user_ct = stupendous.User.select().count() + assert user_ct > 0 + +We follow pytest's `test naming convention `_ +for naming test files as well as tests within files. In our ``test_awesomedb`` file, we group similar tests +by schema together into ``Test`` classes, i.e. for the ``stupendous`` schema, we create a ``TestStupendous`` class. +All tests related to the ``stupendous`` schema will be defined in this class. Individual tests within each class +are defined as methods on the class, named with ``test_xxxx``. + +In order for our test class to understand that we wish to use the ``awesomedb`` database for all defined tests, we +use the provided ``database`` fixture function and parametrize it with the ``awesomedb`` database. See +`fixture parametrization `_ to learn more +about how to parametrize tests or fixtures. + +We've defined a simple test, ``test_user_count``, that checks that our ``user`` table returns +some number of results > 0. In this case, we are a performing a simple select statement that does not modify the +database. If we are writing tests that perform write operations on the database, we could use the provided +``transaction`` fixture to ensure all changes are rolled back. + +SQLAlchemy +^^^^^^^^^^ + +The example ``test_awesomedb.py`` file for a ``sqlalchemy`` database will look very similar to the +``peewee`` version. +:: + + import pytest + from sdssdb.sqlalchemy.awesomedb import database + if database.connected: + from sdssdb.sqlalchemy.awesomedb import stupendous + + + @pytest.mark.parametrize('database', [database], indirect=True) + class TestStupdendous(object): + + def test_user_count(self, session): + ''' test that count of user table returns results ''' + user_ct = session.query(stupendous.User).count() + assert user_ct > 0 + +There are two main differences in this file from the ``peewee`` version. The first is that we must wrap the +import of the ``stupendous`` models inside a conditional that checks if the database has been successfully +connected to. This is needed because importing ``sqlalchemy`` models when no database exists, or +cannot connect, breaks other succcessful database imports. The second change is the use of the ``session`` +fixture inside the test. Since ``sqlalchemy`` needs a db session to perform queries, we use the +provided ``session`` pytest fixture. This fixture will ensure that all changes made to the database +are rolled back and not permanent. + +Generating and Inserting Fake Data into Your Database Tables +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you are only interested in writing simple tests that test real data in your database +tables, then you can stop here and start writing your tests. Sometimes, however, you may want to write +tests for special database queries or model functions where you don't quite have the right data, or enough +of it, loaded. In these cases, we can generate fake data and insert it dynamically into our database tables. +To do so, we have to create a "model factory". This factory creates fake data based on a database Model. + +The following examples use the following resources to generate fake data: + +- `factory_boy `_ - creates db model factories to generate fake entries +- `faker `_ - creates fake data as needed by models +- `pytest-factoryboy `_ - turns model factories into pytest fixtures + +Let's see how to create factories to generate fake Users and Addressess, inside the ``factories.py`` file, +using the ``peewee`` library implementation as an example. +:: + + from sdssdb.peewee.awesomedb import database as awesomedb, stupendous + from .factoryboy import PeeweeModelFactory + + class AddressFactory(PeeweeModelFactory): + # define a Meta class with the associated model and database + class Meta: + model = stupendous.Address + database = awesomedb + + # define fake data generators for all columns in the table + pk = factory.Sequence(lambda n: n) + street = factory.Faker('street_address') + city = factory.Faker('city') + state = factory.Faker('state_abbr') + zipcode = factory.Faker('zipcode') + full = factory.LazyAttribute(lambda a: f'{a.street}\n{a.city}, {a.state} {a.zipcode}') + + class UserFactory(PeeweeModelFactory): + class Meta: + model = stupendous.User + database = awesomedb + + pk = factory.Sequence(lambda n: n) + first = factory.Faker('first_name') + last = factory.Faker('last_name') + name = factory.LazyAttribute(lambda u: f'{u.first} {u.last}') + + # establishes the one-to-one relationship + address = factory.SubFactory(AddressFactory) + +If the ``User`` and ``Address`` models created previously have the following columns on each table, we use +the `factorboy declarations `_ +and `factory.Faker providers `_ to assign each column +a fake data generator. For each factory we need to define a ``Meta`` class in it that defines the database +model associated with it, as well as the database it belongs to. + +These factories allow us to create fake instances of data that automatically inserts into the +designated database table. To create an instance locally without database insertion, you can use +``UserFactory.build`` or to create in bulk, use ``UserFactory.create_batch``. +:: + + >>> user = UserFactory() + >>> user + >>> + >>> user.address + >>> + +The more common use however will be in tests. These factories automatically get converted into pytest +fixture functions using ``pytest-factoryboy``. Let's see how we would use this in ``test_awesomedb.py``. +:: + + @pytest.mark.parametrize('database', [database], indirect=True) + class TestStupdendous(object): + + def test_new_user(self, user_factory): + ''' test that we add a new user ''' + user_factory.create(first='New Bob') + user = stupendous.User.get(stupendous.User.first=='New Bob') + assert user.first == 'New Bob' + +Notice the lowercase-underscore syntax. This is the fixture name of the ``UserFactory``. The above examples +were written using the ``peeweee`` implementation. For real examples, see the sdss5db tests in +``tests/pwdbs/test_sdss5db.py`` and associated factories in ``test/pwdbs/factories.py``. The ``sqlalchemy`` +version of defining a factory is very similar. +:: + + import factory + from sdssdb.tests.sqladbs import get_model_from_database + from sdssdb.sqlalchemy.awesomedb import database as awesomedb + stupendous = get_model_from_database(awesomedb, 'stupendous') + + if stupendous: + class UserFactory(factory.alchemy.SQLAlchemyModelFactory): + ''' factory for stupendous user table ''' + class Meta: + model = stupendous.User + sqlalchemy_session = aweseomdb.Session # the SQLAlchemy session object + + # column definitions as before + pk = factory.Sequence(lambda n: n) + ... + +Because ``sqlalchemy`` models cannot be imported when no database exists locally, we must use +``get_model_from_database`` to conditionally import the models we need, and place the factory class inside +a conditional. Additionally, the factory Meta class needs the ``sqlalchemy`` Session rather the database itself. +All other behaviours and defintions are the same. For examples of ``sqlalchemy`` factories and their uses, see +``tests/sqladbs/factories.py`` and the mangadb tests in ``tests/sqladbs/test_mangadb.py``. + +Using a Generic Test Database +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sometimes you may want to test a function common to many databases, or a generic database connection, or simply +not want to mess with real databases. In these cases, a temporary test postgres database is available to use. +By default, when no real database is passed into the ``database`` fixture function, the test database is generated. +For example, the ``peewee`` test example case from earlier would now be the following, with the pytest +parametrization line removed. +:: + + class TestStupdendous(object): + + def test_user_count(self): + ''' test that count of user table returns results ''' + user_ct = stupendous.User.select().count() + assert user_ct > 0 + +This test would now use the temporary database, which is setup and destroyed for each test module. Because +the test database is created as a blank slate, all database models must be created as well, in addition to any +model factories. These models can be stored in the ``models.py`` file under the respective library directories. +See any of the ``models.py`` files for examples of creating test database models, and ``factories.py`` for their +associated factories. See any of the tests defined in ``test_factory.py`` for examples of how to write tests +against temporary database models defined in ``models.py``. Should I use Peewee or SQLAlchemy? ----------------------------------