From eb1dd9edc2f4148e2f73e2cd76a999d8fc9d4fc5 Mon Sep 17 00:00:00 2001 From: thekevinbot <248217931+thekevinbot@users.noreply.github.com> Date: Sun, 11 Oct 2026 20:17:30 -0400 Subject: [PATCH 1/5] test: frontend state reports query root and declared tables --- packages/rust/tests/cli_e2e.rs | 13 ++- packages/rust/tests/cli_integration.rs | 141 +++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 2 deletions(-) diff --git a/packages/rust/tests/cli_e2e.rs b/packages/rust/tests/cli_e2e.rs index 6c3752650..e5662b7f6 100644 --- a/packages/rust/tests/cli_e2e.rs +++ b/packages/rust/tests/cli_e2e.rs @@ -147,6 +147,10 @@ fn kill_and_wait(mut child: ServerGuard) { let _ = child.wait(); } +fn canonical_root(path: &std::path::Path) -> String { + path.canonicalize().unwrap().to_string_lossy().into_owned() +} + #[test] fn frontend_is_opt_in_and_serves_a_bundled_page() { let root = TempDir::new().unwrap(); @@ -187,7 +191,7 @@ fn frontend_is_opt_in_and_serves_a_bundled_page() { .unwrap() .json::() .unwrap(), - json!({"error": null}) + json!({"error": null, "root": canonical_root(root.path()), "tables": []}) ); assert_eq!( client @@ -260,7 +264,12 @@ fn frontend_loads_with_an_explicit_valid_config() { .unwrap() .json::() .unwrap(), - json!({"error": null}) + json!({"error": null, "root": canonical_root(root.path()), "tables": [ + {"name": "posts", "columns": [ + {"name": "basename", "type": "TEXT"}, + {"name": "size", "type": "INTEGER"} + ]} + ]}) ); assert_eq!( client diff --git a/packages/rust/tests/cli_integration.rs b/packages/rust/tests/cli_integration.rs index 672b3d63a..f00f8c65f 100644 --- a/packages/rust/tests/cli_integration.rs +++ b/packages/rust/tests/cli_integration.rs @@ -634,3 +634,144 @@ async fn cors_preflight_for_a_json_query_is_allowed() { ); handle.shutdown().await.unwrap(); } + +async fn frontend_state(db: DirSQL) -> (ServerHandle, JsonValue) { + let handle = serve(ServerConfig::ephemeral().with_frontend(), db) + .await + .unwrap(); + let resp = reqwest::get(format!("{}/frontend/state", base_url(&handle))) + .await + .unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + let body = resp.json().await.unwrap(); + (handle, body) +} + +fn db_from_config(config: &str) -> (TempDir, DirSQL) { + let root = TempDir::new().unwrap(); + fs::write(root.path().join(".dirsql.toml"), config).unwrap(); + let db = DirSQL::builder() + .root(root.path()) + .config(root.path().join(".dirsql.toml")) + .build() + .unwrap(); + (root, db) +} + +#[tokio::test] +async fn frontend_state_reports_the_query_root_and_declared_columns() { + let (root, db) = blog_fixture(); + let (handle, body) = frontend_state(db).await; + + assert_eq!(body["error"], JsonValue::Null); + assert_eq!(body["root"], json!(root.path().to_string_lossy())); + assert_eq!( + body["tables"], + json!([{"name": "posts", "columns": [ + {"name": "basename", "type": "TEXT"}, + {"name": "size", "type": "INTEGER"}, + ]}]) + ); + handle.shutdown().await.unwrap(); +} + +#[tokio::test] +async fn frontend_state_lists_empty_and_unusually_named_tables_in_name_order() { + let (_root, db) = db_from_config( + r#" +[[table]] +name = "order" +ddl = 'CREATE TABLE "order" ("select" TEXT, "two words" REAL)' +glob = "nothing/*.json" +on-file = "true" + +[[table]] +name = "_Mixed9" +ddl = "CREATE TABLE _Mixed9 (a)" +glob = "nothing/*.csv" +on-file = "true" +"#, + ); + let (handle, body) = frontend_state(db).await; + + assert_eq!( + body["tables"], + json!([ + {"name": "_Mixed9", "columns": [{"name": "a", "type": ""}]}, + {"name": "order", "columns": [ + {"name": "select", "type": "TEXT"}, + {"name": "two words", "type": "REAL"}, + ]}, + ]) + ); + handle.shutdown().await.unwrap(); +} + +#[tokio::test] +async fn frontend_state_excludes_bookkeeping_shadow_and_path_tables() { + let (root, db) = db_from_config( + r#" +[[table]] +name = "notes" +ddl = """ +CREATE TABLE notes (body TEXT); +CREATE VIRTUAL TABLE notes_fts USING fts5(body, content='notes', content_rowid='rowid'); +""" +glob = "nothing/*.json" +on-file = "true" +"#, + ); + fs::write(root.path().join("a.txt"), "a").unwrap(); + db.query("SELECT path FROM './'").unwrap(); + let (handle, body) = frontend_state(db).await; + + assert_eq!( + body["tables"], + json!([{"name": "notes", "columns": [{"name": "body", "type": "TEXT"}]}]) + ); + handle.shutdown().await.unwrap(); +} + +#[tokio::test] +async fn frontend_state_has_no_tables_without_a_config() { + let root = TempDir::new().unwrap(); + let db = DirSQL::builder().root(root.path()).build().unwrap(); + let (handle, body) = frontend_state(db).await; + + assert_eq!(body["tables"], json!([])); + assert_eq!(body["root"], json!(root.path().to_string_lossy())); + handle.shutdown().await.unwrap(); +} + +#[cfg(unix)] +#[tokio::test] +async fn frontend_state_has_no_tables_for_a_functions_only_config() { + let (_root, db) = db_from_config( + r#" +[[dirsql.function]] +name = "up" +args = [1] +command = "python3 worker.py" +"#, + ); + let (handle, body) = frontend_state(db).await; + + assert_eq!(body["tables"], json!([])); + handle.shutdown().await.unwrap(); +} + +#[tokio::test] +async fn frontend_state_leaves_query_responses_unchanged() { + let (_root, db) = blog_fixture(); + let (handle, _) = frontend_state(db).await; + + let resp = reqwest::Client::new() + .post(format!("{}/query", base_url(&handle))) + .json(&json!({"sql": "SELECT basename FROM posts WHERE size = 0 AND 0"})) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.json::().await.unwrap(), json!([])); + handle.shutdown().await.unwrap(); +} From ae61a26f7b9018c3e12aebeea7c6a3cf32ef4d87 Mon Sep 17 00:00:00 2001 From: thekevinbot <248217931+thekevinbot@users.noreply.github.com> Date: Sun, 11 Oct 2026 20:22:22 -0400 Subject: [PATCH 2/5] feat: frontend state reports query root and declared tables --- PARITY.md | 5 +- docs/reference/cli.md | 30 ++++- .../2026-10-11-frontend-state-catalog.md | 1 + .../2026-10-11-frontend-state-catalog.md | 21 ++++ packages/rust/src/cli/router.rs | 37 +++++- packages/rust/src/db.rs | 112 ++++++++++++++++++ packages/rust/src/lib.rs | 14 ++- packages/rust/tests/cli_e2e.rs | 45 ++++--- packages/rust/tests/cli_integration.rs | 13 +- 9 files changed, 249 insertions(+), 29 deletions(-) create mode 100644 packages/rust/changelog.d/2026-10-11-frontend-state-catalog.md create mode 100644 packages/rust/migrations.d/2026-10-11-frontend-state-catalog.md diff --git a/PARITY.md b/PARITY.md index afe474f0f..827422755 100644 --- a/PARITY.md +++ b/PARITY.md @@ -366,8 +366,9 @@ for await (const event of db.watch()) { ... } `dirsql server --frontend` is a shared Rust CLI option available through the Rust binary and the Python and TypeScript launchers. It serves the embedded -SQL explorer shell at `/` on the API's origin and exposes its load diagnostic -through `GET /frontend/state` (HTTP 503 when unavailable). The page needs no +SQL explorer shell at `/` on the API's origin and exposes its load diagnostic, +query root, and declared tables and columns through `GET /frontend/state` +(HTTP 503 when unavailable). The page needs no runtime asset download; the option adds no SDK binding API. Without the flag, the existing HTTP routes are unchanged. Query controls and distribution validation are tracked in the subsequent frontend issues. diff --git a/docs/reference/cli.md b/docs/reference/cli.md index e0a4383c0..8b6b938b0 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -248,10 +248,32 @@ dirsql server --frontend dirsql server --frontend -c .dirsql.toml ``` -The shell reads `GET /frontend/state`: `{"error":null}` when the index is -ready, or HTTP `503` with `{"error":""}` when config loading -fails. Fix the config and restart the server. Without `--frontend`, the `/` -and `/frontend/state` routes return `404`. +The shell reads `GET /frontend/state`. When the index is ready it returns +HTTP `200`: + +```json +{ + "error": null, + "root": "/abs/path/of/the/invocation/directory", + "tables": [ + {"name": "posts", "columns": [{"name": "basename", "type": "TEXT"}]} + ] +} +``` + +`root` is the query root, where [path-tables](./path-tables.md) resolve. +`tables` lists the declared tables, ordered by name, each with its columns in +declaration order and the declared SQLite type (`""` when untyped). Empty +tables are listed. A configless server, or a config with only functions or +extensions, returns `"tables": []`. dirsql's bookkeeping tables, SQLite's own +tables, virtual tables with their shadow tables, and path-tables are never +listed. The metadata is read from SQLite's catalog: no file is parsed, no +directory walked, no table counted. + +When config loading fails the endpoint returns HTTP `503` with +`{"error":""}` and no `root` or `tables`. Fix the config and +restart the server. Without `--frontend`, the `/` and `/frontend/state` +routes return `404`. `POST /query` is unchanged. ### Flags diff --git a/packages/rust/changelog.d/2026-10-11-frontend-state-catalog.md b/packages/rust/changelog.d/2026-10-11-frontend-state-catalog.md new file mode 100644 index 000000000..e743c8469 --- /dev/null +++ b/packages/rust/changelog.d/2026-10-11-frontend-state-catalog.md @@ -0,0 +1 @@ +**Added** `GET /frontend/state` (with `dirsql server --frontend`) reports the query root and the declared tables with their columns, plus `DirSQL::root()` and `DirSQL::tables()` in the Rust core. diff --git a/packages/rust/migrations.d/2026-10-11-frontend-state-catalog.md b/packages/rust/migrations.d/2026-10-11-frontend-state-catalog.md new file mode 100644 index 000000000..9da161cb0 --- /dev/null +++ b/packages/rust/migrations.d/2026-10-11-frontend-state-catalog.md @@ -0,0 +1,21 @@ +### Frontend state reports the query root and declared tables + +#### Summary + +The ready response of `GET /frontend/state` (served only with `dirsql server --frontend`) gains `root` and `tables` next to `error`. The Rust core gains `DirSQL::root()` and `DirSQL::tables()`. Nothing breaks. + +#### Required changes + +_None._ + +#### Deprecations removed + +_None._ + +#### Behavior changes without code changes + +A ready `GET /frontend/state` body is now `{"error": null, "root": "...", "tables": [...]}` rather than `{"error": null}`. `POST /query` is unchanged. + +#### Verification + +Run `dirsql server --frontend --port 0`, then `curl frontend/state`. The body lists `root` and `tables`. diff --git a/packages/rust/src/cli/router.rs b/packages/rust/src/cli/router.rs index 327a7ce40..d71694b98 100644 --- a/packages/rust/src/cli/router.rs +++ b/packages/rust/src/cli/router.rs @@ -22,6 +22,7 @@ use tokio_stream::wrappers::BroadcastStream; use super::AppState; use super::execute::{QueryFailure, execute_query, require_ready}; +use crate::TableInfo; pub(super) struct AppContext { pub state: AppState, @@ -53,7 +54,15 @@ pub(super) fn router(ctx: SharedCtx, frontend: bool) -> Router { async fn handle_frontend_state(State(ctx): State) -> Response { match &ctx.state { - AppState::Ready(_) => Json(json!({ "error": null })).into_response(), + AppState::Ready(db) => match db.tables() { + Ok(tables) => Json(json!({ + "error": null, + "root": db.root().to_string_lossy(), + "tables": tables.iter().map(table_json).collect::>(), + })) + .into_response(), + Err(err) => error_response(StatusCode::INTERNAL_SERVER_ERROR, err.to_string()), + }, AppState::Unavailable(reason) => ( StatusCode::SERVICE_UNAVAILABLE, Json(json!({ "error": reason })), @@ -62,6 +71,17 @@ async fn handle_frontend_state(State(ctx): State) -> Response { } } +fn table_json(table: &TableInfo) -> serde_json::Value { + json!({ + "name": table.name, + "columns": table + .columns + .iter() + .map(|c| json!({ "name": c.name, "type": c.sql_type })) + .collect::>(), + }) +} + async fn handle_query(State(ctx): State, body: String) -> Response { match execute_query(&ctx.state, body, Some(ctx.query_timeout)).await { Ok(value) => Json(value).into_response(), @@ -215,6 +235,21 @@ mod tests { assert_eq!(resp.status(), StatusCode::SERVICE_UNAVAILABLE); } + #[test] + fn table_json_names_each_column_with_its_type() { + let table = TableInfo { + name: "t".into(), + columns: vec![crate::ColumnInfo { + name: "a".into(), + sql_type: "TEXT".into(), + }], + }; + assert_eq!( + table_json(&table), + json!({"name": "t", "columns": [{"name": "a", "type": "TEXT"}]}) + ); + } + #[test] fn error_response_sets_json_content_type() { let resp = error_response(StatusCode::BAD_REQUEST, "boom"); diff --git a/packages/rust/src/db.rs b/packages/rust/src/db.rs index ba2fff932..001062279 100644 --- a/packages/rust/src/db.rs +++ b/packages/rust/src/db.rs @@ -326,6 +326,27 @@ pub struct QueryResult { pub rows: Vec>, } +/// A column of a user table as SQLite's catalog reports it. +#[derive(Debug, Clone, PartialEq)] +pub struct ColumnInfo { + pub name: String, + pub sql_type: String, +} + +/// A user table and its columns in declaration order. +#[derive(Debug, Clone, PartialEq)] +pub struct TableInfo { + pub name: String, + pub columns: Vec, +} + +const USER_TABLES: &str = "SELECT name FROM pragma_table_list WHERE schema = 'main' \ + AND type = 'table' AND name NOT LIKE 'sqlite\\_%' ESCAPE '\\' \ + AND name NOT LIKE '\\_dirsql\\_%' ESCAPE '\\' ORDER BY name"; + +const TABLE_COLUMNS: &str = "SELECT name, type FROM pragma_table_xinfo(?1, 'main') \ + WHERE hidden <> 1 ORDER BY cid"; + pub struct Db { conn: Connection, /// Where this connection's path tables hold their rows for the statement in @@ -553,6 +574,34 @@ impl Db { Ok(columns) } + /// The plain tables in the persistent schema with their columns, read from + /// SQLite's catalog alone: no table is scanned and no virtual table is + /// opened. dirsql's bookkeeping tables, `sqlite_` tables, virtual tables + /// (and their shadow tables) and the per-connection path-tables are left + /// out. + pub fn user_tables(&self) -> Result> { + let names = self + .conn + .prepare(USER_TABLES)? + .query_map([], |row| row.get::<_, String>(0))? + .collect::>>()?; + let mut columns = self.conn.prepare(TABLE_COLUMNS)?; + names + .into_iter() + .map(|name| { + let columns = columns + .query_map([&name], |row| { + Ok(ColumnInfo { + name: row.get(0)?, + sql_type: row.get(1)?, + }) + })? + .collect::>>()?; + Ok(TableInfo { name, columns }) + }) + .collect() + } + /// Normalize a row to match the table schema. /// /// In relaxed mode (strict=false): extra keys are dropped, missing keys become NULL. @@ -1860,6 +1909,69 @@ mod tests { assert_eq!(results[1]["name"], Value::Text("cherry".into())); } + #[test] + fn user_tables_lists_plain_tables_with_columns_in_name_order() { + let db = Db::new().unwrap(); + db.create_table("zeta", "CREATE TABLE zeta (a TEXT, \"two words\" INTEGER)") + .unwrap(); + db.create_table("_alpha", "CREATE TABLE _alpha (x)") + .unwrap(); + let col = |name: &str, sql_type: &str| ColumnInfo { + name: name.into(), + sql_type: sql_type.into(), + }; + assert_eq!( + db.user_tables().unwrap(), + vec![ + TableInfo { + name: "_alpha".into(), + columns: vec![col("x", "")], + }, + TableInfo { + name: "zeta".into(), + columns: vec![col("a", "TEXT"), col("two words", "INTEGER")], + }, + ] + ); + } + + #[test] + fn user_tables_is_empty_for_a_fresh_db() { + assert_eq!(Db::new().unwrap().user_tables().unwrap(), vec![]); + } + + #[test] + fn user_tables_skips_virtual_shadow_sqlite_and_temp_tables() { + let db = Db::new().unwrap(); + db.create_table( + "notes", + "CREATE TABLE notes (body TEXT); \ + CREATE VIRTUAL TABLE notes_fts USING fts5(body); \ + CREATE TABLE seq (id INTEGER PRIMARY KEY AUTOINCREMENT); \ + CREATE TEMP TABLE scratch (y);", + ) + .unwrap(); + let names: Vec = db + .user_tables() + .unwrap() + .into_iter() + .map(|t| t.name) + .collect(); + assert_eq!(names, vec!["notes", "seq"]); + } + + #[test] + fn user_tables_keeps_generated_columns() { + let db = Db::new().unwrap(); + db.create_table( + "g", + "CREATE TABLE g (a INTEGER, b INTEGER GENERATED ALWAYS AS (a + 1) VIRTUAL)", + ) + .unwrap(); + let columns = db.user_tables().unwrap().remove(0).columns; + assert_eq!(columns.len(), 2); + } + #[test] fn get_table_columns_returns_user_columns_only() { let db = Db::new().unwrap(); diff --git a/packages/rust/src/lib.rs b/packages/rust/src/lib.rs index 17d37e1d0..cd006044c 100644 --- a/packages/rust/src/lib.rs +++ b/packages/rust/src/lib.rs @@ -80,7 +80,7 @@ use std::time::Duration; use thiserror::Error; pub use crate::config::ExtensionSpec as Extension; -pub use crate::db::{DbError, QueryResult, Value}; +pub use crate::db::{ColumnInfo, DbError, QueryResult, TableInfo, Value}; pub use crate::differ::RowEvent; #[doc(hidden)] pub use crate::row_event_flat::{FlatRowEvent, flatten_row_event}; @@ -558,6 +558,18 @@ impl DirSQL { db.query_ordered(sql).map_err(map_db_error) } + /// The directory the index is rooted at: where path-tables resolve. + pub fn root(&self) -> &Path { + &self.inner.root + } + + /// The tables a query can read by name, with their columns, straight from + /// SQLite's catalog: empty tables included, no file read or table scanned. + pub fn tables(&self) -> Result> { + let db = self.inner.db.lock().map_err(DirSqlError::lock)?; + db.user_tables().map_err(map_db_error) + } + /// [`query_ordered`](Self::query_ordered) folding each row's cells, in /// projection order, into the state `start` makes from the projection, /// as they are read rather than collected first. diff --git a/packages/rust/tests/cli_e2e.rs b/packages/rust/tests/cli_e2e.rs index e5662b7f6..c38d13a71 100644 --- a/packages/rust/tests/cli_e2e.rs +++ b/packages/rust/tests/cli_e2e.rs @@ -147,8 +147,21 @@ fn kill_and_wait(mut child: ServerGuard) { let _ = child.wait(); } -fn canonical_root(path: &std::path::Path) -> String { - path.canonicalize().unwrap().to_string_lossy().into_owned() +fn assert_state_without_root(client: &Client, base: &str, dir: &std::path::Path, expected: Value) { + let mut state = client + .get(format!("{base}/frontend/state")) + .send() + .unwrap() + .json::() + .unwrap(); + let root = state.as_object_mut().unwrap().remove("root").unwrap(); + assert_eq!( + std::path::Path::new(root.as_str().unwrap()) + .canonicalize() + .unwrap(), + dir.canonicalize().unwrap() + ); + assert_eq!(state, expected); } #[test] @@ -184,14 +197,11 @@ fn frontend_is_opt_in_and_serves_a_bundled_page() { .starts_with("text/html") ); assert!(page.text().unwrap().contains("dirsql SQL explorer")); - assert_eq!( - client - .get(format!("{base}/frontend/state")) - .send() - .unwrap() - .json::() - .unwrap(), - json!({"error": null, "root": canonical_root(root.path()), "tables": []}) + assert_state_without_root( + &client, + &base, + root.path(), + json!({"error": null, "tables": []}), ); assert_eq!( client @@ -257,19 +267,16 @@ fn frontend_loads_with_an_explicit_valid_config() { let client = Client::new(); let base = format!("http://localhost:{port}"); assert_eq!(client.get(&base).send().unwrap().status(), StatusCode::OK); - assert_eq!( - client - .get(format!("{base}/frontend/state")) - .send() - .unwrap() - .json::() - .unwrap(), - json!({"error": null, "root": canonical_root(root.path()), "tables": [ + assert_state_without_root( + &client, + &base, + root.path(), + json!({"error": null, "tables": [ {"name": "posts", "columns": [ {"name": "basename", "type": "TEXT"}, {"name": "size", "type": "INTEGER"} ]} - ]}) + ]}), ); assert_eq!( client diff --git a/packages/rust/tests/cli_integration.rs b/packages/rust/tests/cli_integration.rs index f00f8c65f..82549cda2 100644 --- a/packages/rust/tests/cli_integration.rs +++ b/packages/rust/tests/cli_integration.rs @@ -647,6 +647,15 @@ async fn frontend_state(db: DirSQL) -> (ServerHandle, JsonValue) { (handle, body) } +fn assert_same_dir(reported: &JsonValue, dir: &std::path::Path) { + assert_eq!( + std::path::Path::new(reported.as_str().unwrap()) + .canonicalize() + .unwrap(), + dir.canonicalize().unwrap() + ); +} + fn db_from_config(config: &str) -> (TempDir, DirSQL) { let root = TempDir::new().unwrap(); fs::write(root.path().join(".dirsql.toml"), config).unwrap(); @@ -664,7 +673,7 @@ async fn frontend_state_reports_the_query_root_and_declared_columns() { let (handle, body) = frontend_state(db).await; assert_eq!(body["error"], JsonValue::Null); - assert_eq!(body["root"], json!(root.path().to_string_lossy())); + assert_same_dir(&body["root"], root.path()); assert_eq!( body["tables"], json!([{"name": "posts", "columns": [ @@ -739,7 +748,7 @@ async fn frontend_state_has_no_tables_without_a_config() { let (handle, body) = frontend_state(db).await; assert_eq!(body["tables"], json!([])); - assert_eq!(body["root"], json!(root.path().to_string_lossy())); + assert_same_dir(&body["root"], root.path()); handle.shutdown().await.unwrap(); } From f29b09af9e453dfde8f98ea78649eb0efda530d0 Mon Sep 17 00:00:00 2001 From: thekevinbot <248217931+thekevinbot@users.noreply.github.com> Date: Sun, 11 Oct 2026 20:25:12 -0400 Subject: [PATCH 3/5] e2e attestation for bot/1488-frontend-metadata-1011 --- .../e2e-attestations/bot-1488-frontend-metadata-1011.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 packages/python/e2e-attestations/bot-1488-frontend-metadata-1011.json diff --git a/packages/python/e2e-attestations/bot-1488-frontend-metadata-1011.json b/packages/python/e2e-attestations/bot-1488-frontend-metadata-1011.json new file mode 100644 index 000000000..a2ab2b592 --- /dev/null +++ b/packages/python/e2e-attestations/bot-1488-frontend-metadata-1011.json @@ -0,0 +1,7 @@ +{ + "command": "uv run python -m pytest tests/e2e/cli_version_test.py tests/e2e/query_contract_test.py tests/e2e/declared_table_name_test.py tests/e2e/on_file_query_test.py -x -q", + "ran_at": 1791764712, + "exit_code": 0, + "commit": "ae61a26f7b9018c3e12aebeea7c6a3cf32ef4d87", + "branch": "bot/1488-frontend-metadata-1011" +} From f0c983bde182454c33e079985ab22872af6f45bd Mon Sep 17 00:00:00 2001 From: thekevinbot <248217931+thekevinbot@users.noreply.github.com> Date: Sun, 11 Oct 2026 20:26:23 -0400 Subject: [PATCH 4/5] e2e attestation for bot/1488-frontend-metadata-1011 --- .../e2e-attestations/bot-1488-frontend-metadata-1011.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 packages/ts/e2e-attestations/bot-1488-frontend-metadata-1011.json diff --git a/packages/ts/e2e-attestations/bot-1488-frontend-metadata-1011.json b/packages/ts/e2e-attestations/bot-1488-frontend-metadata-1011.json new file mode 100644 index 000000000..b4e07afdc --- /dev/null +++ b/packages/ts/e2e-attestations/bot-1488-frontend-metadata-1011.json @@ -0,0 +1,7 @@ +{ + "command": "pnpm test:e2e", + "ran_at": 1791764783, + "exit_code": 0, + "commit": "f29b09af9e453dfde8f98ea78649eb0efda530d0", + "branch": "bot/1488-frontend-metadata-1011" +} From 2765202e7f9e3ed66fa4cce7e488fcf0de3a002b Mon Sep 17 00:00:00 2001 From: thekevinbot <248217931+thekevinbot@users.noreply.github.com> Date: Sun, 11 Oct 2026 20:59:59 -0400 Subject: [PATCH 5/5] test: cover DirSQL::tables and root --- packages/rust/src/lib.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/rust/src/lib.rs b/packages/rust/src/lib.rs index cd006044c..601a64737 100644 --- a/packages/rust/src/lib.rs +++ b/packages/rust/src/lib.rs @@ -4296,6 +4296,26 @@ mod internal_tests { assert!(dbg.contains("boom"), "got: {dbg}"); } + #[test] + fn tables_lists_declared_tables_and_root_reports_the_index_root() { + let dir = TempDir::new().unwrap(); + let db = DirSQL::builder() + .root(dir.path()) + .table(Table::new( + "b", + "CREATE TABLE b (y TEXT)", + "*.b", + |_| vec![], + )) + .build() + .unwrap(); + let tables = db.tables().unwrap(); + assert_eq!(tables.len(), 1); + assert_eq!(tables[0].name, "b"); + assert_eq!(tables[0].columns[0].name, "y"); + assert_eq!(db.root(), dir.path()); + } + #[test] fn builder_setters_configure_and_build() { let dir = TempDir::new().unwrap();