Skip to content

Commit

Permalink
Merge pull request #1136 from ethho/dev-tests-plat-166-schema
Browse files Browse the repository at this point in the history
PLAT-166: Migrate test_schema
  • Loading branch information
ethho authored Dec 15, 2023
2 parents 9f1e2c4 + 7bf18f0 commit 03db252
Showing 1 changed file with 248 additions and 0 deletions.
248 changes: 248 additions & 0 deletions tests/test_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import types
import pytest
import inspect
import datajoint as dj
from unittest.mock import patch
from inspect import getmembers
from . import schema
from . import PREFIX


class Ephys(dj.Imported):
definition = """ # This is already declare in ./schema.py
"""


def relation_selector(attr):
try:
return issubclass(attr, dj.Table)
except TypeError:
return False


def part_selector(attr):
try:
return issubclass(attr, dj.Part)
except TypeError:
return False


@pytest.fixture
def schema_empty_module(schema_any, schema_empty):
"""
Mock the module tests_old.schema_empty.
The test `test_namespace_population` will check that the module contains all the
classes in schema_any, after running `spawn_missing_classes`.
"""
namespace_dict = {
"_": schema_any,
"schema": schema_empty,
"Ephys": Ephys,
}
module = types.ModuleType("schema_empty")

# Add classes to the module's namespace
for k, v in namespace_dict.items():
setattr(module, k, v)

return module


@pytest.fixture
def schema_empty(connection_test, schema_any):
context = {**schema.LOCALS_ANY, "Ephys": Ephys}
schema_empty = dj.Schema(
PREFIX + "_test1", context=context, connection=connection_test
)
schema_empty(Ephys)
# load the rest of the classes
schema_empty.spawn_missing_classes(context=context)
yield schema_empty
schema_empty.drop()


def test_schema_size_on_disk(schema_any):
number_of_bytes = schema_any.size_on_disk
assert isinstance(number_of_bytes, int)


def test_schema_list(schema_any):
schemas = dj.list_schemas()
assert schema_any.database in schemas


def test_drop_unauthorized():
info_schema = dj.schema("information_schema")
with pytest.raises(dj.errors.AccessError):
info_schema.drop()


def test_namespace_population(schema_empty_module):
"""
With the schema_empty_module fixture, this test
mimics the behavior of `spawn_missing_classes`, as if the schema
was declared in a separate module and `spawn_missing_classes` was called in that namespace.
"""
# Spawn missing classes in the caller's (self) namespace.
schema_empty_module.schema.context = None
schema_empty_module.schema.spawn_missing_classes(context=None)
# Then add them to the mock module's namespace.
for k, v in locals().items():
if inspect.isclass(v):
setattr(schema_empty_module, k, v)

for name, rel in getmembers(schema, relation_selector):
assert hasattr(
schema_empty_module, name
), "{name} not found in schema_empty".format(name=name)
assert (
rel.__base__ is getattr(schema_empty_module, name).__base__
), "Wrong tier for {name}".format(name=name)

for name_part in dir(rel):
if name_part[0].isupper() and part_selector(getattr(rel, name_part)):
assert (
getattr(rel, name_part).__base__ is dj.Part
), "Wrong tier for {name}".format(name=name_part)


def test_undecorated_table():
"""
Undecorated user table classes should raise an informative exception upon first use
"""

class UndecoratedClass(dj.Manual):
definition = ""

a = UndecoratedClass()
with pytest.raises(dj.DataJointError):
print(a.full_table_name)


def test_reject_decorated_part(schema_any):
"""
Decorating a dj.Part table should raise an informative exception.
"""

class A(dj.Manual):
definition = ...

class B(dj.Part):
definition = ...

with pytest.raises(dj.DataJointError):
schema_any(A.B)
schema_any(A)


def test_unauthorized_database(db_creds_test):
"""
an attempt to create a database to which user has no privileges should raise an informative exception.
"""
with pytest.raises(dj.DataJointError):
dj.Schema(
"unauthorized_schema", connection=dj.conn(reset=True, **db_creds_test)
)


def test_drop_database(db_creds_test):
schema = dj.Schema(
PREFIX + "_drop_test", connection=dj.conn(reset=True, **db_creds_test)
)
assert schema.exists
schema.drop()
assert not schema.exists
schema.drop() # should do nothing


def test_overlapping_name(connection_test):
test_schema = dj.Schema(PREFIX + "_overlapping_schema", connection=connection_test)

@test_schema
class Unit(dj.Manual):
definition = """
id: int # simple id
"""

# hack to update the locals dictionary
locals()

@test_schema
class Cell(dj.Manual):
definition = """
type: varchar(32) # type of cell
"""

class Unit(dj.Part):
definition = """
-> master
-> Unit
"""

test_schema.drop()


def test_list_tables(schema_simp):
"""
https://github.com/datajoint/datajoint-python/issues/838
"""
assert set(
[
"reserved_word",
"#l",
"#a",
"__d",
"__b",
"__b__c",
"__e",
"__e__f",
"#outfit_launch",
"#outfit_launch__outfit_piece",
"#i_j",
"#j_i",
"#t_test_update",
"#data_a",
"#data_b",
"f",
"#argmax_test",
"#website",
"profile",
"profile__website",
]
) == set(schema_simp.list_tables())


def test_schema_save_any(schema_any):
assert "class Experiment(dj.Imported)" in schema_any.code


def test_schema_save_empty(schema_empty):
assert "class Experiment(dj.Imported)" in schema_empty.code


def test_uppercase_schema(db_creds_root):
"""
https://github.com/datajoint/datajoint-python/issues/564
"""
dj.conn(**db_creds_root, reset=True)
schema1 = dj.Schema("Schema_A")

@schema1
class Subject(dj.Manual):
definition = """
name: varchar(32)
"""

Schema_A = dj.VirtualModule("Schema_A", "Schema_A")

schema2 = dj.Schema("schema_b")

@schema2
class Recording(dj.Manual):
definition = """
-> Schema_A.Subject
id: smallint
"""

schema2.drop()
schema1.drop()

0 comments on commit 03db252

Please sign in to comment.