Skip to content

Commit 63c0263

Browse files
committed
new tests and connection fixes
1 parent 72ec659 commit 63c0263

File tree

6 files changed

+365
-55
lines changed

6 files changed

+365
-55
lines changed

.github/workflows/lint.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Lint
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
13+
concurrency:
14+
group: unit-${{ github.ref }}-${{ matrix.python-version }}-${{ matrix.poetry-version }}
15+
cancel-in-progress: true
16+
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
python-version: [3.9]
21+
poetry-version: [1.8]
22+
23+
steps:
24+
- uses: actions/checkout@v3
25+
with:
26+
persist-credentials: false
27+
fetch-depth: 0
28+
29+
- name: Set up Python ${{ matrix.python-version }}
30+
uses: actions/setup-python@v4
31+
with:
32+
python-version: ${{ matrix.python-version }}
33+
34+
- name: Install Poetry ${{ matrix.poetry-version }}
35+
run: |
36+
pip install "poetry~=${{ matrix.poetry-version }}.0"
37+
38+
# Ensure that Poetry is not upgraded past the version we are testing
39+
poetry add "poetry@~${{ matrix.poetry-version }}" --lock
40+
41+
- name: Install packages
42+
run: |
43+
poetry install
44+
45+
- name: Run tests
46+
run: |
47+
poetry run poe lint

tests/test_connection.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
from contextlib import suppress
2+
3+
import pytest
4+
import pytest_asyncio
5+
import ydb
6+
7+
import ydb_dbapi as dbapi
8+
9+
10+
class BaseDBApiTestSuit:
11+
async def _test_isolation_level_read_only(
12+
self,
13+
connection: dbapi.Connection,
14+
isolation_level: str,
15+
read_only: bool,
16+
):
17+
await connection.cursor().execute(
18+
"CREATE TABLE foo(id Int64 NOT NULL, PRIMARY KEY (id))"
19+
)
20+
connection.set_isolation_level(isolation_level)
21+
22+
cursor = connection.cursor()
23+
24+
await connection.begin()
25+
26+
query = "UPSERT INTO foo(id) VALUES (1)"
27+
if read_only:
28+
with pytest.raises(dbapi.DatabaseError):
29+
await cursor.execute(query)
30+
else:
31+
await cursor.execute(query)
32+
33+
await connection.rollback()
34+
35+
await connection.cursor().execute("DROP TABLE foo")
36+
await connection.cursor().close()
37+
38+
async def _test_connection(self, connection: dbapi.Connection):
39+
await connection.commit()
40+
await connection.rollback()
41+
42+
cur = connection.cursor()
43+
with suppress(dbapi.DatabaseError):
44+
await cur.execute("DROP TABLE foo")
45+
46+
assert not await connection.check_exists("/local/foo")
47+
with pytest.raises(dbapi.ProgrammingError):
48+
await connection.describe("/local/foo")
49+
50+
await cur.execute(
51+
"CREATE TABLE foo(id Int64 NOT NULL, PRIMARY KEY (id))"
52+
)
53+
54+
assert await connection.check_exists("/local/foo")
55+
56+
col = (await connection.describe("/local/foo")).columns[0]
57+
assert col.name == "id"
58+
assert col.type == ydb.PrimitiveType.Int64
59+
60+
await cur.execute("DROP TABLE foo")
61+
await cur.close()
62+
63+
async def _test_cursor_raw_query(self, connection: dbapi.Connection):
64+
cur = connection.cursor()
65+
assert cur
66+
67+
with suppress(dbapi.DatabaseError):
68+
await cur.execute("DROP TABLE test")
69+
70+
await cur.execute(
71+
"CREATE TABLE test(id Int64 NOT NULL, text Utf8, PRIMARY KEY (id))"
72+
)
73+
74+
await cur.execute(
75+
"""
76+
DECLARE $data AS List<Struct<id:Int64, text: Utf8>>;
77+
78+
INSERT INTO test SELECT id, text FROM AS_TABLE($data);
79+
""",
80+
{
81+
"$data": ydb.TypedValue(
82+
[
83+
{"id": 17, "text": "seventeen"},
84+
{"id": 21, "text": "twenty one"},
85+
],
86+
ydb.ListType(
87+
ydb.StructType()
88+
.add_member("id", ydb.PrimitiveType.Int64)
89+
.add_member("text", ydb.PrimitiveType.Utf8)
90+
),
91+
)
92+
},
93+
)
94+
95+
await cur.execute("DROP TABLE test")
96+
97+
await cur.close()
98+
99+
async def _test_errors(self, connection: dbapi.Connection):
100+
with pytest.raises(dbapi.InterfaceError):
101+
await dbapi.connect("localhost:2136", database="/local666")
102+
103+
cur = connection.cursor()
104+
105+
with suppress(dbapi.DatabaseError):
106+
await cur.execute("DROP TABLE test")
107+
108+
with pytest.raises(dbapi.DataError):
109+
await cur.execute("SELECT 18446744073709551616")
110+
111+
with pytest.raises(dbapi.DataError):
112+
await cur.execute("SELECT * FROM 拉屎")
113+
114+
with pytest.raises(dbapi.DataError):
115+
await cur.execute("SELECT floor(5 / 2)")
116+
117+
with pytest.raises(dbapi.ProgrammingError):
118+
await cur.execute("SELECT * FROM test")
119+
120+
await cur.execute("CREATE TABLE test(id Int64, PRIMARY KEY (id))")
121+
122+
await cur.execute("INSERT INTO test(id) VALUES(1)")
123+
with pytest.raises(dbapi.IntegrityError):
124+
await cur.execute("INSERT INTO test(id) VALUES(1)")
125+
126+
await cur.execute("DROP TABLE test")
127+
await cur.close()
128+
129+
130+
class TestAsyncConnection(BaseDBApiTestSuit):
131+
@pytest_asyncio.fixture
132+
async def connection(self, endpoint, database):
133+
host, port = endpoint.split(":")
134+
conn = await dbapi.connect(host=host, port=port, database=database)
135+
try:
136+
yield conn
137+
finally:
138+
await conn.close()
139+
140+
@pytest.mark.asyncio
141+
@pytest.mark.parametrize(
142+
"isolation_level, read_only",
143+
[
144+
(dbapi.IsolationLevel.SERIALIZABLE, False),
145+
(dbapi.IsolationLevel.AUTOCOMMIT, False),
146+
# (dbapi.IsolationLevel.ONLINE_READONLY, True),
147+
# (dbapi.IsolationLevel.ONLINE_READONLY_INCONSISTENT, True),
148+
# (dbapi.IsolationLevel.STALE_READONLY, True),
149+
# (dbapi.IsolationLevel.SNAPSHOT_READONLY, True),
150+
],
151+
)
152+
async def test_isolation_level_read_only(
153+
self,
154+
isolation_level: str,
155+
read_only: bool,
156+
connection: dbapi.Connection,
157+
):
158+
await self._test_isolation_level_read_only(
159+
connection, isolation_level, read_only
160+
)
161+
162+
@pytest.mark.asyncio
163+
async def test_connection(self, connection: dbapi.Connection):
164+
await self._test_connection(connection)
165+
166+
@pytest.mark.asyncio
167+
async def test_cursor_raw_query(self, connection: dbapi.Connection):
168+
await self._test_cursor_raw_query(connection)
169+
170+
@pytest.mark.asyncio
171+
async def test_errors(self, connection: dbapi.Connection):
172+
await self._test_errors(connection)

tests/test_cursor.py

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import pytest
22
import ydb_dbapi
3-
import ydb_dbapi.cursors
43

54

65
@pytest.mark.asyncio
76
async def test_cursor_ddl(session_pool):
8-
cursor = ydb_dbapi.cursors.Cursor(session_pool=session_pool)
7+
cursor = ydb_dbapi.Cursor(session_pool=session_pool)
98

109
yql = """
1110
CREATE TABLE table (
@@ -29,7 +28,7 @@ async def test_cursor_ddl(session_pool):
2928

3029
@pytest.mark.asyncio
3130
async def test_cursor_dml(session_pool):
32-
cursor = ydb_dbapi.cursors.Cursor(session_pool=session_pool)
31+
cursor = ydb_dbapi.Cursor(session_pool=session_pool)
3332
yql_text = """
3433
INSERT INTO table (id, val) VALUES
3534
(1, 1),
@@ -40,7 +39,7 @@ async def test_cursor_dml(session_pool):
4039
await cursor.execute(query=yql_text)
4140
assert await cursor.fetchone() is None
4241

43-
cursor = ydb_dbapi.cursors.Cursor(session_pool=session_pool)
42+
cursor = ydb_dbapi.Cursor(session_pool=session_pool)
4443

4544
yql_text = """
4645
SELECT COUNT(*) FROM table as sum
@@ -55,7 +54,7 @@ async def test_cursor_dml(session_pool):
5554

5655
@pytest.mark.asyncio
5756
async def test_cursor_fetch_one(session_pool):
58-
cursor = ydb_dbapi.cursors.Cursor(session_pool=session_pool)
57+
cursor = ydb_dbapi.Cursor(session_pool=session_pool)
5958
yql_text = """
6059
INSERT INTO table (id, val) VALUES
6160
(1, 1),
@@ -65,7 +64,7 @@ async def test_cursor_fetch_one(session_pool):
6564
await cursor.execute(query=yql_text)
6665
assert await cursor.fetchone() is None
6766

68-
cursor = ydb_dbapi.cursors.Cursor(session_pool=session_pool)
67+
cursor = ydb_dbapi.Cursor(session_pool=session_pool)
6968

7069
yql_text = """
7170
SELECT id, val FROM table
@@ -84,7 +83,7 @@ async def test_cursor_fetch_one(session_pool):
8483

8584
@pytest.mark.asyncio
8685
async def test_cursor_fetch_many(session_pool):
87-
cursor = ydb_dbapi.cursors.Cursor(session_pool=session_pool)
86+
cursor = ydb_dbapi.Cursor(session_pool=session_pool)
8887
yql_text = """
8988
INSERT INTO table (id, val) VALUES
9089
(1, 1),
@@ -96,7 +95,7 @@ async def test_cursor_fetch_many(session_pool):
9695
await cursor.execute(query=yql_text)
9796
assert await cursor.fetchone() is None
9897

99-
cursor = ydb_dbapi.cursors.Cursor(session_pool=session_pool)
98+
cursor = ydb_dbapi.Cursor(session_pool=session_pool)
10099

101100
yql_text = """
102101
SELECT id, val FROM table
@@ -122,4 +121,58 @@ async def test_cursor_fetch_many(session_pool):
122121

123122
@pytest.mark.asyncio
124123
async def test_cursor_fetch_all(session_pool):
125-
pass
124+
cursor = ydb_dbapi.Cursor(session_pool=session_pool)
125+
yql_text = """
126+
INSERT INTO table (id, val) VALUES
127+
(1, 1),
128+
(2, 2),
129+
(3, 3)
130+
"""
131+
132+
await cursor.execute(query=yql_text)
133+
assert await cursor.fetchone() is None
134+
135+
cursor = ydb_dbapi.Cursor(session_pool=session_pool)
136+
137+
yql_text = """
138+
SELECT id, val FROM table
139+
"""
140+
141+
await cursor.execute(query=yql_text)
142+
143+
assert cursor.rowcount == 3
144+
145+
res = await cursor.fetchall()
146+
assert len(res) == 3
147+
assert res[0][0] == 1
148+
assert res[1][0] == 2
149+
assert res[2][0] == 3
150+
151+
assert await cursor.fetchall() is None
152+
153+
154+
@pytest.mark.asyncio
155+
async def test_cursor_next_set(session_pool):
156+
cursor = ydb_dbapi.Cursor(session_pool=session_pool)
157+
yql_text = """SELECT 1 as val; SELECT 2 as val;"""
158+
159+
await cursor.execute(query=yql_text)
160+
161+
res = await cursor.fetchall()
162+
assert len(res) == 1
163+
assert res[0][0] == 1
164+
165+
nextset = await cursor.nextset()
166+
assert nextset
167+
168+
res = await cursor.fetchall()
169+
assert len(res) == 1
170+
assert res[0][0] == 2
171+
172+
nextset = await cursor.nextset()
173+
assert nextset
174+
175+
assert await cursor.fetchall() is None
176+
177+
nextset = await cursor.nextset()
178+
assert not nextset

ydb_dbapi/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
from .errors import * # noqa
2+
from .connection import Connection, IsolationLevel, connect # noqa
3+
from .cursors import Cursor # noqa

0 commit comments

Comments
 (0)