dclient/tests/test_rows.py
Claude e0efd08fa3
Add rows command with --filter, --where, --sort, --sort-desc options
New `rows` command fetches rows from a Datasette table with support for:
- `-f`/`--filter column op value` mapping to `?column__op=value` query params
- `--where` for raw SQL WHERE clauses (multiple allowed)
- `--sort` and `--sort-desc` for sorting (mutually exclusive)
- `eq` operator alias maps to Datasette's `exact` filter
- All other operators passed through directly to Datasette

https://claude.ai/code/session_01W24ZNk4u2qJRZ51rzx4cN7
2026-02-25 01:58:34 +00:00

272 lines
7.5 KiB
Python

from click.testing import CliRunner
from dclient.cli import cli
import json
import pathlib
import pytest
def test_rows_basic(httpx_mock):
httpx_mock.add_response(
json={
"ok": True,
"rows": [{"id": 1, "name": "Cleo", "age": 4}],
"next": None,
},
status_code=200,
)
runner = CliRunner()
result = runner.invoke(cli, ["rows", "https://example.com/db", "creatures"])
assert result.exit_code == 0
assert json.loads(result.output) == [{"id": 1, "name": "Cleo", "age": 4}]
request = httpx_mock.get_request()
assert str(request.url) == "https://example.com/db/creatures.json?_shape=objects"
def test_rows_single_filter(httpx_mock):
httpx_mock.add_response(
json={"ok": True, "rows": [{"id": 2, "name": "Luna", "age": 7}]},
status_code=200,
)
runner = CliRunner()
result = runner.invoke(
cli,
["rows", "https://example.com/db", "creatures", "-f", "age", "gte", "5"],
)
assert result.exit_code == 0
request = httpx_mock.get_request()
assert "age__gte=5" in str(request.url)
assert "_shape=objects" in str(request.url)
def test_rows_eq_maps_to_exact(httpx_mock):
httpx_mock.add_response(
json={"ok": True, "rows": [{"id": 1, "name": "Cleo"}]},
status_code=200,
)
runner = CliRunner()
result = runner.invoke(
cli,
["rows", "https://example.com/db", "creatures", "-f", "name", "eq", "Cleo"],
)
assert result.exit_code == 0
request = httpx_mock.get_request()
assert "name__exact=Cleo" in str(request.url)
def test_rows_multiple_filters(httpx_mock):
httpx_mock.add_response(
json={"ok": True, "rows": []},
status_code=200,
)
runner = CliRunner()
result = runner.invoke(
cli,
[
"rows",
"https://example.com/db",
"creatures",
"-f",
"species",
"in",
"dog,cat",
"-f",
"age",
"gt",
"3",
],
)
assert result.exit_code == 0
request = httpx_mock.get_request()
url_str = str(request.url)
assert "species__in=" in url_str
assert "age__gt=3" in url_str
def test_rows_where_clause(httpx_mock):
httpx_mock.add_response(
json={"ok": True, "rows": [{"id": 6}]},
status_code=200,
)
runner = CliRunner()
result = runner.invoke(
cli,
["rows", "https://example.com/db", "creatures", "--where", "id > 5"],
)
assert result.exit_code == 0
request = httpx_mock.get_request()
assert "_where=" in str(request.url)
def test_rows_multiple_where_clauses(httpx_mock):
httpx_mock.add_response(
json={"ok": True, "rows": []},
status_code=200,
)
runner = CliRunner()
result = runner.invoke(
cli,
[
"rows",
"https://example.com/db",
"creatures",
"--where",
"id > 5",
"--where",
"age < 10",
],
)
assert result.exit_code == 0
request = httpx_mock.get_request()
url_str = str(request.url)
assert url_str.count("_where=") == 2
def test_rows_sort(httpx_mock):
httpx_mock.add_response(
json={"ok": True, "rows": [{"id": 1}]},
status_code=200,
)
runner = CliRunner()
result = runner.invoke(
cli,
["rows", "https://example.com/db", "creatures", "--sort", "name"],
)
assert result.exit_code == 0
request = httpx_mock.get_request()
assert "_sort=name" in str(request.url)
def test_rows_sort_desc(httpx_mock):
httpx_mock.add_response(
json={"ok": True, "rows": [{"id": 1}]},
status_code=200,
)
runner = CliRunner()
result = runner.invoke(
cli,
["rows", "https://example.com/db", "creatures", "--sort-desc", "age"],
)
assert result.exit_code == 0
request = httpx_mock.get_request()
assert "_sort_desc=age" in str(request.url)
def test_rows_sort_mutual_exclusion():
runner = CliRunner()
result = runner.invoke(
cli,
[
"rows",
"https://example.com/db",
"creatures",
"--sort",
"name",
"--sort-desc",
"age",
],
)
assert result.exit_code == 1
assert "Cannot use both --sort and --sort-desc" in result.output
@pytest.mark.parametrize("with_token", (False, True))
def test_rows_with_token(httpx_mock, with_token):
httpx_mock.add_response(
json={"ok": True, "rows": [{"id": 1}]},
status_code=200,
)
runner = CliRunner()
args = ["rows", "https://example.com/db", "creatures"]
if with_token:
args.extend(["--token", "xyz"])
result = runner.invoke(cli, args)
assert result.exit_code == 0
request = httpx_mock.get_request()
if with_token:
assert request.headers["authorization"] == "Bearer xyz"
else:
assert "authorization" not in request.headers
def test_rows_http_error(httpx_mock):
httpx_mock.add_response(
json={"title": "Not Found", "error": "Table not found: creatures"},
status_code=404,
)
runner = CliRunner()
result = runner.invoke(cli, ["rows", "https://example.com/db", "creatures"])
assert result.exit_code == 1
assert "404 status code" in result.output
assert "Table not found: creatures" in result.output
def test_rows_ok_false_error(httpx_mock):
httpx_mock.add_response(
json={"ok": False, "error": "Something went wrong"},
status_code=200,
)
runner = CliRunner()
result = runner.invoke(cli, ["rows", "https://example.com/db", "creatures"])
assert result.exit_code == 1
assert "Something went wrong" in result.output
def test_rows_with_alias(httpx_mock, mocker, tmpdir):
mocker.patch("dclient.cli.get_config_dir", return_value=pathlib.Path(tmpdir))
aliases_file = pathlib.Path(tmpdir) / "aliases.json"
aliases_file.write_text(json.dumps({"content": "https://datasette.io/content"}))
httpx_mock.add_response(
json={"ok": True, "rows": [{"id": 1}]},
status_code=200,
)
runner = CliRunner()
result = runner.invoke(cli, ["rows", "content", "creatures"])
assert result.exit_code == 0
request = httpx_mock.get_request()
assert request.url.host == "datasette.io"
assert request.url.path == "/content/creatures.json"
def test_rows_verbose(httpx_mock):
httpx_mock.add_response(
json={"ok": True, "rows": []},
status_code=200,
)
runner = CliRunner()
result = runner.invoke(
cli,
["rows", "https://example.com/db", "creatures", "-v"],
)
assert result.exit_code == 0
assert "creatures.json" in result.output
def test_rows_combined(httpx_mock):
"""Test filters, where, and sort all together."""
httpx_mock.add_response(
json={"ok": True, "rows": [{"id": 1, "name": "Cleo", "age": 4}]},
status_code=200,
)
runner = CliRunner()
result = runner.invoke(
cli,
[
"rows",
"https://example.com/db",
"creatures",
"-f",
"species",
"eq",
"dog",
"--where",
"age > 2",
"--sort-desc",
"age",
],
)
assert result.exit_code == 0
request = httpx_mock.get_request()
url_str = str(request.url)
assert "species__exact=dog" in url_str
assert "_where=" in url_str
assert "_sort_desc=age" in url_str