Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"command": "just test-e2e",
"ran_at": 1790603833,
"exit_code": 0,
"commit": "6b84d84d60179482a842b0db205ee257f8e03c11",
"branch": "claude/1186-win-test-harness"
}
22 changes: 9 additions & 13 deletions packages/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4346,15 +4346,13 @@ mod internal_tests {
}

/// `{abspath}` is not in the substitution table: it is left literal like any
/// unknown `{…}`, so `printf` receives the string `{abspath}` verbatim. The
/// template references `{path}` so that arg is the real path (and no path is
/// appended), isolating the `{abspath}` behavior in the `q` column.
/// unknown `{…}`, so `printf` receives the string `{abspath}` verbatim.
#[test]
fn run_on_file_does_not_substitute_abspath() {
let dir = TempDir::new().unwrap();
let abs = dir.path().join("f.txt");
let rows = run_on_file(
r#"printf '[{"p":"%s","q":"%s"}]' {path} {abspath}"#,
r#"printf '[{"q":"%s"}]' {abspath}"#,
&abs.to_string_lossy(),
dir.path(),
dir.path(),
Expand All @@ -4365,25 +4363,23 @@ mod internal_tests {
}

/// `{path}` interpolates the matched file's **absolute** path (not a
/// root-relative one). The command echoes its `{path}` argument back as a
/// row value, and we assert it is byte-for-byte the absolute path even when
/// the file sits directly under `root` (the case the old `strip_prefix`
/// would have shortened to a bare relative path).
/// root-relative one), byte-for-byte, even when the file sits directly
/// under `root`. The command writes its argument to a side file rather than
/// into the JSON payload, where a Windows path's `\` would need escaping.
#[test]
fn run_on_file_passes_absolute_path_for_path_placeholder() {
let dir = TempDir::new().unwrap();
let abs = dir.path().join("f.txt");
let rows = run_on_file(
r#"sh -c "printf '[{\"p\":\"%s\"}]' \"$1\"" sh {path}"#,
run_on_file(
r#"sh -c 'printf %s "$1" > seen; echo "[]"' sh {path}"#,
&abs.to_string_lossy(),
dir.path(),
dir.path(),
)
.expect("a well-formed payload parses");
assert_eq!(rows.len(), 1);
assert_eq!(
rows[0]["p"],
Value::Text(abs.to_string_lossy().into_owned())
std::fs::read_to_string(dir.path().join("seen")).unwrap(),
abs.to_string_lossy()
);
}

Expand Down
9 changes: 6 additions & 3 deletions packages/rust/src/parsed_vtab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,9 +660,12 @@ mod tests {

#[test]
fn run_parser_substitutes_the_absolute_path_and_the_root() {
let payload =
run_parser("echo {path} {root}", Path::new("/tmp"), Path::new("a.json")).unwrap();
assert_eq!(payload, "/tmp/a.json /tmp");
let root = std::env::temp_dir();
let payload = run_parser("echo {path} {root}", &root, Path::new("a.json")).unwrap();
assert_eq!(
payload,
format!("{} {}", root.join("a.json").display(), root.display())
);
}

#[test]
Expand Down
8 changes: 4 additions & 4 deletions packages/rust/tests/cli_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fn blog_fixture() -> TempDir {
name = "posts"
ddl = "CREATE TABLE posts (basename TEXT, size INTEGER)"
glob = "posts/*/*.json"
on-file = '''sh -c 'base=${1##*/}; size=$(wc -c < "$1" | tr -d " "); printf "[{\"basename\":\"%s\",\"size\":%s}]" "$base" "$size"' sh {path}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); base=${p##*/}; size=$(wc -c < "$1" | tr -d " "); printf "[{\"basename\":\"%s\",\"size\":%s}]" "$base" "$size"' sh {path}'''
"#,
)
.unwrap();
Expand All @@ -69,7 +69,7 @@ fn quoted_blog_fixture() -> TempDir {
name = "posts"
ddl = 'CREATE TABLE "posts" (basename TEXT)'
glob = "posts/*/*.json"
on-file = '''sh -c 'printf "[{\"basename\":\"%s\"}]" "${1##*/}"' sh {path}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); printf "[{\"basename\":\"%s\"}]" "${p##*/}"' sh {path}'''
"#,
)
.unwrap();
Expand Down Expand Up @@ -973,13 +973,13 @@ fn query_subcommand_fans_out_file_to_overlapping_tables() {
name = "ta"
ddl = "CREATE TABLE ta (path TEXT)"
glob = "data/*/metadata.json"
on-file = '''sh -c 'rel=${1#"$2"/}; printf "[{\"path\":\"%s\"}]" "$rel"' sh {path} {root}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); r=$(printf %s "$2" | tr "\\\\" /); rel=${p#"$r"/}; printf "[{\"path\":\"%s\"}]" "$rel"' sh {path} {root}'''

[[table]]
name = "tb"
ddl = "CREATE TABLE tb (path TEXT)"
glob = "data/**/metadata.json"
on-file = '''sh -c 'rel=${1#"$2"/}; printf "[{\"path\":\"%s\"}]" "$rel"' sh {path} {root}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); r=$(printf %s "$2" | tr "\\\\" /); rel=${p#"$r"/}; printf "[{\"path\":\"%s\"}]" "$rel"' sh {path} {root}'''
"#,
)
.unwrap();
Expand Down
2 changes: 1 addition & 1 deletion packages/rust/tests/cli_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ fn blog_fixture() -> (TempDir, DirSQL) {
name = "posts"
ddl = "CREATE TABLE posts (basename TEXT, size INTEGER)"
glob = "posts/*/*.json"
on-file = '''sh -c 'base=${1##*/}; size=$(wc -c < "$1" | tr -d " "); printf "[{\"basename\":\"%s\",\"size\":%s}]" "$base" "$size"' sh {path}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); base=${p##*/}; size=$(wc -c < "$1" | tr -d " "); printf "[{\"basename\":\"%s\",\"size\":%s}]" "$base" "$size"' sh {path}'''
"#,
)
.unwrap();
Expand Down
4 changes: 2 additions & 2 deletions packages/rust/tests/config_root_derivation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ fn config_without_root_indexes_process_cwd_not_config_parent() {
name = "files"
ddl = "CREATE TABLE files (path TEXT)"
glob = "*.txt"
on-file = '''sh -c 'rel=${1#"$2"/}; printf "[{\"path\":\"%s\"}]" "$rel"' sh {path} {root}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); r=$(printf %s "$2" | tr "\\\\" /); rel=${p#"$r"/}; printf "[{\"path\":\"%s\"}]" "$rel"' sh {path} {root}'''
"#,
)
.unwrap();
Expand Down Expand Up @@ -90,7 +90,7 @@ fn explicit_root_wins_over_cwd() {
name = "files"
ddl = "CREATE TABLE files (path TEXT)"
glob = "*.txt"
on-file = '''sh -c 'rel=${1#"$2"/}; printf "[{\"path\":\"%s\"}]" "$rel"' sh {path} {root}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); r=$(printf %s "$2" | tr "\\\\" /); rel=${p#"$r"/}; printf "[{\"path\":\"%s\"}]" "$rel"' sh {path} {root}'''
"#,
)
.unwrap();
Expand Down
2 changes: 1 addition & 1 deletion packages/rust/tests/declared_table_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::fs;
use tempfile::TempDir;

/// An `on-file` hook emitting the file's root-relative `path`.
const HOOK: &str = r#"on-file = '''sh -c 'rel=${1#"$2"/}; printf "[{\"path\":\"%s\"}]" "$rel"' sh {path} {root}'''"#;
const HOOK: &str = r#"on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); r=$(printf %s "$2" | tr "\\\\" /); rel=${p#"$r"/}; printf "[{\"path\":\"%s\"}]" "$rel"' sh {path} {root}'''"#;

/// A tempdir with one matched file and a `.dirsql.toml` holding `config`.
fn fixture(config: &str) -> TempDir {
Expand Down
4 changes: 2 additions & 2 deletions packages/rust/tests/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ fn loads_real_extension_and_calls_registered_function() {
format!(
r#"
[[dirsql.extension]]
path = "{}"
path = {}
entrypoint = "sqlite3_extension_init"

[[table]]
Expand All @@ -122,7 +122,7 @@ ddl = "CREATE TABLE files (path TEXT)"
glob = "*.txt"
on-file = "cat {{path}}"
"#,
ext.display(),
toml::Value::String(ext.display().to_string()),
),
)
.unwrap();
Expand Down
14 changes: 7 additions & 7 deletions packages/rust/tests/from_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn from_config_produces_one_row_per_matched_file() {
name = "files"
ddl = "CREATE TABLE files (path TEXT, basename TEXT)"
glob = "data/*.csv"
on-file = '''sh -c 'rel=${1#"$2"/}; base=${1##*/}; printf "[{\"path\":\"%s\",\"basename\":\"%s\"}]" "$rel" "$base"' sh {path} {root}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); r=$(printf %s "$2" | tr "\\\\" /); rel=${p#"$r"/}; base=${p##*/}; printf "[{\"path\":\"%s\",\"basename\":\"%s\"}]" "$rel" "$base"' sh {path} {root}'''
"#,
)
.unwrap();
Expand Down Expand Up @@ -57,7 +57,7 @@ ignore = ["ignored/**"]
name = "files"
ddl = "CREATE TABLE files (path TEXT)"
glob = "**/*.csv"
on-file = '''sh -c 'rel=${1#"$2"/}; printf "[{\"path\":\"%s\"}]" "$rel"' sh {path} {root}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); r=$(printf %s "$2" | tr "\\\\" /); rel=${p#"$r"/}; printf "[{\"path\":\"%s\"}]" "$rel"' sh {path} {root}'''
"#,
)
.unwrap();
Expand Down Expand Up @@ -143,7 +143,7 @@ fn from_config_capture_placeholder_without_column_still_matches() {
name = "comments"
ddl = "CREATE TABLE comments (path TEXT, basename TEXT)"
glob = "_comments/{thread_id}/*.txt"
on-file = '''sh -c 'rel=${1#"$2"/}; base=${1##*/}; printf "[{\"path\":\"%s\",\"basename\":\"%s\"}]" "$rel" "$base"' sh {path} {root}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); r=$(printf %s "$2" | tr "\\\\" /); rel=${p#"$r"/}; base=${p##*/}; printf "[{\"path\":\"%s\",\"basename\":\"%s\"}]" "$rel" "$base"' sh {path} {root}'''
"#,
)
.unwrap();
Expand Down Expand Up @@ -196,7 +196,7 @@ fn from_config_exposes_stat_virtuals() {
name = "files"
ddl = "CREATE TABLE files (path TEXT, basename TEXT, dir TEXT, ext TEXT, size INTEGER, mtime INTEGER)"
glob = "docs/*.md"
on-file = '''sh -c 'rel=${1#"$2"/}; base=${1##*/}; case "$rel" in */*) dir=${rel%/*};; *) dir="";; esac; ext=${base##*.}; [ "$ext" = "$base" ] && ext=""; size=$(wc -c < "$1" | tr -d " "); mtime=$(stat -c %Y "$1"); printf "[{\"path\":\"%s\",\"basename\":\"%s\",\"dir\":\"%s\",\"ext\":\"%s\",\"size\":%s,\"mtime\":%s}]" "$rel" "$base" "$dir" "$ext" "$size" "$mtime"' sh {path} {root}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); r=$(printf %s "$2" | tr "\\\\" /); rel=${p#"$r"/}; base=${p##*/}; case "$rel" in */*) dir=${rel%/*};; *) dir="";; esac; ext=${base##*.}; [ "$ext" = "$base" ] && ext=""; size=$(wc -c < "$1" | tr -d " "); mtime=$(stat -c %Y "$1"); printf "[{\"path\":\"%s\",\"basename\":\"%s\",\"dir\":\"%s\",\"ext\":\"%s\",\"size\":%s,\"mtime\":%s}]" "$rel" "$base" "$dir" "$ext" "$size" "$mtime"' sh {path} {root}'''
"#,
)
.unwrap();
Expand Down Expand Up @@ -243,7 +243,7 @@ fn from_config_undeclared_stat_columns_are_silently_dropped() {
name = "minimal"
ddl = "CREATE TABLE minimal (path TEXT)"
glob = "*.txt"
on-file = '''sh -c 'rel=${1#"$2"/}; base=${1##*/}; printf "[{\"path\":\"%s\",\"basename\":\"%s\"}]" "$rel" "$base"' sh {path} {root}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); r=$(printf %s "$2" | tr "\\\\" /); rel=${p#"$r"/}; base=${p##*/}; printf "[{\"path\":\"%s\",\"basename\":\"%s\"}]" "$rel" "$base"' sh {path} {root}'''
"#,
)
.unwrap();
Expand Down Expand Up @@ -377,7 +377,7 @@ name = "files"
ddl = "CREATE TABLE files (path TEXT)"
glob = "*.csv"
strict = true
on-file = '''sh -c 'rel=${1#"$2"/}; printf "[{\"path\":\"%s\"}]" "$rel"' sh {path} {root}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); r=$(printf %s "$2" | tr "\\\\" /); rel=${p#"$r"/}; printf "[{\"path\":\"%s\"}]" "$rel"' sh {path} {root}'''
"#,
)
.unwrap();
Expand All @@ -403,7 +403,7 @@ async fn async_from_config_works() {
name = "files"
ddl = "CREATE TABLE files (path TEXT, basename TEXT)"
glob = "*.csv"
on-file = '''sh -c 'rel=${1#"$2"/}; base=${1##*/}; printf "[{\"path\":\"%s\",\"basename\":\"%s\"}]" "$rel" "$base"' sh {path} {root}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); r=$(printf %s "$2" | tr "\\\\" /); rel=${p#"$r"/}; base=${p##*/}; printf "[{\"path\":\"%s\",\"basename\":\"%s\"}]" "$rel" "$base"' sh {path} {root}'''
"#,
)
.unwrap();
Expand Down
24 changes: 16 additions & 8 deletions packages/rust/tests/persist_parsed_path_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ impl Fixture {
fs::write(&script, format!("printf x >> \"$2\"\n{body}\n")).unwrap();
format!(
"sh {} {{path}} {}",
script.display(),
self.counter.display()
shell_word(&script),
shell_word(&self.counter)
)
}

Expand Down Expand Up @@ -120,18 +120,29 @@ impl Fixture {
}
}

fn shell_word(path: &Path) -> String {
shlex::try_quote(&path.to_string_lossy())
.unwrap()
.into_owned()
}

fn payload(i: usize, tag: &str) -> String {
format!(
"[{{\"id\":{i},\"tag\":\"{tag}\",\"body\":\"{}\"}}]",
"x".repeat(120 + i % 17)
)
}

/// Windows refuses `set_modified` on a handle opened read-only.
fn writable(path: &Path) -> fs::File {
fs::OpenOptions::new().write(true).open(path).unwrap()
}

/// Push a file's mtime clear of the racy window so the stat tuple alone
/// decides whether it changed.
fn touch_into_the_future(path: &Path) {
let future = SystemTime::now() + Duration::from_secs(5);
fs::File::open(path).unwrap().set_modified(future).unwrap();
writable(path).set_modified(future).unwrap();
}

/// Pin a file's mtime far enough ahead that every cache written during the
Expand All @@ -140,7 +151,7 @@ fn touch_into_the_future(path: &Path) {
/// changed and the reuse decision falls through to the content hash.
fn pin_inside_the_racy_window(path: &Path) -> SystemTime {
let pinned = SystemTime::now() + Duration::from_secs(3600);
fs::File::open(path).unwrap().set_modified(pinned).unwrap();
writable(path).set_modified(pinned).unwrap();
pinned
}

Expand Down Expand Up @@ -319,10 +330,7 @@ fn content_that_leaves_the_stat_tuple_untouched_is_caught_by_the_hash() {
// `payload` is the same length for either tag, so rewriting in place leaves
// size, inode and device alone; restoring the mtime leaves the rest.
fs::write(&edited, payload(3, "v2")).unwrap();
fs::File::open(&edited)
.unwrap()
.set_modified(pinned)
.unwrap();
writable(&edited).set_modified(pinned).unwrap();

let after = fs::metadata(&edited).unwrap();
assert_eq!(
Expand Down
2 changes: 1 addition & 1 deletion packages/rust/tests/repeatable_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn table_config(name: &str) -> String {
name = "{name}"
ddl = "CREATE TABLE {name} (basename TEXT)"
glob = "*.json"
on-file = '''sh -c 'printf "[{{\"basename\":\"%s\"}}]" "${{1##*/}}"' sh {{path}}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); printf "[{{\"basename\":\"%s\"}}]" "${{p##*/}}"' sh {{path}}'''
"#
)
}
Expand Down
2 changes: 1 addition & 1 deletion packages/rust/tests/sdk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ fn builder_explicit_root_wins_over_config_directory() {
name = "items"
ddl = "CREATE TABLE items (basename TEXT)"
glob = "*.json"
on-file = '''sh -c 'printf "[{\"basename\":\"%s\"}]" "${1##*/}"' sh {path}'''
on-file = '''sh -c 'p=$(printf %s "$1" | tr "\\\\" /); printf "[{\"basename\":\"%s\"}]" "${p##*/}"' sh {path}'''
"#,
)
.unwrap();
Expand Down
12 changes: 8 additions & 4 deletions packages/rust/tests/watch_scan_correctness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use std::time::{Duration, Instant};
/// A table matching *every* path (like the default `files` table's `**/*`),
/// so a newly created subdirectory is a matcher candidate on the watch path.
fn files_table(root: &std::path::Path) -> Table {
let root = fs::canonicalize(root).unwrap_or_else(|_| root.to_path_buf());
let canonical = fs::canonicalize(root).unwrap_or_else(|_| root.to_path_buf());
let root = root.to_path_buf();
Table::new(
"files",
"CREATE TABLE files (name TEXT, path TEXT)",
Expand All @@ -22,10 +23,13 @@ fn files_table(root: &std::path::Path) -> Table {
let content = fs::read_to_string(path).unwrap_or_default();
let abs = std::path::Path::new(path);
let rel = abs
.strip_prefix(&root)
.strip_prefix(&canonical)
.or_else(|_| abs.strip_prefix(&root))
.unwrap_or(abs)
.to_string_lossy()
.into_owned();
.iter()
.map(|part| part.to_string_lossy())
.collect::<Vec<_>>()
.join("/");
vec![HashMap::from([
("name".to_string(), Value::Text(content.trim().to_string())),
("path".to_string(), Value::Text(rel)),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"command": "pnpm test:e2e",
"ran_at": 1790603978,
"exit_code": 0,
"commit": "3aa71ff81e229fd7f5da6536659b45fc7c54e559",
"branch": "claude/1186-win-test-harness"
}
Loading