Merge remote-tracking branch 'origin/main' into claude/json-api-docs-1-0-review-a3e83u

# Conflicts:
#	datasette/__init__.py
#	tests/test_api_write.py
This commit is contained in:
Claude 2026-07-06 22:25:27 +00:00
commit 194ee95ae2
No known key found for this signature in database
37 changed files with 4888 additions and 179 deletions

View file

@ -1338,6 +1338,109 @@ async def test_binary_data_in_json(ds_client, path, expected_json, expected_text
assert response.text == expected_text
@pytest.mark.asyncio
async def test_column_details_extra_table(ds_client):
response = await ds_client.get(
"/fixtures/binary_data.json?_size=0&_extra=column_details"
)
assert response.status_code == 200
data_detail = response.json()["column_details"]["data"]
assert data_detail["type"].lower() == "blob"
assert data_detail == {
"type": data_detail["type"],
"sqlite_type": "BLOB",
"notnull": False,
"default": None,
"is_pk": False,
"pk_position": 0,
"hidden": 0,
}
response = await ds_client.get(
"/fixtures/simple_primary_key.json?_size=0&_extra=column_details"
)
assert response.status_code == 200
column_details = response.json()["column_details"]
id_detail = column_details["id"]
assert id_detail["type"].lower() == "integer"
assert id_detail == {
"type": id_detail["type"],
"sqlite_type": "INTEGER",
"notnull": False,
"default": None,
"is_pk": True,
"pk_position": 1,
"hidden": 0,
}
content_detail = column_details["content"]
assert content_detail["type"].lower() == "text"
assert content_detail == {
"type": content_detail["type"],
"sqlite_type": "TEXT",
"notnull": False,
"default": None,
"is_pk": False,
"pk_position": 0,
"hidden": 0,
}
response = await ds_client.get(
"/fixtures/compound_three_primary_keys.json?_size=0&_extra=column_details"
)
assert response.status_code == 200
column_details = response.json()["column_details"]
assert column_details["pk1"]["is_pk"] is True
assert column_details["pk1"]["pk_position"] == 1
assert column_details["pk2"]["is_pk"] is True
assert column_details["pk2"]["pk_position"] == 2
assert column_details["pk3"]["is_pk"] is True
assert column_details["pk3"]["pk_position"] == 3
assert column_details["content"]["is_pk"] is False
assert column_details["content"]["pk_position"] == 0
def test_column_details_extra_defaults_and_notnull():
with make_app_client(extra_databases={"defaults.db": """
CREATE TABLE defaults (
i INTEGER NOT NULL DEFAULT 42,
s TEXT DEFAULT 'hello',
dt TEXT DEFAULT (datetime('now'))
);
"""}) as client:
response = client.get("/defaults/defaults.json?_size=0&_extra=column_details")
assert response.status == 200
column_details = response.json["column_details"]
assert column_details["i"]["notnull"] is True
assert column_details["i"]["default"] == "42"
assert column_details["s"]["notnull"] is False
assert column_details["s"]["default"] == "'hello'"
assert column_details["dt"]["default"] == "datetime('now')"
@pytest.mark.skipif(
sqlite_version() < (3, 31, 0),
reason="generated columns were added in SQLite 3.31.0",
)
def test_column_details_extra_generated_columns():
with make_app_client(extra_databases={"generated.db": """
CREATE TABLE generated_columns (
body TEXT,
body_length_virtual INTEGER
GENERATED ALWAYS AS (length(body)) VIRTUAL,
body_length_stored INTEGER
GENERATED ALWAYS AS (length(body)) STORED
);
"""}) as client:
response = client.get(
"/generated/generated_columns.json?_size=0&_extra=column_details"
)
assert response.status == 200
column_details = response.json["column_details"]
assert column_details["body"]["hidden"] == 0
assert column_details["body_length_virtual"]["hidden"] == 2
assert column_details["body_length_stored"]["hidden"] == 3
@pytest.mark.asyncio
@pytest.mark.parametrize(
"qs",