deriva 1.7.1__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.
- deriva/config/annotation_config.py +2 -2
- deriva/config/rollback_annotation.py +1 -1
- deriva/core/__init__.py +1 -1
- deriva/core/datapath.py +203 -21
- deriva/core/ermrest_catalog.py +103 -23
- deriva/core/ermrest_model.py +955 -59
- deriva/core/hatrac_store.py +9 -20
- deriva/core/mmo.py +379 -0
- deriva/core/utils/globus_auth_utils.py +3 -1
- deriva/transfer/download/processors/postprocess/transfer_post_processor.py +2 -2
- deriva/transfer/download/processors/query/base_query_processor.py +2 -1
- deriva/transfer/upload/deriva_upload.py +5 -2
- {deriva-1.7.1.dist-info → deriva-1.7.4.dist-info}/METADATA +1 -1
- {deriva-1.7.1.dist-info → deriva-1.7.4.dist-info}/RECORD +27 -18
- tests/deriva/core/mmo/__init__.py +0 -0
- tests/deriva/core/mmo/base.py +300 -0
- tests/deriva/core/mmo/test_mmo_drop.py +252 -0
- tests/deriva/core/mmo/test_mmo_find.py +90 -0
- tests/deriva/core/mmo/test_mmo_prune.py +196 -0
- tests/deriva/core/mmo/test_mmo_rename.py +222 -0
- tests/deriva/core/mmo/test_mmo_replace.py +180 -0
- tests/deriva/core/test_datapath.py +52 -26
- tests/deriva/core/test_ermrest_model.py +782 -0
- {deriva-1.7.1.dist-info → deriva-1.7.4.dist-info}/LICENSE +0 -0
- {deriva-1.7.1.dist-info → deriva-1.7.4.dist-info}/WHEEL +0 -0
- {deriva-1.7.1.dist-info → deriva-1.7.4.dist-info}/entry_points.txt +0 -0
- {deriva-1.7.1.dist-info → deriva-1.7.4.dist-info}/top_level.txt +0 -0
|
@@ -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)
|