singlestoredb 0.4.0__py3-none-any.whl → 1.0.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.
Potentially problematic release.
This version of singlestoredb might be problematic. Click here for more details.
- singlestoredb/__init__.py +33 -1
- singlestoredb/alchemy/__init__.py +90 -0
- singlestoredb/auth.py +5 -1
- singlestoredb/config.py +116 -14
- singlestoredb/connection.py +483 -516
- singlestoredb/converters.py +238 -135
- singlestoredb/exceptions.py +30 -2
- singlestoredb/functions/__init__.py +1 -0
- singlestoredb/functions/decorator.py +142 -0
- singlestoredb/functions/dtypes.py +1639 -0
- singlestoredb/functions/ext/__init__.py +2 -0
- singlestoredb/functions/ext/arrow.py +375 -0
- singlestoredb/functions/ext/asgi.py +661 -0
- singlestoredb/functions/ext/json.py +427 -0
- singlestoredb/functions/ext/mmap.py +306 -0
- singlestoredb/functions/ext/rowdat_1.py +744 -0
- singlestoredb/functions/signature.py +673 -0
- singlestoredb/fusion/__init__.py +11 -0
- singlestoredb/fusion/graphql.py +213 -0
- singlestoredb/fusion/handler.py +621 -0
- singlestoredb/fusion/handlers/stage.py +257 -0
- singlestoredb/fusion/handlers/utils.py +162 -0
- singlestoredb/fusion/handlers/workspace.py +412 -0
- singlestoredb/fusion/registry.py +164 -0
- singlestoredb/fusion/result.py +399 -0
- singlestoredb/http/__init__.py +27 -0
- singlestoredb/{http.py → http/connection.py} +555 -154
- singlestoredb/management/__init__.py +3 -0
- singlestoredb/management/billing_usage.py +148 -0
- singlestoredb/management/cluster.py +14 -6
- singlestoredb/management/manager.py +100 -38
- singlestoredb/management/organization.py +188 -0
- singlestoredb/management/region.py +5 -5
- singlestoredb/management/utils.py +281 -2
- singlestoredb/management/workspace.py +1344 -49
- singlestoredb/{clients/pymysqlsv → mysql}/__init__.py +16 -21
- singlestoredb/{clients/pymysqlsv → mysql}/_auth.py +39 -8
- singlestoredb/{clients/pymysqlsv → mysql}/charset.py +26 -23
- singlestoredb/{clients/pymysqlsv/connections.py → mysql/connection.py} +532 -165
- singlestoredb/{clients/pymysqlsv → mysql}/constants/CLIENT.py +0 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/COMMAND.py +0 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/CR.py +0 -2
- singlestoredb/{clients/pymysqlsv → mysql}/constants/ER.py +0 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/FIELD_TYPE.py +1 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/FLAG.py +0 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/SERVER_STATUS.py +0 -1
- singlestoredb/mysql/converters.py +271 -0
- singlestoredb/{clients/pymysqlsv → mysql}/cursors.py +228 -112
- singlestoredb/mysql/err.py +92 -0
- singlestoredb/{clients/pymysqlsv → mysql}/optionfile.py +5 -4
- singlestoredb/{clients/pymysqlsv → mysql}/protocol.py +49 -20
- singlestoredb/mysql/tests/__init__.py +19 -0
- singlestoredb/{clients/pymysqlsv → mysql}/tests/base.py +32 -12
- singlestoredb/mysql/tests/conftest.py +37 -0
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_DictCursor.py +11 -7
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_SSCursor.py +17 -12
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_basic.py +32 -24
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_connection.py +130 -119
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_converters.py +9 -7
- singlestoredb/mysql/tests/test_cursor.py +141 -0
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_err.py +3 -2
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_issues.py +35 -27
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_load_local.py +13 -11
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_nextset.py +7 -3
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_optionfile.py +2 -1
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/__init__.py +1 -1
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/capabilities.py +19 -17
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/dbapi20.py +31 -22
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +3 -4
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +24 -20
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +4 -4
- singlestoredb/{clients/pymysqlsv → mysql}/times.py +3 -4
- singlestoredb/pytest.py +283 -0
- singlestoredb/tests/empty.sql +0 -0
- singlestoredb/tests/ext_funcs/__init__.py +385 -0
- singlestoredb/tests/test.sql +210 -0
- singlestoredb/tests/test2.sql +1 -0
- singlestoredb/tests/test_basics.py +482 -115
- singlestoredb/tests/test_config.py +13 -13
- singlestoredb/tests/test_connection.py +241 -305
- singlestoredb/tests/test_dbapi.py +27 -0
- singlestoredb/tests/test_ext_func.py +1193 -0
- singlestoredb/tests/test_ext_func_data.py +1101 -0
- singlestoredb/tests/test_fusion.py +465 -0
- singlestoredb/tests/test_http.py +32 -26
- singlestoredb/tests/test_management.py +588 -8
- singlestoredb/tests/test_plugin.py +33 -0
- singlestoredb/tests/test_results.py +11 -12
- singlestoredb/tests/test_udf.py +687 -0
- singlestoredb/tests/utils.py +3 -2
- singlestoredb/utils/config.py +58 -0
- singlestoredb/utils/debug.py +13 -0
- singlestoredb/utils/mogrify.py +151 -0
- singlestoredb/utils/results.py +4 -1
- singlestoredb-1.0.4.dist-info/METADATA +139 -0
- singlestoredb-1.0.4.dist-info/RECORD +112 -0
- {singlestoredb-0.4.0.dist-info → singlestoredb-1.0.4.dist-info}/WHEEL +1 -1
- singlestoredb-1.0.4.dist-info/entry_points.txt +2 -0
- singlestoredb/clients/pymysqlsv/converters.py +0 -365
- singlestoredb/clients/pymysqlsv/err.py +0 -144
- singlestoredb/clients/pymysqlsv/tests/__init__.py +0 -19
- singlestoredb/clients/pymysqlsv/tests/test_cursor.py +0 -133
- singlestoredb/clients/pymysqlsv/tests/thirdparty/test_MySQLdb/__init__.py +0 -9
- singlestoredb/drivers/__init__.py +0 -45
- singlestoredb/drivers/base.py +0 -198
- singlestoredb/drivers/cymysql.py +0 -38
- singlestoredb/drivers/http.py +0 -47
- singlestoredb/drivers/mariadb.py +0 -40
- singlestoredb/drivers/mysqlconnector.py +0 -49
- singlestoredb/drivers/mysqldb.py +0 -60
- singlestoredb/drivers/pymysql.py +0 -37
- singlestoredb/drivers/pymysqlsv.py +0 -35
- singlestoredb/drivers/pyodbc.py +0 -65
- singlestoredb-0.4.0.dist-info/METADATA +0 -111
- singlestoredb-0.4.0.dist-info/RECORD +0 -86
- /singlestoredb/{clients → fusion/handlers}/__init__.py +0 -0
- /singlestoredb/{clients/pymysqlsv → mysql}/constants/__init__.py +0 -0
- {singlestoredb-0.4.0.dist-info → singlestoredb-1.0.4.dist-info}/LICENSE +0 -0
- {singlestoredb-0.4.0.dist-info → singlestoredb-1.0.4.dist-info}/top_level.txt +0 -0
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
# type: ignore
|
|
3
|
-
"""SingleStoreDB
|
|
3
|
+
"""SingleStoreDB Management API testing."""
|
|
4
4
|
import os
|
|
5
|
+
import pathlib
|
|
5
6
|
import random
|
|
6
7
|
import re
|
|
7
8
|
import secrets
|
|
8
9
|
import unittest
|
|
9
10
|
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
10
13
|
import singlestoredb as s2
|
|
11
14
|
|
|
12
15
|
|
|
16
|
+
TEST_DIR = pathlib.Path(os.path.dirname(__file__))
|
|
17
|
+
|
|
18
|
+
|
|
13
19
|
def clean_name(s):
|
|
14
20
|
"""Change all non-word characters to -."""
|
|
15
21
|
return re.sub(r'[^\w]', r'-', s).replace('_', '-').lower()
|
|
16
22
|
|
|
17
23
|
|
|
24
|
+
@pytest.mark.management
|
|
18
25
|
class TestCluster(unittest.TestCase):
|
|
19
26
|
|
|
20
27
|
manager = None
|
|
@@ -26,7 +33,7 @@ class TestCluster(unittest.TestCase):
|
|
|
26
33
|
cls.manager = s2.manage_cluster()
|
|
27
34
|
|
|
28
35
|
us_regions = [x for x in cls.manager.regions if 'US' in x.name]
|
|
29
|
-
cls.password = secrets.token_urlsafe(20)
|
|
36
|
+
cls.password = secrets.token_urlsafe(20) + '-x&'
|
|
30
37
|
|
|
31
38
|
cls.cluster = cls.manager.create_cluster(
|
|
32
39
|
clean_name('cm-test-{}'.format(secrets.token_urlsafe(20)[:20])),
|
|
@@ -62,10 +69,24 @@ class TestCluster(unittest.TestCase):
|
|
|
62
69
|
def test_regions(self):
|
|
63
70
|
out = self.manager.regions
|
|
64
71
|
providers = {x.provider for x in out}
|
|
72
|
+
names = [x.name for x in out]
|
|
65
73
|
assert 'Azure' in providers, providers
|
|
66
74
|
assert 'GCP' in providers, providers
|
|
67
75
|
assert 'AWS' in providers, providers
|
|
68
76
|
|
|
77
|
+
objs = {}
|
|
78
|
+
ids = []
|
|
79
|
+
for item in out:
|
|
80
|
+
ids.append(item.id)
|
|
81
|
+
objs[item.id] = item
|
|
82
|
+
if item.name not in objs:
|
|
83
|
+
objs[item.name] = item
|
|
84
|
+
|
|
85
|
+
name = random.choice(names)
|
|
86
|
+
assert out[name] == objs[name]
|
|
87
|
+
id = random.choice(ids)
|
|
88
|
+
assert out[id] == objs[id]
|
|
89
|
+
|
|
69
90
|
def test_clusters(self):
|
|
70
91
|
clusters = self.manager.clusters
|
|
71
92
|
ids = [x.id for x in clusters]
|
|
@@ -141,6 +162,8 @@ class TestCluster(unittest.TestCase):
|
|
|
141
162
|
trues = ['1', 'on', 'true']
|
|
142
163
|
pure_python = os.environ.get('SINGLESTOREDB_PURE_PYTHON', '0').lower() in trues
|
|
143
164
|
|
|
165
|
+
self.skipTest('Connection test is disable due to flakey server')
|
|
166
|
+
|
|
144
167
|
if pure_python:
|
|
145
168
|
self.skipTest('Connections through managed service are disabled')
|
|
146
169
|
|
|
@@ -163,6 +186,7 @@ class TestCluster(unittest.TestCase):
|
|
|
163
186
|
assert 'endpoint' in cm.exception.msg, cm.exception.msg
|
|
164
187
|
|
|
165
188
|
|
|
189
|
+
@pytest.mark.management
|
|
166
190
|
class TestWorkspace(unittest.TestCase):
|
|
167
191
|
|
|
168
192
|
manager = None
|
|
@@ -188,7 +212,7 @@ class TestWorkspace(unittest.TestCase):
|
|
|
188
212
|
|
|
189
213
|
try:
|
|
190
214
|
cls.workspace = cls.workspace_group.create_workspace(
|
|
191
|
-
f'ws-test-{name}',
|
|
215
|
+
f'ws-test-{name}-x',
|
|
192
216
|
wait_on_active=True,
|
|
193
217
|
)
|
|
194
218
|
except Exception:
|
|
@@ -222,19 +246,63 @@ class TestWorkspace(unittest.TestCase):
|
|
|
222
246
|
def test_regions(self):
|
|
223
247
|
out = self.manager.regions
|
|
224
248
|
providers = {x.provider for x in out}
|
|
249
|
+
names = [x.name for x in out]
|
|
225
250
|
assert 'Azure' in providers, providers
|
|
226
251
|
assert 'GCP' in providers, providers
|
|
227
252
|
assert 'AWS' in providers, providers
|
|
228
253
|
|
|
254
|
+
objs = {}
|
|
255
|
+
ids = []
|
|
256
|
+
for item in out:
|
|
257
|
+
ids.append(item.id)
|
|
258
|
+
objs[item.id] = item
|
|
259
|
+
if item.name not in objs:
|
|
260
|
+
objs[item.name] = item
|
|
261
|
+
|
|
262
|
+
name = random.choice(names)
|
|
263
|
+
assert out[name] == objs[name]
|
|
264
|
+
id = random.choice(ids)
|
|
265
|
+
assert out[id] == objs[id]
|
|
266
|
+
|
|
229
267
|
def test_workspace_groups(self):
|
|
230
268
|
workspace_groups = self.manager.workspace_groups
|
|
231
269
|
ids = [x.id for x in workspace_groups]
|
|
232
|
-
|
|
270
|
+
names = [x.name for x in workspace_groups]
|
|
271
|
+
assert self.workspace_group.id in ids
|
|
272
|
+
assert self.workspace_group.name in names
|
|
273
|
+
|
|
274
|
+
assert workspace_groups.ids() == ids
|
|
275
|
+
assert workspace_groups.names() == names
|
|
276
|
+
|
|
277
|
+
objs = {}
|
|
278
|
+
for item in workspace_groups:
|
|
279
|
+
objs[item.id] = item
|
|
280
|
+
objs[item.name] = item
|
|
281
|
+
|
|
282
|
+
name = random.choice(names)
|
|
283
|
+
assert workspace_groups[name] == objs[name]
|
|
284
|
+
id = random.choice(ids)
|
|
285
|
+
assert workspace_groups[id] == objs[id]
|
|
233
286
|
|
|
234
287
|
def test_workspaces(self):
|
|
235
288
|
spaces = self.workspace_group.workspaces
|
|
236
289
|
ids = [x.id for x in spaces]
|
|
237
|
-
|
|
290
|
+
names = [x.name for x in spaces]
|
|
291
|
+
assert self.workspace.id in ids
|
|
292
|
+
assert self.workspace.name in names
|
|
293
|
+
|
|
294
|
+
assert spaces.ids() == ids
|
|
295
|
+
assert spaces.names() == names
|
|
296
|
+
|
|
297
|
+
objs = {}
|
|
298
|
+
for item in spaces:
|
|
299
|
+
objs[item.id] = item
|
|
300
|
+
objs[item.name] = item
|
|
301
|
+
|
|
302
|
+
name = random.choice(names)
|
|
303
|
+
assert spaces[name] == objs[name]
|
|
304
|
+
id = random.choice(ids)
|
|
305
|
+
assert spaces[id] == objs[id]
|
|
238
306
|
|
|
239
307
|
def test_get_workspace_group(self):
|
|
240
308
|
group = self.manager.get_workspace_group(self.workspace_group.id)
|
|
@@ -293,6 +361,518 @@ class TestWorkspace(unittest.TestCase):
|
|
|
293
361
|
assert 'endpoint' in cm.exception.msg, cm.exception.msg
|
|
294
362
|
|
|
295
363
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
364
|
+
@pytest.mark.management
|
|
365
|
+
class TestStage(unittest.TestCase):
|
|
366
|
+
|
|
367
|
+
manager = None
|
|
368
|
+
wg = None
|
|
369
|
+
password = None
|
|
370
|
+
|
|
371
|
+
@classmethod
|
|
372
|
+
def setUpClass(cls):
|
|
373
|
+
cls.manager = s2.manage_workspaces()
|
|
374
|
+
|
|
375
|
+
us_regions = [x for x in cls.manager.regions if 'US' in x.name]
|
|
376
|
+
cls.password = secrets.token_urlsafe(20)
|
|
377
|
+
|
|
378
|
+
name = clean_name(secrets.token_urlsafe(20)[:20])
|
|
379
|
+
|
|
380
|
+
cls.wg = cls.manager.create_workspace_group(
|
|
381
|
+
f'wg-test-{name}',
|
|
382
|
+
region=random.choice(us_regions).id,
|
|
383
|
+
admin_password=cls.password,
|
|
384
|
+
firewall_ranges=['0.0.0.0/0'],
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
@classmethod
|
|
388
|
+
def tearDownClass(cls):
|
|
389
|
+
if cls.wg is not None:
|
|
390
|
+
cls.wg.terminate(force=True)
|
|
391
|
+
cls.wg = None
|
|
392
|
+
cls.manager = None
|
|
393
|
+
cls.password = None
|
|
394
|
+
|
|
395
|
+
def test_upload_file(self):
|
|
396
|
+
st = self.wg.stage
|
|
397
|
+
|
|
398
|
+
root = st.info('/')
|
|
399
|
+
assert str(root.path) == '/'
|
|
400
|
+
assert root.type == 'directory'
|
|
401
|
+
|
|
402
|
+
# Upload file
|
|
403
|
+
f = st.upload_file(TEST_DIR / 'test.sql', 'upload_test.sql')
|
|
404
|
+
assert str(f.path) == 'upload_test.sql'
|
|
405
|
+
assert f.type == 'file'
|
|
406
|
+
|
|
407
|
+
# Download and compare to original
|
|
408
|
+
txt = f.download(encoding='utf-8')
|
|
409
|
+
assert txt == open(TEST_DIR / 'test.sql').read()
|
|
410
|
+
|
|
411
|
+
# Make sure we can't overwrite
|
|
412
|
+
with self.assertRaises(OSError):
|
|
413
|
+
st.upload_file(TEST_DIR / 'test.sql', 'upload_test.sql')
|
|
414
|
+
|
|
415
|
+
# Force overwrite with new content
|
|
416
|
+
f = st.upload_file(TEST_DIR / 'test2.sql', 'upload_test.sql', overwrite=True)
|
|
417
|
+
assert str(f.path) == 'upload_test.sql'
|
|
418
|
+
assert f.type == 'file'
|
|
419
|
+
|
|
420
|
+
# Verify new content
|
|
421
|
+
txt = f.download(encoding='utf-8')
|
|
422
|
+
assert txt == open(TEST_DIR / 'test2.sql').read()
|
|
423
|
+
|
|
424
|
+
# Try to upload folder
|
|
425
|
+
with self.assertRaises(IsADirectoryError):
|
|
426
|
+
st.upload_file(TEST_DIR, 'test3.sql')
|
|
427
|
+
|
|
428
|
+
lib = st.mkdir('/lib/')
|
|
429
|
+
assert str(lib.path) == 'lib/'
|
|
430
|
+
assert lib.type == 'directory'
|
|
431
|
+
|
|
432
|
+
# Try to overwrite stage folder with file
|
|
433
|
+
with self.assertRaises(IsADirectoryError):
|
|
434
|
+
st.upload_file(TEST_DIR / 'test2.sql', lib.path, overwrite=True)
|
|
435
|
+
|
|
436
|
+
# Write file into folder
|
|
437
|
+
f = st.upload_file(
|
|
438
|
+
TEST_DIR / 'test2.sql',
|
|
439
|
+
os.path.join(lib.path, 'upload_test2.sql'),
|
|
440
|
+
)
|
|
441
|
+
assert str(f.path) == 'lib/upload_test2.sql'
|
|
442
|
+
assert f.type == 'file'
|
|
443
|
+
|
|
444
|
+
def test_open(self):
|
|
445
|
+
st = self.wg.stage
|
|
446
|
+
|
|
447
|
+
# See if error is raised for non-existent file
|
|
448
|
+
with self.assertRaises(s2.ManagementError):
|
|
449
|
+
st.open('open_test.sql', 'r')
|
|
450
|
+
|
|
451
|
+
# Load test file
|
|
452
|
+
st.upload_file(TEST_DIR / 'test.sql', 'open_test.sql')
|
|
453
|
+
|
|
454
|
+
# Read file using `open`
|
|
455
|
+
with st.open('open_test.sql', 'r') as rfile:
|
|
456
|
+
assert rfile.read() == open(TEST_DIR / 'test.sql').read()
|
|
457
|
+
|
|
458
|
+
# Read file using `open` with 'rt' mode
|
|
459
|
+
with st.open('open_test.sql', 'rt') as rfile:
|
|
460
|
+
assert rfile.read() == open(TEST_DIR / 'test.sql').read()
|
|
461
|
+
|
|
462
|
+
# Read file using `open` with 'rb' mode
|
|
463
|
+
with st.open('open_test.sql', 'rb') as rfile:
|
|
464
|
+
assert rfile.read() == open(TEST_DIR / 'test.sql', 'rb').read()
|
|
465
|
+
|
|
466
|
+
# Read file using `open` with 'rb' mode
|
|
467
|
+
with self.assertRaises(ValueError):
|
|
468
|
+
with st.open('open_test.sql', 'b') as rfile:
|
|
469
|
+
pass
|
|
470
|
+
|
|
471
|
+
# Attempt overwrite file using `open` with mode 'x'
|
|
472
|
+
with self.assertRaises(OSError):
|
|
473
|
+
with st.open('open_test.sql', 'x') as wfile:
|
|
474
|
+
pass
|
|
475
|
+
|
|
476
|
+
# Attempt overwrite file using `open` with mode 'w'
|
|
477
|
+
with st.open('open_test.sql', 'w') as wfile:
|
|
478
|
+
wfile.write(open(TEST_DIR / 'test2.sql').read())
|
|
479
|
+
|
|
480
|
+
txt = st.download_file('open_test.sql', encoding='utf-8')
|
|
481
|
+
|
|
482
|
+
assert txt == open(TEST_DIR / 'test2.sql').read()
|
|
483
|
+
|
|
484
|
+
# Test writer without context manager
|
|
485
|
+
wfile = st.open('open_raw_test.sql', 'w')
|
|
486
|
+
for line in open(TEST_DIR / 'test.sql'):
|
|
487
|
+
wfile.write(line)
|
|
488
|
+
wfile.close()
|
|
489
|
+
|
|
490
|
+
txt = st.download_file('open_raw_test.sql', encoding='utf-8')
|
|
491
|
+
|
|
492
|
+
assert txt == open(TEST_DIR / 'test.sql').read()
|
|
493
|
+
|
|
494
|
+
# Test reader without context manager
|
|
495
|
+
rfile = st.open('open_raw_test.sql', 'r')
|
|
496
|
+
txt = ''
|
|
497
|
+
for line in rfile:
|
|
498
|
+
txt += line
|
|
499
|
+
rfile.close()
|
|
500
|
+
|
|
501
|
+
assert txt == open(TEST_DIR / 'test.sql').read()
|
|
502
|
+
|
|
503
|
+
def test_obj_open(self):
|
|
504
|
+
st = self.wg.stage
|
|
505
|
+
|
|
506
|
+
# Load test file
|
|
507
|
+
f = st.upload_file(TEST_DIR / 'test.sql', 'obj_open_test.sql')
|
|
508
|
+
|
|
509
|
+
# Read file using `open`
|
|
510
|
+
with f.open() as rfile:
|
|
511
|
+
assert rfile.read() == open(TEST_DIR / 'test.sql').read()
|
|
512
|
+
|
|
513
|
+
# Make sure directories error out
|
|
514
|
+
d = st.mkdir('obj_open_dir')
|
|
515
|
+
with self.assertRaises(IsADirectoryError):
|
|
516
|
+
d.open()
|
|
517
|
+
|
|
518
|
+
# Write file using `open`
|
|
519
|
+
with f.open('w', encoding='utf-8') as wfile:
|
|
520
|
+
wfile.write(open(TEST_DIR / 'test2.sql').read())
|
|
521
|
+
|
|
522
|
+
assert f.download(encoding='utf-8') == open(TEST_DIR / 'test2.sql').read()
|
|
523
|
+
|
|
524
|
+
# Test writer without context manager
|
|
525
|
+
wfile = f.open('w')
|
|
526
|
+
for line in open(TEST_DIR / 'test.sql'):
|
|
527
|
+
wfile.write(line)
|
|
528
|
+
wfile.close()
|
|
529
|
+
|
|
530
|
+
txt = st.download_file(f.path, encoding='utf-8')
|
|
531
|
+
|
|
532
|
+
assert txt == open(TEST_DIR / 'test.sql').read()
|
|
533
|
+
|
|
534
|
+
# Test reader without context manager
|
|
535
|
+
rfile = f.open('r')
|
|
536
|
+
txt = ''
|
|
537
|
+
for line in rfile:
|
|
538
|
+
txt += line
|
|
539
|
+
rfile.close()
|
|
540
|
+
|
|
541
|
+
assert txt == open(TEST_DIR / 'test.sql').read()
|
|
542
|
+
|
|
543
|
+
def test_os_directories(self):
|
|
544
|
+
st = self.wg.stage
|
|
545
|
+
|
|
546
|
+
# mkdir
|
|
547
|
+
st.mkdir('mkdir_test_1')
|
|
548
|
+
st.mkdir('mkdir_test_2')
|
|
549
|
+
with self.assertRaises(s2.ManagementError):
|
|
550
|
+
st.mkdir('mkdir_test_2/nest_1/nest_2')
|
|
551
|
+
st.mkdir('mkdir_test_2/nest_1')
|
|
552
|
+
st.mkdir('mkdir_test_2/nest_1/nest_2')
|
|
553
|
+
st.mkdir('mkdir_test_3')
|
|
554
|
+
|
|
555
|
+
assert st.exists('mkdir_test_1/')
|
|
556
|
+
assert st.exists('mkdir_test_2/')
|
|
557
|
+
assert st.exists('mkdir_test_2/nest_1/')
|
|
558
|
+
assert st.exists('mkdir_test_2/nest_1/nest_2/')
|
|
559
|
+
assert not st.exists('foo/')
|
|
560
|
+
assert not st.exists('foo/bar/')
|
|
561
|
+
|
|
562
|
+
assert st.is_dir('mkdir_test_1/')
|
|
563
|
+
assert st.is_dir('mkdir_test_2/')
|
|
564
|
+
assert st.is_dir('mkdir_test_2/nest_1/')
|
|
565
|
+
assert st.is_dir('mkdir_test_2/nest_1/nest_2/')
|
|
566
|
+
|
|
567
|
+
assert not st.is_file('mkdir_test_1/')
|
|
568
|
+
assert not st.is_file('mkdir_test_2/')
|
|
569
|
+
assert not st.is_file('mkdir_test_2/nest_1/')
|
|
570
|
+
assert not st.is_file('mkdir_test_2/nest_1/nest_2/')
|
|
571
|
+
|
|
572
|
+
out = st.listdir('/')
|
|
573
|
+
assert 'mkdir_test_1/' in out
|
|
574
|
+
assert 'mkdir_test_2/' in out
|
|
575
|
+
assert 'mkdir_test_2/nest_1/nest_2/' not in out
|
|
576
|
+
|
|
577
|
+
out = st.listdir('/', recursive=True)
|
|
578
|
+
assert 'mkdir_test_1/' in out
|
|
579
|
+
assert 'mkdir_test_2/' in out
|
|
580
|
+
assert 'mkdir_test_2/nest_1/nest_2/' in out
|
|
581
|
+
|
|
582
|
+
out = st.listdir('mkdir_test_2')
|
|
583
|
+
assert 'mkdir_test_1/' not in out
|
|
584
|
+
assert 'nest_1/' in out
|
|
585
|
+
assert 'nest_2/' not in out
|
|
586
|
+
assert 'nest_1/nest_2/' not in out
|
|
587
|
+
|
|
588
|
+
out = st.listdir('mkdir_test_2', recursive=True)
|
|
589
|
+
assert 'mkdir_test_1/' not in out
|
|
590
|
+
assert 'nest_1/' in out
|
|
591
|
+
assert 'nest_2/' not in out
|
|
592
|
+
assert 'nest_1/nest_2/' in out
|
|
593
|
+
|
|
594
|
+
# rmdir
|
|
595
|
+
before = st.listdir('/', recursive=True)
|
|
596
|
+
st.rmdir('mkdir_test_1/')
|
|
597
|
+
after = st.listdir('/', recursive=True)
|
|
598
|
+
assert 'mkdir_test_1/' in before
|
|
599
|
+
assert 'mkdir_test_1/' not in after
|
|
600
|
+
assert list(sorted(before)) == list(sorted(after + ['mkdir_test_1/']))
|
|
601
|
+
|
|
602
|
+
with self.assertRaises(OSError):
|
|
603
|
+
st.rmdir('mkdir_test_2/')
|
|
604
|
+
|
|
605
|
+
st.upload_file(TEST_DIR / 'test.sql', 'mkdir_test.sql')
|
|
606
|
+
|
|
607
|
+
with self.assertRaises(NotADirectoryError):
|
|
608
|
+
st.rmdir('mkdir_test.sql')
|
|
609
|
+
|
|
610
|
+
# removedirs
|
|
611
|
+
before = st.listdir('/')
|
|
612
|
+
st.removedirs('mkdir_test_2/')
|
|
613
|
+
after = st.listdir('/')
|
|
614
|
+
assert 'mkdir_test_2/' in before
|
|
615
|
+
assert 'mkdir_test_2/' not in after
|
|
616
|
+
assert list(sorted(before)) == list(sorted(after + ['mkdir_test_2/']))
|
|
617
|
+
|
|
618
|
+
with self.assertRaises(s2.ManagementError):
|
|
619
|
+
st.removedirs('mkdir_test.sql')
|
|
620
|
+
|
|
621
|
+
def test_os_files(self):
|
|
622
|
+
st = self.wg.stage
|
|
623
|
+
|
|
624
|
+
st.mkdir('files_test_1')
|
|
625
|
+
st.mkdir('files_test_1/nest_1')
|
|
626
|
+
|
|
627
|
+
st.upload_file(TEST_DIR / 'test.sql', 'files_test.sql')
|
|
628
|
+
st.upload_file(TEST_DIR / 'test.sql', 'files_test_1/nest_1/nested_files_test.sql')
|
|
629
|
+
st.upload_file(
|
|
630
|
+
TEST_DIR / 'test.sql',
|
|
631
|
+
'files_test_1/nest_1/nested_files_test_2.sql',
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
# remove
|
|
635
|
+
with self.assertRaises(IsADirectoryError):
|
|
636
|
+
st.remove('files_test_1/')
|
|
637
|
+
|
|
638
|
+
before = st.listdir('/')
|
|
639
|
+
st.remove('files_test.sql')
|
|
640
|
+
after = st.listdir('/')
|
|
641
|
+
assert 'files_test.sql' in before
|
|
642
|
+
assert 'files_test.sql' not in after
|
|
643
|
+
assert list(sorted(before)) == list(sorted(after + ['files_test.sql']))
|
|
644
|
+
|
|
645
|
+
before = st.listdir('files_test_1/nest_1/')
|
|
646
|
+
st.remove('files_test_1/nest_1/nested_files_test.sql')
|
|
647
|
+
after = st.listdir('files_test_1/nest_1/')
|
|
648
|
+
assert 'nested_files_test.sql' in before
|
|
649
|
+
assert 'nested_files_test.sql' not in after
|
|
650
|
+
assert st.is_dir('files_test_1/nest_1/')
|
|
651
|
+
|
|
652
|
+
# Removing the last file does not remove empty directories
|
|
653
|
+
st.remove('files_test_1/nest_1/nested_files_test_2.sql')
|
|
654
|
+
assert not st.is_file('files_test_1/nest_1/nested_files_test_2.sql')
|
|
655
|
+
assert st.is_dir('files_test_1/nest_1/')
|
|
656
|
+
assert st.is_dir('files_test_1/')
|
|
657
|
+
|
|
658
|
+
st.removedirs('files_test_1')
|
|
659
|
+
assert not st.is_dir('files_test_1/nest_1/')
|
|
660
|
+
assert not st.is_dir('files_test_1/')
|
|
661
|
+
|
|
662
|
+
def test_os_rename(self):
|
|
663
|
+
st = self.wg.stage
|
|
664
|
+
|
|
665
|
+
st.upload_file(TEST_DIR / 'test.sql', 'rename_test.sql')
|
|
666
|
+
|
|
667
|
+
with self.assertRaises(s2.ManagementError):
|
|
668
|
+
st.upload_file(
|
|
669
|
+
TEST_DIR / 'test.sql',
|
|
670
|
+
'rename_test_1/nest_1/nested_rename_test.sql',
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
st.mkdir('rename_test_1')
|
|
674
|
+
st.mkdir('rename_test_1/nest_1')
|
|
675
|
+
|
|
676
|
+
assert st.exists('/rename_test_1/nest_1/')
|
|
677
|
+
|
|
678
|
+
st.upload_file(
|
|
679
|
+
TEST_DIR / 'test.sql',
|
|
680
|
+
'rename_test_1/nest_1/nested_rename_test.sql',
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
st.upload_file(
|
|
684
|
+
TEST_DIR / 'test.sql',
|
|
685
|
+
'rename_test_1/nest_1/nested_rename_test_2.sql',
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
# rename file
|
|
689
|
+
assert 'rename_test.sql' in st.listdir('/')
|
|
690
|
+
assert 'rename_test_2.sql' not in st.listdir('/')
|
|
691
|
+
st.rename('rename_test.sql', 'rename_test_2.sql')
|
|
692
|
+
assert 'rename_test.sql' not in st.listdir('/')
|
|
693
|
+
assert 'rename_test_2.sql' in st.listdir('/')
|
|
694
|
+
|
|
695
|
+
# rename directory
|
|
696
|
+
assert 'rename_test_1/' in st.listdir('/')
|
|
697
|
+
assert 'rename_test_2/' not in st.listdir('/')
|
|
698
|
+
st.rename('rename_test_1/', 'rename_test_2/')
|
|
699
|
+
assert 'rename_test_1/' not in st.listdir('/')
|
|
700
|
+
assert 'rename_test_2/' in st.listdir('/')
|
|
701
|
+
assert st.is_file('rename_test_2/nest_1/nested_rename_test.sql')
|
|
702
|
+
assert st.is_file('rename_test_2/nest_1/nested_rename_test_2.sql')
|
|
703
|
+
|
|
704
|
+
# rename nested
|
|
705
|
+
assert 'rename_test_2/nest_1/nested_rename_test.sql' in st.listdir(
|
|
706
|
+
'/', recursive=True,
|
|
707
|
+
)
|
|
708
|
+
assert 'rename_test_2/nest_1/nested_rename_test_3.sql' not in st.listdir(
|
|
709
|
+
'/', recursive=True,
|
|
710
|
+
)
|
|
711
|
+
st.rename(
|
|
712
|
+
'rename_test_2/nest_1/nested_rename_test.sql',
|
|
713
|
+
'rename_test_2/nest_1/nested_rename_test_3.sql',
|
|
714
|
+
)
|
|
715
|
+
assert 'rename_test_2/nest_1/nested_rename_test.sql' not in st.listdir(
|
|
716
|
+
'/', recursive=True,
|
|
717
|
+
)
|
|
718
|
+
assert 'rename_test_2/nest_1/nested_rename_test_3.sql' in st.listdir(
|
|
719
|
+
'/', recursive=True,
|
|
720
|
+
)
|
|
721
|
+
assert not st.is_file('rename_test_2/nest_1/nested_rename_test.sql')
|
|
722
|
+
assert st.is_file('rename_test_2/nest_1/nested_rename_test_2.sql')
|
|
723
|
+
assert st.is_file('rename_test_2/nest_1/nested_rename_test_3.sql')
|
|
724
|
+
|
|
725
|
+
# non-existent file
|
|
726
|
+
with self.assertRaises(OSError):
|
|
727
|
+
st.rename('rename_foo.sql', 'rename_foo_2.sql')
|
|
728
|
+
|
|
729
|
+
# overwrite
|
|
730
|
+
with self.assertRaises(OSError):
|
|
731
|
+
st.rename(
|
|
732
|
+
'rename_test_2.sql',
|
|
733
|
+
'rename_test_2/nest_1/nested_rename_test_3.sql',
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
st.rename(
|
|
737
|
+
'rename_test_2.sql',
|
|
738
|
+
'rename_test_2/nest_1/nested_rename_test_3.sql', overwrite=True,
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
def test_stage_object(self):
|
|
742
|
+
st = self.wg.stage
|
|
743
|
+
|
|
744
|
+
st.mkdir('obj_test')
|
|
745
|
+
st.mkdir('obj_test/nest_1')
|
|
746
|
+
|
|
747
|
+
f1 = st.upload_file(TEST_DIR / 'test.sql', 'obj_test.sql')
|
|
748
|
+
f2 = st.upload_file(TEST_DIR / 'test.sql', 'obj_test/nest_1/obj_test.sql')
|
|
749
|
+
d2 = st.info('obj_test/nest_1/')
|
|
750
|
+
|
|
751
|
+
# is_file / is_dir
|
|
752
|
+
assert not f1.is_dir()
|
|
753
|
+
assert f1.is_file()
|
|
754
|
+
assert not f2.is_dir()
|
|
755
|
+
assert f2.is_file()
|
|
756
|
+
assert d2.is_dir()
|
|
757
|
+
assert not d2.is_file()
|
|
758
|
+
|
|
759
|
+
# abspath / basename / dirname / exists
|
|
760
|
+
assert f1.abspath() == 'obj_test.sql'
|
|
761
|
+
assert f1.basename() == 'obj_test.sql'
|
|
762
|
+
assert f1.dirname() == '/'
|
|
763
|
+
assert f1.exists()
|
|
764
|
+
assert f2.abspath() == 'obj_test/nest_1/obj_test.sql'
|
|
765
|
+
assert f2.basename() == 'obj_test.sql'
|
|
766
|
+
assert f2.dirname() == 'obj_test/nest_1/'
|
|
767
|
+
assert f2.exists()
|
|
768
|
+
assert d2.abspath() == 'obj_test/nest_1/'
|
|
769
|
+
assert d2.basename() == 'nest_1'
|
|
770
|
+
assert d2.dirname() == 'obj_test/'
|
|
771
|
+
assert d2.exists()
|
|
772
|
+
|
|
773
|
+
# download
|
|
774
|
+
assert f1.download(encoding='utf-8') == open(TEST_DIR / 'test.sql', 'r').read()
|
|
775
|
+
assert f1.download() == open(TEST_DIR / 'test.sql', 'rb').read()
|
|
776
|
+
|
|
777
|
+
# remove
|
|
778
|
+
with self.assertRaises(IsADirectoryError):
|
|
779
|
+
d2.remove()
|
|
780
|
+
|
|
781
|
+
assert st.is_file('obj_test.sql')
|
|
782
|
+
f1.remove()
|
|
783
|
+
assert not st.is_file('obj_test.sql')
|
|
784
|
+
|
|
785
|
+
# removedirs
|
|
786
|
+
with self.assertRaises(NotADirectoryError):
|
|
787
|
+
f2.removedirs()
|
|
788
|
+
|
|
789
|
+
assert st.exists(d2.path)
|
|
790
|
+
d2.removedirs()
|
|
791
|
+
assert not st.exists(d2.path)
|
|
792
|
+
|
|
793
|
+
# rmdir
|
|
794
|
+
f1 = st.upload_file(TEST_DIR / 'test.sql', 'obj_test.sql')
|
|
795
|
+
d2 = st.mkdir('obj_test/nest_1')
|
|
796
|
+
|
|
797
|
+
assert st.exists(f1.path)
|
|
798
|
+
assert st.exists(d2.path)
|
|
799
|
+
|
|
800
|
+
with self.assertRaises(NotADirectoryError):
|
|
801
|
+
f1.rmdir()
|
|
802
|
+
|
|
803
|
+
assert st.exists(f1.path)
|
|
804
|
+
assert st.exists(d2.path)
|
|
805
|
+
|
|
806
|
+
d2.rmdir()
|
|
807
|
+
|
|
808
|
+
assert not st.exists('obj_test/nest_1/')
|
|
809
|
+
assert not st.exists('obj_test')
|
|
810
|
+
|
|
811
|
+
# mtime / ctime
|
|
812
|
+
assert f1.getmtime() > 0
|
|
813
|
+
assert f1.getctime() > 0
|
|
814
|
+
|
|
815
|
+
# rename
|
|
816
|
+
assert st.exists('obj_test.sql')
|
|
817
|
+
assert not st.exists('obj_test_2.sql')
|
|
818
|
+
f1.rename('obj_test_2.sql')
|
|
819
|
+
assert not st.exists('obj_test.sql')
|
|
820
|
+
assert st.exists('obj_test_2.sql')
|
|
821
|
+
assert f1.abspath() == 'obj_test_2.sql'
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
@pytest.mark.management
|
|
825
|
+
class TestSecrets(unittest.TestCase):
|
|
826
|
+
|
|
827
|
+
manager = None
|
|
828
|
+
wg = None
|
|
829
|
+
password = None
|
|
830
|
+
|
|
831
|
+
@classmethod
|
|
832
|
+
def setUpClass(cls):
|
|
833
|
+
cls.manager = s2.manage_workspaces()
|
|
834
|
+
|
|
835
|
+
us_regions = [x for x in cls.manager.regions if 'US' in x.name]
|
|
836
|
+
cls.password = secrets.token_urlsafe(20)
|
|
837
|
+
|
|
838
|
+
name = clean_name(secrets.token_urlsafe(20)[:20])
|
|
839
|
+
|
|
840
|
+
cls.wg = cls.manager.create_workspace_group(
|
|
841
|
+
f'wg-test-{name}',
|
|
842
|
+
region=random.choice(us_regions).id,
|
|
843
|
+
admin_password=cls.password,
|
|
844
|
+
firewall_ranges=['0.0.0.0/0'],
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
@classmethod
|
|
848
|
+
def tearDownClass(cls):
|
|
849
|
+
if cls.wg is not None:
|
|
850
|
+
cls.wg.terminate(force=True)
|
|
851
|
+
cls.wg = None
|
|
852
|
+
cls.manager = None
|
|
853
|
+
cls.password = None
|
|
854
|
+
|
|
855
|
+
def test_get_secret(self):
|
|
856
|
+
# manually create secret and then get secret
|
|
857
|
+
# try to delete the secret if it exists
|
|
858
|
+
try:
|
|
859
|
+
secret = self.manager.organizations.current.get_secret('secret_name')
|
|
860
|
+
|
|
861
|
+
secret_id = secret.id
|
|
862
|
+
|
|
863
|
+
self.manager._delete(f'secrets/{secret_id}')
|
|
864
|
+
except s2.ManagementError:
|
|
865
|
+
pass
|
|
866
|
+
|
|
867
|
+
self.manager._post(
|
|
868
|
+
'secrets',
|
|
869
|
+
json=dict(
|
|
870
|
+
name='secret_name',
|
|
871
|
+
value='secret_value',
|
|
872
|
+
),
|
|
873
|
+
)
|
|
874
|
+
|
|
875
|
+
secret = self.manager.organizations.current.get_secret('secret_name')
|
|
876
|
+
|
|
877
|
+
assert secret.name == 'secret_name'
|
|
878
|
+
assert secret.value == 'secret_value'
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# type: ignore
|
|
3
|
+
"""SingleStoreDB Pytest Plugin testing
|
|
4
|
+
|
|
5
|
+
Each of these tests performs the same simple operation which
|
|
6
|
+
would fail if any other test had been run on the same database.
|
|
7
|
+
"""
|
|
8
|
+
from singlestoredb.connection import Cursor
|
|
9
|
+
|
|
10
|
+
# pytest_plugins = ('singlestoredb.pytest',)
|
|
11
|
+
|
|
12
|
+
CREATE_TABLE_STATEMENT = 'create table test_dict (a text)'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_tempdb1(singlestoredb_tempdb: Cursor):
|
|
16
|
+
# alias the fixture
|
|
17
|
+
cursor = singlestoredb_tempdb
|
|
18
|
+
|
|
19
|
+
cursor.execute(CREATE_TABLE_STATEMENT)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_tempdb2(singlestoredb_tempdb: Cursor):
|
|
23
|
+
# alias the fixture
|
|
24
|
+
cursor = singlestoredb_tempdb
|
|
25
|
+
|
|
26
|
+
cursor.execute(CREATE_TABLE_STATEMENT)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_tempdb3(singlestoredb_tempdb: Cursor):
|
|
30
|
+
# alias the fixture
|
|
31
|
+
cursor = singlestoredb_tempdb
|
|
32
|
+
|
|
33
|
+
cursor.execute(CREATE_TABLE_STATEMENT)
|