Skip to content

Commit b514850

Browse files
committed
Improve cell magic parser test coverage
1 parent d95802b commit b514850

File tree

1 file changed

+229
-7
lines changed

1 file changed

+229
-7
lines changed

tests/unit/test_magics.py

+229-7
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
from google.cloud import bigquery
4444
from google.cloud.bigquery import job
4545
from google.cloud.bigquery import table
46-
from google.cloud.bigquery.ipython_magics import line_arg_parser as lap
4746
from google.cloud.bigquery.ipython_magics import magics
4847
from tests.unit.helpers import make_connection
4948
from test_utils.imports import maybe_fail_import
@@ -1165,6 +1164,37 @@ def test_bigquery_magic_with_project():
11651164
assert magics.context.project == "general-project"
11661165

11671166

1167+
@pytest.mark.usefixtures("ipython_interactive")
1168+
def test_bigquery_magic_with_multiple_options():
1169+
ip = IPython.get_ipython()
1170+
ip.extension_manager.load_extension("google.cloud.bigquery")
1171+
magics.context._project = None
1172+
1173+
credentials_mock = mock.create_autospec(
1174+
google.auth.credentials.Credentials, instance=True
1175+
)
1176+
default_patch = mock.patch(
1177+
"google.auth.default", return_value=(credentials_mock, "general-project")
1178+
)
1179+
run_query_patch = mock.patch(
1180+
"google.cloud.bigquery.ipython_magics.magics._run_query", autospec=True
1181+
)
1182+
with run_query_patch as run_query_mock, default_patch:
1183+
ip.run_cell_magic(
1184+
"bigquery",
1185+
"--project=specific-project --use_legacy_sql --maximum_bytes_billed 1024",
1186+
"SELECT 17 as num",
1187+
)
1188+
1189+
args, kwargs = run_query_mock.call_args
1190+
client_used = args[0]
1191+
assert client_used.project == "specific-project"
1192+
1193+
job_config_used = kwargs["job_config"]
1194+
assert job_config_used.use_legacy_sql
1195+
assert job_config_used.maximum_bytes_billed == 1024
1196+
1197+
11681198
@pytest.mark.usefixtures("ipython_interactive")
11691199
@pytest.mark.skipif(pandas is None, reason="Requires `pandas`")
11701200
def test_bigquery_magic_with_string_params():
@@ -1176,7 +1206,9 @@ def test_bigquery_magic_with_string_params():
11761206

11771207
sql = "SELECT @num AS num"
11781208
result = pandas.DataFrame([17], columns=["num"])
1179-
assert "params_string_df" not in ip.user_ns
1209+
1210+
if "params_dict_df" in ip.user_ns:
1211+
del ip.user_ns["params_dict_df"]
11801212

11811213
run_query_patch = mock.patch(
11821214
"google.cloud.bigquery.ipython_magics.magics._run_query", autospec=True
@@ -1207,9 +1239,13 @@ def test_bigquery_magic_with_dict_params():
12071239
google.auth.credentials.Credentials, instance=True
12081240
)
12091241

1210-
sql = "SELECT @num AS num"
1211-
result = pandas.DataFrame([17], columns=["num"])
1212-
assert "params_dict_df" not in ip.user_ns
1242+
sql = "SELECT @num AS num, @tricky_value as tricky_value"
1243+
result = pandas.DataFrame(
1244+
[(17, '--params "value"')], columns=["num", "tricky_value"]
1245+
)
1246+
1247+
if "params_dict_df" in ip.user_ns:
1248+
del ip.user_ns["params_dict_df"]
12131249

12141250
run_query_patch = mock.patch(
12151251
"google.cloud.bigquery.ipython_magics.magics._run_query", autospec=True
@@ -1221,7 +1257,7 @@ def test_bigquery_magic_with_dict_params():
12211257
with run_query_patch as run_query_mock:
12221258
run_query_mock.return_value = query_job_mock
12231259

1224-
params = {"num": 17}
1260+
params = {"num": 17, "tricky_value": '--params "value"'}
12251261
# Insert dictionary into user namespace so that it can be expanded
12261262
ip.user_ns["params"] = params
12271263
ip.run_cell_magic("bigquery", "params_dict_df --params $params", sql)
@@ -1233,6 +1269,192 @@ def test_bigquery_magic_with_dict_params():
12331269
assert len(df) == len(result) # verify row count
12341270
assert list(df) == list(result) # verify column names
12351271

1272+
assert df["num"][0] == 17
1273+
assert df["tricky_value"][0] == '--params "value"'
1274+
1275+
1276+
@pytest.mark.usefixtures("ipython_interactive")
1277+
@pytest.mark.skipif(pandas is None, reason="Requires `pandas`")
1278+
def test_bigquery_magic_with_dict_params_nonexisting():
1279+
ip = IPython.get_ipython()
1280+
ip.extension_manager.load_extension("google.cloud.bigquery")
1281+
magics.context.credentials = mock.create_autospec(
1282+
google.auth.credentials.Credentials, instance=True
1283+
)
1284+
1285+
sql = "SELECT @foo AS foo"
1286+
1287+
with pytest.raises(NameError, match=r".*undefined variable.*unknown_name.*"):
1288+
ip.run_cell_magic("bigquery", "params_dict_df --params $unknown_name", sql)
1289+
1290+
1291+
@pytest.mark.usefixtures("ipython_interactive")
1292+
@pytest.mark.skipif(pandas is None, reason="Requires `pandas`")
1293+
def test_bigquery_magic_with_dict_params_incorrect_syntax():
1294+
ip = IPython.get_ipython()
1295+
ip.extension_manager.load_extension("google.cloud.bigquery")
1296+
magics.context.credentials = mock.create_autospec(
1297+
google.auth.credentials.Credentials, instance=True
1298+
)
1299+
1300+
sql = "SELECT @foo AS foo"
1301+
1302+
with pytest.raises(SyntaxError, match=r".*--params.*"):
1303+
cell_magic_args = "params_dict_df --params {'foo': 1; 'bar': 2}"
1304+
ip.run_cell_magic("bigquery", cell_magic_args, sql)
1305+
1306+
1307+
@pytest.mark.usefixtures("ipython_interactive")
1308+
@pytest.mark.skipif(pandas is None, reason="Requires `pandas`")
1309+
def test_bigquery_magic_with_dict_params_duplicate():
1310+
ip = IPython.get_ipython()
1311+
ip.extension_manager.load_extension("google.cloud.bigquery")
1312+
magics.context.credentials = mock.create_autospec(
1313+
google.auth.credentials.Credentials, instance=True
1314+
)
1315+
1316+
sql = "SELECT @foo AS foo"
1317+
1318+
with pytest.raises(ValueError, match=r"Duplicate --params option\."):
1319+
cell_magic_args = (
1320+
"params_dict_df --params {'foo': 1} " "--verbose " "--params {'bar': 2} "
1321+
)
1322+
ip.run_cell_magic("bigquery", cell_magic_args, sql)
1323+
1324+
1325+
@pytest.mark.usefixtures("ipython_interactive")
1326+
@pytest.mark.skipif(pandas is None, reason="Requires `pandas`")
1327+
def test_bigquery_magic_with_option_value_incorrect():
1328+
ip = IPython.get_ipython()
1329+
ip.extension_manager.load_extension("google.cloud.bigquery")
1330+
magics.context.credentials = mock.create_autospec(
1331+
google.auth.credentials.Credentials, instance=True
1332+
)
1333+
1334+
sql = "SELECT @foo AS foo"
1335+
1336+
exc_pattern = r".*[Uu]nrecognized input.*option values correct\?.*"
1337+
with pytest.raises(ValueError, match=exc_pattern):
1338+
cell_magic_args = "params_dict_df --max_results [PLENTY!]"
1339+
ip.run_cell_magic("bigquery", cell_magic_args, sql)
1340+
1341+
1342+
@pytest.mark.usefixtures("ipython_interactive")
1343+
@pytest.mark.skipif(pandas is None, reason="Requires `pandas`")
1344+
def test_bigquery_magic_with_dict_params_negative_value():
1345+
ip = IPython.get_ipython()
1346+
ip.extension_manager.load_extension("google.cloud.bigquery")
1347+
magics.context.credentials = mock.create_autospec(
1348+
google.auth.credentials.Credentials, instance=True
1349+
)
1350+
1351+
sql = "SELECT @num AS num"
1352+
result = pandas.DataFrame([-17], columns=["num"])
1353+
1354+
if "params_dict_df" in ip.user_ns:
1355+
del ip.user_ns["params_dict_df"]
1356+
1357+
run_query_patch = mock.patch(
1358+
"google.cloud.bigquery.ipython_magics.magics._run_query", autospec=True
1359+
)
1360+
query_job_mock = mock.create_autospec(
1361+
google.cloud.bigquery.job.QueryJob, instance=True
1362+
)
1363+
query_job_mock.to_dataframe.return_value = result
1364+
with run_query_patch as run_query_mock:
1365+
run_query_mock.return_value = query_job_mock
1366+
1367+
params = {"num": -17}
1368+
# Insert dictionary into user namespace so that it can be expanded
1369+
ip.user_ns["params"] = params
1370+
ip.run_cell_magic("bigquery", "params_dict_df --params $params", sql)
1371+
1372+
run_query_mock.assert_called_once_with(mock.ANY, sql.format(num=-17), mock.ANY)
1373+
1374+
assert "params_dict_df" in ip.user_ns # verify that the variable exists
1375+
df = ip.user_ns["params_dict_df"]
1376+
assert len(df) == len(result) # verify row count
1377+
assert list(df) == list(result) # verify column names
1378+
assert df["num"][0] == -17
1379+
1380+
1381+
@pytest.mark.usefixtures("ipython_interactive")
1382+
@pytest.mark.skipif(pandas is None, reason="Requires `pandas`")
1383+
def test_bigquery_magic_with_dict_params_array_value():
1384+
ip = IPython.get_ipython()
1385+
ip.extension_manager.load_extension("google.cloud.bigquery")
1386+
magics.context.credentials = mock.create_autospec(
1387+
google.auth.credentials.Credentials, instance=True
1388+
)
1389+
1390+
sql = "SELECT @num AS num"
1391+
result = pandas.DataFrame(["foo bar", "baz quux"], columns=["array_data"])
1392+
1393+
if "params_dict_df" in ip.user_ns:
1394+
del ip.user_ns["params_dict_df"]
1395+
1396+
run_query_patch = mock.patch(
1397+
"google.cloud.bigquery.ipython_magics.magics._run_query", autospec=True
1398+
)
1399+
query_job_mock = mock.create_autospec(
1400+
google.cloud.bigquery.job.QueryJob, instance=True
1401+
)
1402+
query_job_mock.to_dataframe.return_value = result
1403+
with run_query_patch as run_query_mock:
1404+
run_query_mock.return_value = query_job_mock
1405+
1406+
params = {"array_data": ["foo bar", "baz quux"]}
1407+
# Insert dictionary into user namespace so that it can be expanded
1408+
ip.user_ns["params"] = params
1409+
ip.run_cell_magic("bigquery", "params_dict_df --params $params", sql)
1410+
1411+
run_query_mock.assert_called_once_with(mock.ANY, sql.format(num=-17), mock.ANY)
1412+
1413+
assert "params_dict_df" in ip.user_ns # verify that the variable exists
1414+
df = ip.user_ns["params_dict_df"]
1415+
assert len(df) == len(result) # verify row count
1416+
assert list(df) == list(result) # verify column names
1417+
assert list(df["array_data"]) == ["foo bar", "baz quux"]
1418+
1419+
1420+
@pytest.mark.usefixtures("ipython_interactive")
1421+
@pytest.mark.skipif(pandas is None, reason="Requires `pandas`")
1422+
def test_bigquery_magic_with_dict_params_tuple_value():
1423+
ip = IPython.get_ipython()
1424+
ip.extension_manager.load_extension("google.cloud.bigquery")
1425+
magics.context.credentials = mock.create_autospec(
1426+
google.auth.credentials.Credentials, instance=True
1427+
)
1428+
1429+
sql = "SELECT @num AS num"
1430+
result = pandas.DataFrame(["foo bar", "baz quux"], columns=["array_data"])
1431+
1432+
if "params_dict_df" in ip.user_ns:
1433+
del ip.user_ns["params_dict_df"]
1434+
1435+
run_query_patch = mock.patch(
1436+
"google.cloud.bigquery.ipython_magics.magics._run_query", autospec=True
1437+
)
1438+
query_job_mock = mock.create_autospec(
1439+
google.cloud.bigquery.job.QueryJob, instance=True
1440+
)
1441+
query_job_mock.to_dataframe.return_value = result
1442+
with run_query_patch as run_query_mock:
1443+
run_query_mock.return_value = query_job_mock
1444+
1445+
params = {"array_data": ("foo bar", "baz quux")}
1446+
# Insert dictionary into user namespace so that it can be expanded
1447+
ip.user_ns["params"] = params
1448+
ip.run_cell_magic("bigquery", "params_dict_df --params $params", sql)
1449+
1450+
run_query_mock.assert_called_once_with(mock.ANY, sql.format(num=-17), mock.ANY)
1451+
1452+
assert "params_dict_df" in ip.user_ns # verify that the variable exists
1453+
df = ip.user_ns["params_dict_df"]
1454+
assert len(df) == len(result) # verify row count
1455+
assert list(df) == list(result) # verify column names
1456+
assert list(df["array_data"]) == ["foo bar", "baz quux"]
1457+
12361458

12371459
@pytest.mark.usefixtures("ipython_interactive")
12381460
@pytest.mark.skipif(pandas is None, reason="Requires `pandas`")
@@ -1245,7 +1467,7 @@ def test_bigquery_magic_with_improperly_formatted_params():
12451467

12461468
sql = "SELECT @num AS num"
12471469

1248-
with pytest.raises(lap.exceptions.QueryParamsParseError):
1470+
with pytest.raises(SyntaxError):
12491471
ip.run_cell_magic("bigquery", "--params {17}", sql)
12501472

12511473

0 commit comments

Comments
 (0)