deriva 1.7.3__py3-none-any.whl → 1.7.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,300 @@
1
+ """Base class for MMO test cases.
2
+ """
3
+ import logging
4
+ import os
5
+ import unittest
6
+
7
+ from deriva.core import DerivaServer, ErmrestCatalog, get_credential
8
+ from deriva.core.ermrest_model import Schema, Table, Column, Key, ForeignKey, tag, builtin_types
9
+
10
+ logger = logging.getLogger(__name__)
11
+ logger.setLevel(os.getenv('DERIVA_PY_TEST_LOGLEVEL', default=logging.WARNING))
12
+ ermrest_hostname = os.getenv('DERIVA_PY_TEST_HOSTNAME')
13
+ ermrest_catalog_id = os.getenv('DERIVA_PY_TEST_CATALOG')
14
+ catalog = None
15
+
16
+ # baseline annotation doc for `dept` table
17
+ dept_annotations = {
18
+ tag.visible_columns: {
19
+ "compact": [
20
+ ["dept_schema", "dept_RID_key"],
21
+ ["dept_schema", "dept_dept_no_key"],
22
+ "name"
23
+ ],
24
+ "detailed": [
25
+ "RID",
26
+ "RCT",
27
+ {
28
+ "source": "RMT",
29
+ "markdown_name": "Last Modified Time"
30
+ },
31
+ "dept_no",
32
+ "name",
33
+ {
34
+ "source": "street_address",
35
+ "markdown_name": "Number and Street Name"
36
+ },
37
+ "postal_code",
38
+ {
39
+ "sourcekey": "head_count",
40
+ "markdown_name": "Head Count"
41
+ },
42
+ {
43
+ "display": {
44
+ "wait_for": [
45
+ "personnel"
46
+ ],
47
+ "template_engine": "handlebars",
48
+ "markdown_pattern": "{{#each personnel}}{{{this.values.name}}}{{#unless @last}}, {{/unless}}{{/each}}."
49
+ },
50
+ "markdown_name": "Personnel"
51
+ }
52
+ ]
53
+ },
54
+ tag.visible_foreign_keys: {
55
+ "*": [
56
+ [
57
+ "person_schema",
58
+ "person_dept_fkey"
59
+ ]
60
+ ]
61
+ },
62
+ tag.source_definitions: {
63
+ "columns": [
64
+ "dept_no",
65
+ "name",
66
+ "RID",
67
+ "country"
68
+ ],
69
+ "sources": {
70
+ "personnel": {
71
+ "source": [
72
+ {
73
+ "inbound": [
74
+ "person_schema",
75
+ "person_dept_fkey"
76
+ ]
77
+ },
78
+ "name"
79
+ ]
80
+ },
81
+ "head_count": {
82
+ "source": [
83
+ {
84
+ "inbound": [
85
+ "person_schema",
86
+ "person_dept_fkey"
87
+ ]
88
+ },
89
+ "RID"
90
+ ],
91
+ "entity": False,
92
+ "aggregate": "cnt_d"
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ # baseline annotation doc for `person` table
99
+ person_annotations = {
100
+ tag.visible_columns: {
101
+ "compact": [
102
+ ["person_schema", "person_RID_key"],
103
+ "name"
104
+ ],
105
+ "detailed": [
106
+ "RID",
107
+ "name",
108
+ ["person_schema", "person_dept_fkey"],
109
+ {
110
+ "markdown_name": "Department Name",
111
+ "source": [
112
+ {
113
+ "outbound": [
114
+ "person_schema",
115
+ "person_dept_fkey"
116
+ ]
117
+ },
118
+ "name"
119
+ ],
120
+ "entity": False
121
+ },
122
+ {
123
+ "sourcekey": "dept_size",
124
+ "markdown_name": "Department Size"
125
+ },
126
+ {
127
+ "sourcekey": "dept_city",
128
+ "markdown_name": "City"
129
+ },
130
+ {
131
+ "markdown_name": "State or Province",
132
+ "source": [
133
+ {
134
+ "outbound": [
135
+ "person_schema",
136
+ "person_dept_fkey"
137
+ ]
138
+ },
139
+ "state"
140
+ ],
141
+ "entity": False
142
+ },
143
+ ]
144
+ },
145
+ tag.source_definitions: {
146
+ "columns": [
147
+ "RID",
148
+ "name",
149
+ "dept"
150
+ ],
151
+ "fkeys": [
152
+ ["person_schema", "person_dept_fkey"]
153
+ ],
154
+ "sources": {
155
+ "dept_size": {
156
+ "source": [
157
+ {
158
+ "outbound": [
159
+ "person_schema",
160
+ "person_dept_fkey"
161
+ ]
162
+ },
163
+ {
164
+ "inbound": [
165
+ "person_schema",
166
+ "person_dept_fkey"
167
+ ]
168
+ },
169
+ "RID"
170
+ ],
171
+ "entity": False,
172
+ "aggregate": "cnt_d"
173
+ },
174
+ "dept_city": {
175
+ "markdown_name": "City",
176
+ "source": [
177
+ {
178
+ "outbound": [
179
+ "person_schema",
180
+ "person_dept_fkey"
181
+ ]
182
+ },
183
+ "city"
184
+ ],
185
+ "entity": False
186
+ }
187
+ },
188
+ "search-box": {
189
+ "or": [
190
+ {
191
+ "source": "last_name"
192
+ }
193
+ ]
194
+ }
195
+ }
196
+ }
197
+
198
+
199
+ @unittest.skipUnless(ermrest_hostname, 'ERMrest hostname not defined.')
200
+ class BaseMMOTestCase (unittest.TestCase):
201
+
202
+ @classmethod
203
+ def setUpClass(cls):
204
+ BaseMMOTestCase.setUpCatalog()
205
+
206
+ @classmethod
207
+ def setUpCatalog(cls):
208
+ global catalog
209
+
210
+ # create catalog
211
+ server = DerivaServer('https', ermrest_hostname, credentials=get_credential(ermrest_hostname))
212
+ if ermrest_catalog_id:
213
+ logger.debug(f'Connecting to {ermrest_hostname}/ermrest/catalog/{ermrest_catalog_id}')
214
+ catalog = server.connect_ermrest(ermrest_catalog_id)
215
+ else:
216
+ catalog = server.create_ermrest_catalog()
217
+ logger.debug(f'Created {ermrest_hostname}/ermrest/catalog/{catalog.catalog_id}')
218
+
219
+ # get the model
220
+ model = catalog.getCatalogModel()
221
+
222
+ # drop all schemas (except 'public')
223
+ for sname in [sname for sname in model.schemas if sname != 'public']:
224
+ model.schemas[sname].drop(cascade=True)
225
+
226
+ # recreate schemas
227
+ for sname in ["dept_schema", "person_schema"]:
228
+ model.create_schema(Schema.define(sname))
229
+
230
+ # create `dept` table
231
+ model.schemas["dept_schema"].create_table(
232
+ Table.define(
233
+ 'dept',
234
+ column_defs=[
235
+ Column.define('dept_no', builtin_types.int8),
236
+ Column.define('name', builtin_types.text),
237
+ Column.define('street_address', builtin_types.text),
238
+ Column.define('city', builtin_types.text),
239
+ Column.define('state', builtin_types.text),
240
+ Column.define('country', builtin_types.text),
241
+ Column.define('postal_code', builtin_types.int8)
242
+ ],
243
+ key_defs=[
244
+ Key.define(['dept_no'])
245
+ ],
246
+ annotations=dept_annotations
247
+ )
248
+ )
249
+
250
+ # create `person` table
251
+ model.schemas["person_schema"].create_table(
252
+ Table.define(
253
+ 'person',
254
+ column_defs=[
255
+ Column.define('name', builtin_types.text),
256
+ Column.define('dept', builtin_types.int8),
257
+ Column.define('last_name', builtin_types.text)
258
+ ],
259
+ fkey_defs=[
260
+ ForeignKey.define(['dept'], "dept_schema", 'dept', ['dept_no'])
261
+ ],
262
+ annotations=person_annotations
263
+ )
264
+ )
265
+
266
+ # populate for good measure (though not necessary for current set of tests)
267
+ pbuilder = catalog.getPathBuilder()
268
+
269
+ pbuilder.dept_schema.dept.insert([
270
+ {'dept_no': 1, 'name': 'Dept A', 'street_address': '123 Main St', 'city': 'Anywhere', 'state': 'CA', 'country': 'US', 'postal_code': 98765},
271
+ {'dept_no': 2, 'name': 'Dept B', 'street_address': '777 Oak Ave', 'city': 'Somewhere', 'state': 'NY', 'country': 'US', 'postal_code': 12345}
272
+ ])
273
+
274
+ pbuilder.person_schema.person.insert([
275
+ {'name': 'John', 'dept': 1},
276
+ {'name': 'Helena', 'dept': 1},
277
+ {'name': 'Ben', 'dept': 1},
278
+ {'name': 'Sonia', 'dept': 2},
279
+ {'name': 'Rafael', 'dept': 2},
280
+ ])
281
+
282
+ @classmethod
283
+ def tearDownClass(cls):
284
+ BaseMMOTestCase.tearDownCatalog()
285
+
286
+ @classmethod
287
+ def tearDownCatalog(cls):
288
+ global catalog
289
+ if not ermrest_catalog_id and isinstance(catalog, ErmrestCatalog) and int(catalog.catalog_id) > 1000:
290
+ # note: the '... > 1000' clause is intended to safeguard against accidental deletion of production catalogs in the usual (lower) range
291
+ catalog.delete_ermrest_catalog(really=True)
292
+ catalog = None
293
+
294
+ def setUp(self):
295
+ # reset annotations to baseline
296
+ assert isinstance(catalog, ErmrestCatalog)
297
+ self.model = catalog.getCatalogModel()
298
+
299
+ def tearDown(self):
300
+ pass
@@ -0,0 +1,252 @@
1
+ """Unit tests for MMO+DDL Drop operations.
2
+ """
3
+ import os
4
+ import logging
5
+ from deriva.core import mmo
6
+ from deriva.core.ermrest_model import UpdateMappings
7
+ from tests.deriva.core.mmo.base import BaseMMOTestCase
8
+
9
+ logger = logging.getLogger(__name__)
10
+ logger.setLevel(os.getenv('DERIVA_PY_TEST_LOGLEVEL', default=logging.WARNING))
11
+
12
+
13
+ class TestMMOxDDLDrop (BaseMMOTestCase):
14
+
15
+ @classmethod
16
+ def setUpClass(cls):
17
+ """Don't bother setting up catalog for the suite.
18
+ """
19
+ pass
20
+
21
+ def setUp(self):
22
+ """Setup catalog for each unit test.
23
+ """
24
+ TestMMOxDDLDrop.setUpCatalog()
25
+ super().setUp()
26
+
27
+ def test_drop_key(self):
28
+ kname = ["dept_schema", "dept_dept_no_key"]
29
+
30
+ def test():
31
+ matches = mmo.find(self.model, kname)
32
+ return len(matches)
33
+
34
+ self.assertTrue(test())
35
+ t = self.model.schemas[kname[0]].tables["dept"]
36
+ key = t.keys[(t.schema, kname[1])]
37
+ key.drop(cascade=True, update_mappings=UpdateMappings.immediate)
38
+ self.assertFalse(test())
39
+
40
+ def test_drop_key_deferred(self):
41
+ kname = ["dept_schema", "dept_dept_no_key"]
42
+
43
+ def test(model):
44
+ matches = mmo.find(model, kname)
45
+ return len(matches)
46
+
47
+ self.assertTrue(test(self.model))
48
+ t = self.model.schemas[kname[0]].tables["dept"]
49
+ key = t.keys[(t.schema, kname[1])]
50
+ key.drop(cascade=True, update_mappings=UpdateMappings.deferred)
51
+ self.assertTrue(test(self.model.catalog.getCatalogModel()))
52
+ self.model.apply()
53
+ self.assertFalse(test(self.model.catalog.getCatalogModel()))
54
+
55
+ def test_drop_key_cascade(self):
56
+ kname = ["dept_schema", "dept_dept_no_key"]
57
+ fkname = ["person_schema", "person_dept_fkey"]
58
+
59
+ def test(name):
60
+ matches = mmo.find(self.model, name)
61
+ return len(matches)
62
+
63
+ self.assertTrue(test(kname))
64
+ self.assertTrue(test(fkname))
65
+ t = self.model.schemas[kname[0]].tables["dept"]
66
+ key = t.keys[(t.schema, kname[1])]
67
+ key.drop(cascade=True, update_mappings=UpdateMappings.immediate)
68
+ self.assertFalse(test(kname))
69
+ self.assertFalse(test(fkname))
70
+
71
+ def test_drop_fkey(self):
72
+ fkname = ["person_schema", "person_dept_fkey"]
73
+
74
+ def test():
75
+ matches = mmo.find(self.model, fkname)
76
+ return len(matches)
77
+
78
+ self.assertTrue(test())
79
+ self.model.fkey(fkname).drop(update_mappings=UpdateMappings.immediate)
80
+ self.assertFalse(test())
81
+
82
+ def test_drop_fkey_deferred(self):
83
+ fkname = ["person_schema", "person_dept_fkey"]
84
+
85
+ def test(model):
86
+ matches = mmo.find(model, fkname)
87
+ return len(matches)
88
+
89
+ self.assertTrue(test(self.model))
90
+ self.model.fkey(fkname).drop(update_mappings=UpdateMappings.deferred)
91
+ self.assertTrue(test(self.model.catalog.getCatalogModel()))
92
+ self.model.apply()
93
+ self.assertFalse(test(self.model.catalog.getCatalogModel()))
94
+
95
+ def test_drop_col(self):
96
+ cname = ["person_schema", "person", "last_name"]
97
+
98
+ def test():
99
+ matches = mmo.find(self.model, cname)
100
+ return len(matches)
101
+
102
+ self.assertTrue(test())
103
+ t = self.model.schemas[cname[0]].tables[cname[1]]
104
+ col = t.columns[cname[2]]
105
+ col.drop(update_mappings=UpdateMappings.immediate)
106
+ self.assertFalse(test())
107
+
108
+ def test_drop_col_deferred(self):
109
+ cname = ["person_schema", "person", "last_name"]
110
+
111
+ def test(model):
112
+ matches = mmo.find(model, cname)
113
+ return len(matches)
114
+
115
+ self.assertTrue(test(self.model))
116
+ t = self.model.schemas[cname[0]].tables[cname[1]]
117
+ col = t.columns[cname[2]]
118
+ col.drop(update_mappings=UpdateMappings.deferred)
119
+ self.assertTrue(test(self.model.catalog.getCatalogModel()))
120
+ self.model.apply()
121
+ self.assertFalse(test(self.model.catalog.getCatalogModel()))
122
+
123
+ def test_drop_col_cascade_key(self):
124
+ cname = ["dept_schema", "dept", "dept_no"]
125
+ kname = ["dept_schema", "dept_dept_no_key"]
126
+ fkname = ["person_schema", "person_dept_fkey"]
127
+
128
+ def test(name):
129
+ matches = mmo.find(self.model, name)
130
+ return len(matches)
131
+
132
+ for name in [cname, kname, fkname]:
133
+ with self.subTest(f'subTest {name}'):
134
+ self.assertTrue(test(name))
135
+
136
+ t = self.model.schemas[cname[0]].tables[cname[1]]
137
+ col = t.columns[cname[2]]
138
+ col.drop(cascade=True, update_mappings=UpdateMappings.immediate)
139
+
140
+ for name in [cname, kname, fkname]:
141
+ with self.subTest(f'subTest {name}'):
142
+ self.assertFalse(test(name))
143
+
144
+ def test_drop_col_cascade_fkey(self):
145
+ cname = ["person_schema", "person", "dept"]
146
+ fkname = ["person_schema", "person_dept_fkey"]
147
+
148
+ def test(name):
149
+ matches = mmo.find(self.model, name)
150
+ return len(matches)
151
+
152
+ self.assertTrue(test(cname))
153
+ self.assertTrue(test(fkname))
154
+ t = self.model.schemas[cname[0]].tables[cname[1]]
155
+ col = t.columns[cname[2]]
156
+ col.drop(cascade=True, update_mappings=UpdateMappings.immediate)
157
+ self.assertFalse(test(cname))
158
+ self.assertFalse(test(fkname))
159
+
160
+ def test_drop_table_cascade(self):
161
+ cname = ["dept_schema", "dept", "dept_no"]
162
+ kname = ["dept_schema", "dept_dept_no_key"]
163
+ fkname = ["person_schema", "person_dept_fkey"]
164
+
165
+ def test(name):
166
+ matches = mmo.find(self.model, name)
167
+ return len(matches)
168
+
169
+ for name in [cname, kname, fkname]:
170
+ with self.subTest(f'subTest {name}'):
171
+ self.assertTrue(test(name))
172
+
173
+ t = self.model.schemas[cname[0]].tables[cname[1]]
174
+ t.drop(cascade=True, update_mappings=UpdateMappings.immediate)
175
+
176
+ for name in [cname, kname, fkname]:
177
+ with self.subTest(f'subTest {name}'):
178
+ self.assertFalse(test(name))
179
+
180
+ def test_drop_table_cascade_deferred(self):
181
+ cname = ["person_schema", "person", "name"]
182
+ kname = ["person_schema", "person_RID_key"]
183
+ fkname = ["person_schema", "person_dept_fkey"]
184
+
185
+ def test(model, name):
186
+ matches = mmo.find(model, name)
187
+ return len(matches)
188
+
189
+ for name in [cname, kname, fkname]:
190
+ with self.subTest(f'subtest {name} precondition'):
191
+ self.assertTrue(test(self.model, name))
192
+
193
+ t = self.model.schemas[cname[0]].tables[cname[1]]
194
+ t.drop(cascade=True, update_mappings=UpdateMappings.deferred)
195
+
196
+ # fkname should linger on in the dept table's annotations until the update is applied to the model
197
+ self.assertTrue(test(self.model.catalog.getCatalogModel(), fkname))
198
+ self.model.apply()
199
+ self.assertFalse(test(self.model.catalog.getCatalogModel(), fkname))
200
+
201
+ def test_drop_table(self):
202
+ fkname = ["person_schema", "person_dept_fkey"]
203
+
204
+ def test(name):
205
+ matches = mmo.find(self.model, name)
206
+ return len(matches)
207
+
208
+ self.assertTrue(test(fkname))
209
+ t = self.model.schemas["person_schema"].tables["person"]
210
+ t.drop(update_mappings=UpdateMappings.immediate)
211
+ self.assertFalse(test(fkname))
212
+
213
+ def test_drop_schema_cascade(self):
214
+ cname = ["dept_schema", "dept", "dept_no"]
215
+ kname = ["dept_schema", "dept_dept_no_key"]
216
+ fkname = ["person_schema", "person_dept_fkey"]
217
+
218
+ def test(name):
219
+ matches = mmo.find(self.model, name)
220
+ return len(matches)
221
+
222
+ for name in [cname, kname, fkname]:
223
+ with self.subTest(f'subtest {name} precondition'):
224
+ self.assertTrue(test(name))
225
+
226
+ s = self.model.schemas[cname[0]]
227
+ s.drop(cascade=True, update_mappings=UpdateMappings.immediate)
228
+
229
+ for name in [cname, kname, fkname]:
230
+ with self.subTest(f'subtest {name} postcondition'):
231
+ self.assertFalse(test(name))
232
+
233
+ def test_drop_schema_cascade_deferred(self):
234
+ cname = ["person_schema", "person", "name"]
235
+ kname = ["person_schema", "person_RID_key"]
236
+ fkname = ["person_schema", "person_dept_fkey"]
237
+
238
+ def test(model, name):
239
+ matches = mmo.find(model, name)
240
+ return len(matches)
241
+
242
+ for name in [cname, kname, fkname]:
243
+ with self.subTest(f'subtest {name} precondition'):
244
+ self.assertTrue(test(self.model, name))
245
+
246
+ s = self.model.schemas[cname[0]]
247
+ s.drop(cascade=True, update_mappings=UpdateMappings.deferred)
248
+
249
+ # fkname should linger on in the dept table's annotations until the update is applied to the model
250
+ self.assertTrue(test(self.model.catalog.getCatalogModel(), fkname))
251
+ self.model.apply()
252
+ self.assertFalse(test(self.model.catalog.getCatalogModel(), fkname))
@@ -0,0 +1,90 @@
1
+ """Unit tests for MMO find operation.
2
+ """
3
+ import logging
4
+ import os
5
+
6
+ from deriva.core import mmo
7
+ from deriva.core.ermrest_model import tag
8
+ from tests.deriva.core.mmo.base import BaseMMOTestCase
9
+
10
+ logger = logging.getLogger(__name__)
11
+ logger.setLevel(os.getenv('DERIVA_PY_TEST_LOGLEVEL', default=logging.WARNING))
12
+
13
+
14
+ def first_element_match(l1, l2):
15
+ return isinstance(l1, list) and isinstance(l2, list) and \
16
+ len(l1) == len(l2) and len(l1) > 1 and \
17
+ l1[0] == l2[0]
18
+
19
+
20
+ class TestMMOFind (BaseMMOTestCase):
21
+
22
+ def test_find_key_in_vizcols(self):
23
+ matches = mmo.find(self.model, ["dept_schema", "dept_RID_key"])
24
+ self.assertEqual(len(matches), 1)
25
+
26
+ def test_find_key_in_vizcols_by_schema_only(self):
27
+ matches = mmo.find(self.model, ["dept_schema", None])
28
+ self.assertGreater(len(matches), 0)
29
+
30
+ def test_find_col_in_vizcols(self):
31
+ matches = mmo.find(self.model, ["dept_schema", "dept", "RCT"])
32
+ self.assertEqual(len(matches), 1)
33
+
34
+ def test_find_col_in_vizcols_pseudocol_simple(self):
35
+ matches = mmo.find(self.model, ["dept_schema", "dept", "RMT"])
36
+ self.assertEqual(len(matches), 1)
37
+
38
+ def test_find_col_in_vizcols_pseudocol(self):
39
+ matches = mmo.find(self.model, ["dept_schema", "dept", "name"])
40
+ self.assertTrue(any([m.anchor.name == 'person' and m.tag == tag.visible_columns and isinstance(m.mapping, dict) for m in matches]))
41
+
42
+ def test_find_col_in_sourcedefs_columns(self):
43
+ matches = mmo.find(self.model, ["person_schema", "person", "dept"])
44
+ self.assertTrue(any([m.anchor.name == 'person' and m.tag == tag.source_definitions and m.mapping == 'dept' for m in matches]))
45
+
46
+ def test_find_col_in_sourcedefs_sources(self):
47
+ matches = mmo.find(self.model, ["person_schema", "person", "RID"])
48
+ self.assertTrue(any([m.tag == tag.source_definitions and m.mapping == 'dept_size' for m in matches]))
49
+
50
+ def test_find_fkey_in_vizfkeys(self):
51
+ fkname = ["person_schema", "person_dept_fkey"]
52
+ matches = mmo.find(self.model, fkname)
53
+ self.assertTrue(any([m.tag == tag.visible_foreign_keys and m.mapping == fkname for m in matches]))
54
+
55
+ def test_find_fkey_in_vizfkeys_by_schema_only(self):
56
+ fkname = ["person_schema", None]
57
+ matches = mmo.find(self.model, fkname)
58
+ self.assertTrue(any([m.tag == tag.visible_foreign_keys and first_element_match(m.mapping, fkname) for m in matches]))
59
+
60
+ def test_find_fkey_in_vizcols(self):
61
+ fkname = ["person_schema", "person_dept_fkey"]
62
+ matches = mmo.find(self.model, fkname)
63
+ self.assertTrue(any([m.tag == tag.visible_columns and m.mapping == fkname for m in matches]))
64
+
65
+ def test_find_fkey_in_vizcols_by_schema_only(self):
66
+ fkname = ["person_schema", None]
67
+ matches = mmo.find(self.model, fkname)
68
+ self.assertTrue(any([m.tag == tag.visible_columns and first_element_match(m.mapping, fkname) for m in matches]))
69
+
70
+ def test_find_fkey_in_sourcedefs_sources(self):
71
+ matches = mmo.find(self.model, ["person_schema", "person_dept_fkey"])
72
+ self.assertTrue(any([m.tag == tag.source_definitions and m.mapping == 'personnel' for m in matches]))
73
+
74
+ def test_find_fkey_in_sourcedefs_sources_by_schema_only(self):
75
+ matches = mmo.find(self.model, ["person_schema", None])
76
+ self.assertTrue(any([m.tag == tag.source_definitions and m.mapping == 'personnel' for m in matches]))
77
+
78
+ def test_find_fkey_in_sourcedefs_fkeys(self):
79
+ fkname = ["person_schema", "person_dept_fkey"]
80
+ matches = mmo.find(self.model, fkname)
81
+ self.assertTrue(any([m.tag == tag.source_definitions and m.mapping == fkname for m in matches]))
82
+
83
+ def test_find_fkey_in_sourcedefs_fkeys_by_schema_only(self):
84
+ fkname = ["person_schema", None]
85
+ matches = mmo.find(self.model, fkname)
86
+ self.assertTrue(any([m.tag == tag.source_definitions and m.context == 'fkeys' and first_element_match(m.mapping, fkname) for m in matches]))
87
+
88
+ def test_find_col_in_search_box(self):
89
+ matches = mmo.find(self.model, ["person_schema", "person", "last_name"])
90
+ self.assertTrue(len(matches) == 1)