mirror of
https://github.com/simonw/dclient.git
synced 2026-07-26 10:54:33 +02:00
267 lines
6.9 KiB
Python
267 lines
6.9 KiB
Python
"""Tests for multiple output formats on the query and default_query commands."""
|
|
|
|
from click.testing import CliRunner
|
|
from dclient.cli import cli
|
|
import json
|
|
import pathlib
|
|
|
|
QUERY_RESPONSE = {
|
|
"ok": True,
|
|
"database": "fixtures",
|
|
"query_name": None,
|
|
"rows": [
|
|
{"id": 1, "name": "Cleo", "age": 5},
|
|
{"id": 2, "name": "Pancakes", "age": 3},
|
|
],
|
|
"truncated": False,
|
|
"columns": ["id", "name", "age"],
|
|
"query": {"sql": "select * from dogs", "params": {}},
|
|
"error": None,
|
|
"private": False,
|
|
"allow_execute_sql": True,
|
|
}
|
|
|
|
|
|
def _mock_and_invoke(httpx_mock, extra_args=None):
|
|
httpx_mock.add_response(json=QUERY_RESPONSE, status_code=200)
|
|
runner = CliRunner()
|
|
args = ["query", "fixtures", "select * from dogs", "-i", "https://example.com"]
|
|
if extra_args:
|
|
args.extend(extra_args)
|
|
return runner.invoke(cli, args)
|
|
|
|
|
|
# -- query --csv --
|
|
|
|
|
|
def test_query_csv(httpx_mock):
|
|
result = _mock_and_invoke(httpx_mock, ["--csv"])
|
|
assert result.exit_code == 0
|
|
lines = result.output.strip().split("\n")
|
|
assert lines[0] == "id,name,age"
|
|
assert lines[1] == "1,Cleo,5"
|
|
assert lines[2] == "2,Pancakes,3"
|
|
|
|
|
|
# -- query --tsv --
|
|
|
|
|
|
def test_query_tsv(httpx_mock):
|
|
result = _mock_and_invoke(httpx_mock, ["--tsv"])
|
|
assert result.exit_code == 0
|
|
lines = result.output.strip().split("\n")
|
|
assert lines[0] == "id\tname\tage"
|
|
assert lines[1] == "1\tCleo\t5"
|
|
assert lines[2] == "2\tPancakes\t3"
|
|
|
|
|
|
# -- query --nl --
|
|
|
|
|
|
def test_query_nl(httpx_mock):
|
|
result = _mock_and_invoke(httpx_mock, ["--nl"])
|
|
assert result.exit_code == 0
|
|
lines = result.output.strip().split("\n")
|
|
assert len(lines) == 2
|
|
assert json.loads(lines[0]) == {"id": 1, "name": "Cleo", "age": 5}
|
|
assert json.loads(lines[1]) == {"id": 2, "name": "Pancakes", "age": 3}
|
|
|
|
|
|
# -- query --table --
|
|
|
|
|
|
def test_query_table(httpx_mock):
|
|
result = _mock_and_invoke(httpx_mock, ["--table"])
|
|
assert result.exit_code == 0
|
|
lines = result.output.strip().split("\n")
|
|
# Should have a header row, a separator row, and 2 data rows
|
|
assert len(lines) == 4
|
|
# Header should contain column names
|
|
assert "id" in lines[0]
|
|
assert "name" in lines[0]
|
|
assert "age" in lines[0]
|
|
# Data rows should contain values
|
|
assert "Cleo" in lines[2]
|
|
assert "Pancakes" in lines[3]
|
|
|
|
|
|
# -- query -t shortcut for --table --
|
|
|
|
|
|
def test_query_table_shortcut(httpx_mock):
|
|
result = _mock_and_invoke(httpx_mock, ["-t"])
|
|
assert result.exit_code == 0
|
|
lines = result.output.strip().split("\n")
|
|
assert len(lines) == 4
|
|
assert "Cleo" in lines[2]
|
|
|
|
|
|
# -- default JSON (no flag) stays the same --
|
|
|
|
|
|
def test_query_default_json(httpx_mock):
|
|
result = _mock_and_invoke(httpx_mock)
|
|
assert result.exit_code == 0
|
|
data = json.loads(result.output)
|
|
assert data == [
|
|
{"id": 1, "name": "Cleo", "age": 5},
|
|
{"id": 2, "name": "Pancakes", "age": 3},
|
|
]
|
|
|
|
|
|
# -- default_query also supports output formats --
|
|
|
|
|
|
def _mock_default_query(httpx_mock, mocker, tmpdir, extra_args=None):
|
|
mocker.patch("dclient.cli.get_config_dir", return_value=pathlib.Path(tmpdir))
|
|
config_file = pathlib.Path(tmpdir) / "config.json"
|
|
config_file.write_text(
|
|
json.dumps(
|
|
{
|
|
"default_instance": "prod",
|
|
"instances": {
|
|
"prod": {
|
|
"url": "https://prod.example.com",
|
|
"default_database": "main",
|
|
}
|
|
},
|
|
}
|
|
)
|
|
)
|
|
httpx_mock.add_response(json=QUERY_RESPONSE, status_code=200)
|
|
runner = CliRunner()
|
|
args = ["select * from dogs"]
|
|
if extra_args:
|
|
args.extend(extra_args)
|
|
return runner.invoke(cli, args)
|
|
|
|
|
|
def test_default_query_csv(httpx_mock, mocker, tmpdir):
|
|
result = _mock_default_query(httpx_mock, mocker, tmpdir, ["--csv"])
|
|
assert result.exit_code == 0
|
|
lines = result.output.strip().split("\n")
|
|
assert lines[0] == "id,name,age"
|
|
assert lines[1] == "1,Cleo,5"
|
|
|
|
|
|
def test_default_query_table(httpx_mock, mocker, tmpdir):
|
|
result = _mock_default_query(httpx_mock, mocker, tmpdir, ["--table"])
|
|
assert result.exit_code == 0
|
|
assert "Cleo" in result.output
|
|
assert "Pancakes" in result.output
|
|
lines = result.output.strip().split("\n")
|
|
assert len(lines) == 4
|
|
|
|
|
|
def test_default_query_nl(httpx_mock, mocker, tmpdir):
|
|
result = _mock_default_query(httpx_mock, mocker, tmpdir, ["--nl"])
|
|
assert result.exit_code == 0
|
|
lines = result.output.strip().split("\n")
|
|
assert json.loads(lines[0]) == {"id": 1, "name": "Cleo", "age": 5}
|
|
|
|
|
|
# -- edge cases --
|
|
|
|
|
|
def test_query_csv_with_commas_in_values(httpx_mock):
|
|
httpx_mock.add_response(
|
|
json={
|
|
"ok": True,
|
|
"rows": [{"name": "Smith, John", "note": 'He said "hi"'}],
|
|
"columns": ["name", "note"],
|
|
},
|
|
status_code=200,
|
|
)
|
|
runner = CliRunner()
|
|
result = runner.invoke(
|
|
cli,
|
|
[
|
|
"query",
|
|
"db",
|
|
"select * from t",
|
|
"-i",
|
|
"https://example.com",
|
|
"--csv",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
lines = result.output.strip().split("\n")
|
|
assert lines[0] == "name,note"
|
|
# CSV should properly quote fields with commas/quotes
|
|
assert '"Smith, John"' in lines[1]
|
|
|
|
|
|
def test_query_table_empty_results(httpx_mock):
|
|
httpx_mock.add_response(
|
|
json={
|
|
"ok": True,
|
|
"rows": [],
|
|
"columns": ["id", "name"],
|
|
},
|
|
status_code=200,
|
|
)
|
|
runner = CliRunner()
|
|
result = runner.invoke(
|
|
cli,
|
|
[
|
|
"query",
|
|
"db",
|
|
"select * from t",
|
|
"-i",
|
|
"https://example.com",
|
|
"--table",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
|
|
|
|
def test_query_csv_empty_results(httpx_mock):
|
|
httpx_mock.add_response(
|
|
json={
|
|
"ok": True,
|
|
"rows": [],
|
|
"columns": ["id", "name"],
|
|
},
|
|
status_code=200,
|
|
)
|
|
runner = CliRunner()
|
|
result = runner.invoke(
|
|
cli,
|
|
[
|
|
"query",
|
|
"db",
|
|
"select * from t",
|
|
"-i",
|
|
"https://example.com",
|
|
"--csv",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
lines = result.output.strip().split("\n")
|
|
assert lines[0] == "id,name"
|
|
assert len(lines) == 1 # header only, no data rows
|
|
|
|
|
|
def test_query_nl_empty_results(httpx_mock):
|
|
httpx_mock.add_response(
|
|
json={
|
|
"ok": True,
|
|
"rows": [],
|
|
"columns": ["id", "name"],
|
|
},
|
|
status_code=200,
|
|
)
|
|
runner = CliRunner()
|
|
result = runner.invoke(
|
|
cli,
|
|
[
|
|
"query",
|
|
"db",
|
|
"select * from t",
|
|
"-i",
|
|
"https://example.com",
|
|
"--nl",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
assert result.output.strip() == ""
|