-
Notifications
You must be signed in to change notification settings - Fork 92
PLAT-166: Migrate test_schema #1136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f34ca35
7598441
55bf4ea
5fed6a5
58c6103
b9ccb4f
0e194e7
7bf18f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was an interesting problem. In |
||
|
|
||
|
|
||
| @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 | ||
| """ | ||
|
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A bit inconsistent in the way we add |
||
| 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() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another part of the move towards putting these credentials in fixtures.