|
20 | 20 | import os
|
21 | 21 | import pkg_resources
|
22 | 22 | import pytest
|
| 23 | +import random |
23 | 24 | import unittest
|
24 | 25 | from unittest import mock
|
25 | 26 |
|
|
61 | 62 | )
|
62 | 63 |
|
63 | 64 | from google.api_core.datetime_helpers import DatetimeWithNanoseconds
|
64 |
| - |
65 | 65 | from google.cloud import spanner_dbapi
|
66 | 66 |
|
67 | 67 | from sqlalchemy.testing.suite.test_cte import * # noqa: F401, F403
|
|
98 | 98 | )
|
99 | 99 | from sqlalchemy.testing.suite.test_results import RowFetchTest as _RowFetchTest
|
100 | 100 | from sqlalchemy.testing.suite.test_types import ( # noqa: F401, F403
|
| 101 | + _DateFixture as _DateFixtureTest, |
| 102 | + _LiteralRoundTripFixture, |
| 103 | + _UnicodeFixture as _UnicodeFixtureTest, |
101 | 104 | BooleanTest as _BooleanTest,
|
102 | 105 | DateTest as _DateTest,
|
103 |
| - _DateFixture as _DateFixtureTest, |
104 | 106 | DateTimeHistoricTest,
|
105 | 107 | DateTimeCoercedToDateTimeTest as _DateTimeCoercedToDateTimeTest,
|
106 | 108 | DateTimeMicrosecondsTest as _DateTimeMicrosecondsTest,
|
107 | 109 | DateTimeTest as _DateTimeTest,
|
108 | 110 | IntegerTest as _IntegerTest,
|
109 |
| - _LiteralRoundTripFixture, |
| 111 | + JSONTest as _JSONTest, |
110 | 112 | NumericTest as _NumericTest,
|
111 | 113 | StringTest as _StringTest,
|
112 | 114 | TextTest as _TextTest,
|
|
115 | 117 | TimestampMicrosecondsTest,
|
116 | 118 | UnicodeVarcharTest as _UnicodeVarcharTest,
|
117 | 119 | UnicodeTextTest as _UnicodeTextTest,
|
118 |
| - _UnicodeFixture as _UnicodeFixtureTest, |
119 | 120 | )
|
120 | 121 | from test._helpers import get_db_url
|
121 | 122 |
|
@@ -1751,3 +1752,128 @@ def test_get_column_returns_computed(self):
|
1751 | 1752 | is_true("computed" in compData)
|
1752 | 1753 | is_true("sqltext" in compData["computed"])
|
1753 | 1754 | eq_(self.normalize(compData["computed"]["sqltext"]), "normal+42")
|
| 1755 | + |
| 1756 | + |
| 1757 | +@pytest.mark.skipif( |
| 1758 | + bool(os.environ.get("SPANNER_EMULATOR_HOST")), reason="Skipped on emulator" |
| 1759 | +) |
| 1760 | +class JSONTest(_JSONTest): |
| 1761 | + @pytest.mark.skip("Values without keys are not supported.") |
| 1762 | + def test_single_element_round_trip(self, element): |
| 1763 | + pass |
| 1764 | + |
| 1765 | + def _test_round_trip(self, data_element): |
| 1766 | + data_table = self.tables.data_table |
| 1767 | + |
| 1768 | + config.db.execute( |
| 1769 | + data_table.insert(), |
| 1770 | + {"id": random.randint(1, 100000000), "name": "row1", "data": data_element}, |
| 1771 | + ) |
| 1772 | + |
| 1773 | + row = config.db.execute(select([data_table.c.data])).first() |
| 1774 | + |
| 1775 | + eq_(row, (data_element,)) |
| 1776 | + |
| 1777 | + def test_unicode_round_trip(self): |
| 1778 | + # note we include Unicode supplementary characters as well |
| 1779 | + with config.db.connect() as conn: |
| 1780 | + conn.execute( |
| 1781 | + self.tables.data_table.insert(), |
| 1782 | + { |
| 1783 | + "id": random.randint(1, 100000000), |
| 1784 | + "name": "r1", |
| 1785 | + "data": { |
| 1786 | + util.u("réve🐍 illé"): util.u("réve🐍 illé"), |
| 1787 | + "data": {"k1": util.u("drôl🐍e")}, |
| 1788 | + }, |
| 1789 | + }, |
| 1790 | + ) |
| 1791 | + |
| 1792 | + eq_( |
| 1793 | + conn.scalar(select([self.tables.data_table.c.data])), |
| 1794 | + { |
| 1795 | + util.u("réve🐍 illé"): util.u("réve🐍 illé"), |
| 1796 | + "data": {"k1": util.u("drôl🐍e")}, |
| 1797 | + }, |
| 1798 | + ) |
| 1799 | + |
| 1800 | + @pytest.mark.skip("Parameterized types are not supported.") |
| 1801 | + def test_eval_none_flag_orm(self): |
| 1802 | + pass |
| 1803 | + |
| 1804 | + @pytest.mark.skip( |
| 1805 | + "Spanner JSON_VALUE() always returns STRING," |
| 1806 | + "thus, this test case can't be executed." |
| 1807 | + ) |
| 1808 | + def test_index_typed_comparison(self): |
| 1809 | + pass |
| 1810 | + |
| 1811 | + @pytest.mark.skip( |
| 1812 | + "Spanner JSON_VALUE() always returns STRING," |
| 1813 | + "thus, this test case can't be executed." |
| 1814 | + ) |
| 1815 | + def test_path_typed_comparison(self): |
| 1816 | + pass |
| 1817 | + |
| 1818 | + @pytest.mark.skip("Custom JSON de-/serializers are not supported.") |
| 1819 | + def test_round_trip_custom_json(self): |
| 1820 | + pass |
| 1821 | + |
| 1822 | + def _index_fixtures(fn): |
| 1823 | + fn = testing.combinations( |
| 1824 | + ("boolean", True), |
| 1825 | + ("boolean", False), |
| 1826 | + ("boolean", None), |
| 1827 | + ("string", "some string"), |
| 1828 | + ("string", None), |
| 1829 | + ("integer", 15), |
| 1830 | + ("integer", 1), |
| 1831 | + ("integer", 0), |
| 1832 | + ("integer", None), |
| 1833 | + ("float", 28.5), |
| 1834 | + ("float", None), |
| 1835 | + id_="sa", |
| 1836 | + )(fn) |
| 1837 | + return fn |
| 1838 | + |
| 1839 | + @_index_fixtures |
| 1840 | + def test_index_typed_access(self, datatype, value): |
| 1841 | + data_table = self.tables.data_table |
| 1842 | + data_element = {"key1": value} |
| 1843 | + with config.db.connect() as conn: |
| 1844 | + conn.execute( |
| 1845 | + data_table.insert(), |
| 1846 | + { |
| 1847 | + "id": random.randint(1, 100000000), |
| 1848 | + "name": "row1", |
| 1849 | + "data": data_element, |
| 1850 | + "nulldata": data_element, |
| 1851 | + }, |
| 1852 | + ) |
| 1853 | + |
| 1854 | + expr = data_table.c.data["key1"] |
| 1855 | + expr = getattr(expr, "as_%s" % datatype)() |
| 1856 | + |
| 1857 | + roundtrip = conn.scalar(select([expr])) |
| 1858 | + if roundtrip in ("true", "false", None): |
| 1859 | + roundtrip = str(roundtrip).capitalize() |
| 1860 | + |
| 1861 | + eq_(str(roundtrip), str(value)) |
| 1862 | + |
| 1863 | + @pytest.mark.skip( |
| 1864 | + "Spanner doesn't support type casts inside JSON_VALUE() function." |
| 1865 | + ) |
| 1866 | + def test_round_trip_json_null_as_json_null(self): |
| 1867 | + pass |
| 1868 | + |
| 1869 | + @pytest.mark.skip( |
| 1870 | + "Spanner doesn't support type casts inside JSON_VALUE() function." |
| 1871 | + ) |
| 1872 | + def test_round_trip_none_as_json_null(self): |
| 1873 | + pass |
| 1874 | + |
| 1875 | + @pytest.mark.skip( |
| 1876 | + "Spanner doesn't support type casts inside JSON_VALUE() function." |
| 1877 | + ) |
| 1878 | + def test_round_trip_none_as_sql_null(self): |
| 1879 | + pass |
0 commit comments