Skip to content

Commit 79695c2

Browse files
test: fix integer compliance tests (#20)
* fix: integer complaince tests * fix: add docstring for tests * fix: change docstring to be consistent * test: add todo note to remove override method * test: nit
1 parent 7db3af4 commit 79695c2

File tree

2 files changed

+96
-6
lines changed

2 files changed

+96
-6
lines changed

google/cloud/sqlalchemy_spanner/sqlalchemy_spanner.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ def visit_BOOLEAN(self, type_, **kw):
176176
def visit_DATETIME(self, type_, **kw):
177177
return "TIMESTAMP"
178178

179+
def visit_BIGINT(self, type_, **kw):
180+
return "INT64"
181+
179182

180183
class SpannerDialect(DefaultDialect):
181184
"""Cloud Spanner dialect.

test/test_suite.py

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,20 @@
1616

1717
import pytest
1818

19-
from sqlalchemy.testing import config
19+
from sqlalchemy.testing import config, db
2020
from sqlalchemy.testing import eq_
2121
from sqlalchemy.testing import provide_metadata
2222
from sqlalchemy.testing.schema import Column
2323
from sqlalchemy.testing.schema import Table
2424
from sqlalchemy import literal_column
25-
from sqlalchemy import select, case, bindparam
25+
26+
from sqlalchemy import bindparam, case, literal, select, util
2627
from sqlalchemy import exists
2728
from sqlalchemy import Boolean
2829
from sqlalchemy import String
29-
from sqlalchemy.testing import requires
3030
from sqlalchemy.types import Integer
31+
from sqlalchemy.testing import requires
32+
3133
from google.api_core.datetime_helpers import DatetimeWithNanoseconds
3234

3335
from sqlalchemy.testing.suite.test_ddl import * # noqa: F401, F403
@@ -44,9 +46,7 @@
4446
from sqlalchemy.testing.suite.test_dialect import EscapingTest as _EscapingTest
4547
from sqlalchemy.testing.suite.test_select import ExistsTest as _ExistsTest
4648
from sqlalchemy.testing.suite.test_types import BooleanTest as _BooleanTest
47-
48-
config.test_schema = ""
49-
49+
from sqlalchemy.testing.suite.test_types import IntegerTest as _IntegerTest
5050

5151
from sqlalchemy.testing.suite.test_types import ( # noqa: F401, F403
5252
DateTest as _DateTest,
@@ -59,6 +59,8 @@
5959
TimestampMicrosecondsTest,
6060
)
6161

62+
config.test_schema = ""
63+
6264

6365
class EscapingTest(_EscapingTest):
6466
@provide_metadata
@@ -422,3 +424,88 @@ class TimeTests(_TimeMicrosecondsTest, _TimeTest):
422424
@pytest.mark.skip("Spanner doesn't coerce dates from datetime.")
423425
class DateTimeCoercedToDateTimeTest(_DateTimeCoercedToDateTimeTest):
424426
pass
427+
428+
429+
class IntegerTest(_IntegerTest):
430+
@provide_metadata
431+
def _round_trip(self, datatype, data):
432+
"""
433+
SPANNER OVERRIDE:
434+
435+
This is the helper method for integer class tests which creates a table and
436+
performs an insert operation.
437+
Cloud Spanner supports tables with an empty primary key, but only one
438+
row can be inserted into such a table - following insertions will fail with
439+
`400 id must not be NULL in table date_table`.
440+
Overriding the tests and adding a manual primary key value to avoid the same
441+
failures and deleting the table at the end.
442+
"""
443+
metadata = self.metadata
444+
int_table = Table(
445+
"integer_table",
446+
metadata,
447+
Column("id", Integer, primary_key=True, test_needs_autoincrement=True),
448+
Column("integer_data", datatype),
449+
)
450+
451+
metadata.create_all(config.db)
452+
453+
config.db.execute(int_table.insert(), {"id": 1, "integer_data": data})
454+
455+
row = config.db.execute(select([int_table.c.integer_data])).first()
456+
457+
eq_(row, (data,))
458+
459+
if util.py3k:
460+
assert isinstance(row[0], int)
461+
else:
462+
assert isinstance(row[0], (long, int)) # noqa
463+
464+
config.db.execute(int_table.delete())
465+
466+
@provide_metadata
467+
def _literal_round_trip(self, type_, input_, output, filter_=None):
468+
"""
469+
SPANNER OVERRIDE:
470+
471+
Spanner DBAPI does not execute DDL statements unless followed by a
472+
non DDL statement, which is preventing correct table clean up.
473+
The table already exists after related tests finish, so it doesn't
474+
create a new table and when running tests for other data types
475+
insertions will fail with `400 Duplicate name in schema: t`.
476+
Overriding the tests to create and drop a new table to prevent
477+
database existence errors.
478+
"""
479+
480+
# for literal, we test the literal render in an INSERT
481+
# into a typed column. we can then SELECT it back as its
482+
# official type; ideally we'd be able to use CAST here
483+
# but MySQL in particular can't CAST fully
484+
t = Table("int_t", self.metadata, Column("x", type_))
485+
t.create()
486+
487+
with db.connect() as conn:
488+
for value in input_:
489+
ins = (
490+
t.insert()
491+
.values(x=literal(value))
492+
.compile(
493+
dialect=db.dialect, compile_kwargs=dict(literal_binds=True),
494+
)
495+
)
496+
conn.execute(ins)
497+
conn.execute("SELECT 1")
498+
499+
if self.supports_whereclause:
500+
stmt = t.select().where(t.c.x == literal(value))
501+
else:
502+
stmt = t.select()
503+
504+
stmt = stmt.compile(
505+
dialect=db.dialect, compile_kwargs=dict(literal_binds=True),
506+
)
507+
for row in conn.execute(stmt):
508+
value = row[0]
509+
if filter_ is not None:
510+
value = filter_(value)
511+
assert value in output

0 commit comments

Comments
 (0)