Skip to content

Conversation

@findolor
Copy link
Collaborator

@findolor findolor commented Jan 27, 2026

Motivation

This change adds the ability to completely reset an in-browser SQLite database by wiping all OPFS storage and recreating the worker. This is useful for scenarios where users need a fresh database state without reloading the page.

Solution

Added a new wipe_and_recreate() async method to SQLiteWasmDatabase that:

  1. Terminates the existing worker
  2. Rejects any pending queries with "Database wipe in progress" error
  3. Resets the ready signal to pending state
  4. Deletes the OPFS sahpool directory containing persisted data
  5. Creates a new worker with fresh state
  6. Waits for the new worker to be ready

Supporting changes:

  • Added new opfs.rs module with OPFS directory deletion utilities
  • Added ReadySignal::reset() method to return signal to pending state
  • Added OpfsDeletionFailed error variant
  • Added web-sys features for OPFS access (StorageManager, FileSystemDirectoryHandle, etc.)
  • Changed worker field to Rc<RefCell<Worker>> to allow replacement
  • Added db_name field to store database name for worker recreation
  • Added comprehensive tests for the new functionality
  • Added demo UI button in svelte-test app

Checks

By submitting this for review, I'm confirming I've done the following:

  • made this PR as small as possible
  • unit-tested any new functionality
  • linked any relevant issues or PRs
  • included screenshots (if this involves a front-end change)

Summary by CodeRabbit

  • New Features

    • Added a “Wipe & Recreate DB” capability and UI button to fully reset and recreate the database during a session.
  • Bug Fixes / Reliability

    • Improved cleanup to remove on-disk OPFS state and recover embedded DB workers; readiness signaling now resets cleanly; clearer error reporting for OPFS deletion failures.
  • Tests

    • New integration tests for wipe-and-recreate, CRUD after reset, recovery, and consecutive wipes; some low-level WASM tests simplified.
  • Chores

    • Minor package/config updates, dependency version bump, and new .claude ignore entry.

✏️ Tip: You can customize this high-level summary in your review settings.

Features added: StorageManager, FileSystemDirectoryHandle,
FileSystemHandle, FileSystemRemoveOptions, FileSystemGetDirectoryOptions
Allows resetting the signal back to pending state for reuse
after wipe_and_recreate operations.
Provides delete_opfs_sahpool_directory() to remove the .opfs-sahpool
directory used by sqlite-wasm-rs for persistent storage.
Allows completely wiping and recreating the database:
- Terminates current worker
- Rejects pending queries
- Deletes OPFS storage
- Creates fresh worker
- Same instance remains valid after operation

Uses interior mutability (Rc<RefCell<Worker>>) to allow
bound callbacks to continue working.
- 8 integration tests covering data clearing, post-wipe operations,
  instance validity, and error recovery
- Wipe & Recreate button in demo app for manual testing
@findolor findolor self-assigned this Jan 27, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 27, 2026

Walkthrough

Refactors DB worker to use shared references and store db_name; adds wipe_and_recreate() which terminates the worker, rejects in-flight queries, resets readiness, deletes the OPFS ".opfs-sahpool" directory, and reinstalls a new worker; introduces OPFS cleanup module, ReadySignal.reset, tests, UI button, and dependency/package updates.

Changes

Cohort / File(s) Summary
Core DB logic
packages/sqlite-web/src/db.rs
worker changed to Rc<RefCell<Worker>>, added db_name: String, new public wipe_and_recreate() (wasm_export) that terminates worker, rejects pending queries, resets readiness, deletes OPFS sahpool, spawns & installs a new worker, and awaits readiness. Tests updated to exercise wipe behavior.
OPFS cleanup module
packages/sqlite-web/src/opfs.rs, packages/sqlite-web/src/lib.rs
New opfs.rs with pub async fn delete_opfs_sahpool_directory() and JS-interop helpers; mod opfs; added to lib.rs.
Ready signal
packages/sqlite-web/src/ready.rs
Added pub(crate) fn reset(&self) to clear handlers, reject previous readiness, recreate readiness promise, and set state to Pending. Includes tests for reset behavior.
Errors
packages/sqlite-web/src/errors.rs
Added OpfsDeletionFailed(String) variant to SQLiteWasmDatabaseError.
Tests trimmed/updated
packages/sqlite-web/src/tests.rs
Removed many wasm tests related to DB messaging/parameter normalization; retained error/display/worker-template tests.
Integration tests & UI
svelte-test/src/routes/+page.svelte, svelte-test/tests/integration/wipe-and-recreate.test.ts
Adds wipeAndRecreate() UI action and button; new integration test suite verifying wipe, recreate, CRUD, params, multiple wipes, and recovery scenarios.
Dependencies
Cargo.toml
Added web-sys features: StorageManager, FileSystemDirectoryHandle, FileSystemHandle, FileSystemRemoveOptions, FileSystemGetDirectoryOptions, DomException.
Package metadata & misc
pkg/package.json, svelte-test/package.json, .gitignore
Removed repository object from pkg/package.json; bumped @rainlanguage/sqlite-web in svelte-test to 0.0.1-alpha.20; added .claude to .gitignore.

Sequence Diagram

sequenceDiagram
    participant App as Svelte App
    participant DB as SQLiteWasmDatabase
    participant OPS as opfs module
    participant Worker as Embedded Worker
    participant OPFS as Browser OPFS

    App->>DB: wipe_and_recreate()
    DB->>Worker: worker.borrow().terminate()
    Worker-->>DB: terminated
    DB->>DB: reject pending queries
    DB->>DB: ready.reset()
    DB->>OPS: delete_opfs_sahpool_directory()
    OPS->>OPFS: navigator.storage.getDirectory() / get OPFS root
    OPFS-->>OPS: root handle
    OPS->>OPFS: get_directory_if_exists(".opfs-sahpool")
    OPFS-->>OPS: sahpool handle or NotFound
    OPS->>OPFS: delete_directory_contents(sahpool)
    OPFS-->>OPS: contents deleted
    OPS->>OPFS: removeEntry("sahpool")
    OPFS-->>OPS: removed
    OPS-->>DB: Ok(())
    DB->>DB: spawn embedded worker (new)
    DB->>Worker: install onmessage handler
    Worker-->>DB: ready message
    DB-->>App: Ok(())
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

codex

Suggested reviewers

  • hardyjosh
  • 0xgleb
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 65.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: adding a wipe_and_recreate method to SQLiteWasmDatabase, which is the primary feature introduced across the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@findolor findolor requested review from 0xgleb and hardyjosh January 27, 2026 07:50
@findolor
Copy link
Collaborator Author

Code review

Found 1 issue:

  1. Test ready_signal_reset_returns_to_pending violates promise lifecycle contract. At line 173-174, the test calls mark_ready() then expects wait_promise() to succeed. But mark_ready() removes the promise via self.promise.borrow_mut().take() (line 74), and existing test at lines 133-136 documents that "promise should be dropped after resolution". The fix is to get the promise BEFORE calling mark_ready().

signal.mark_ready();
let promise = signal.wait_promise().expect("promise exists after mark_ready");
wasm_bindgen_futures::JsFuture::from(promise)
.await
.expect("promise should resolve after mark_ready");
}

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@packages/sqlite-web/src/db.rs`:
- Around line 185-210: The wipe_and_recreate implementation resets the request
ID counter causing possible ID reuse between worker generations; instead of
setting *self.next_request_id.borrow_mut() = 1, leave the counter unchanged so
IDs remain monotonic (or advance it safely to a new high value) to avoid
collisions with stale replies from the terminated worker; update
wipe_and_recreate (and any code that initializes next_request_id) to stop
resetting next_request_id and ensure request ID generation logic in the code
that issues requests continues incrementing the existing counter (refer to
next_request_id, pending_queries, wipe_and_recreate, install_onmessage_handler,
and worker).

In `@packages/sqlite-web/src/opfs.rs`:
- Around line 60-75: The function get_directory_if_exists currently treats all
JS errors as "not found"; add the web-sys "DomException" feature in Cargo.toml
and change the error handling in get_directory_if_exists to detect DomException
and only treat a "NotFoundError" as Ok(None), while propagating other errors as
SQLiteWasmDatabaseError (preserving existing describe_js_value-based messages);
keep the creation options via FileSystemGetDirectoryOptions and return
handle.dyn_into().map(Some) on success, but on Err(e) use
e.dyn_ref::<web_sys::DomException>() to inspect .name() and return Ok(None) only
when name == "NotFoundError", otherwise convert the error into an appropriate
SQLiteWasmDatabaseError and return Err.

In `@packages/sqlite-web/src/ready.rs`:
- Around line 173-177: The test currently calls signal.mark_ready() before
retrieving the promise, but mark_ready() consumes/drops the internal promise so
signal.wait_promise() will error; fix by calling let promise =
signal.wait_promise().expect(...) before invoking signal.mark_ready(), then call
signal.mark_ready() and await the previously captured promise with
wasm_bindgen_futures::JsFuture::from(promise).await.expect(...); reference the
existing symbols signal.wait_promise(), signal.mark_ready(), and the promise
variable to locate where to reorder the operations.
- Around line 91-96: reset() currently replaces self.promise without notifying
any existing waiters; if InitializationState is Pending, call the existing
reject (or resolve) callback before dropping it so outstanding promises don't
hang. Specifically, in reset() check whether *self.state.borrow() ==
InitializationState::Pending and if so take() the stored reject (or resolve)
from self.reject/self.resolve and invoke it with a suitable JS error/message
(then drop those callbacks), then proceed to set state to Pending, replace the
promise via create_ready_promise(&self.resolve,&self.reject) and store it in
self.promise; ensure resolve/reject are cleared after invocation to avoid
double-calls.

Comment on lines 60 to 75
async fn get_directory_if_exists(
parent: &FileSystemDirectoryHandle,
name: &str,
) -> Result<Option<FileSystemDirectoryHandle>, SQLiteWasmDatabaseError> {
let options = FileSystemGetDirectoryOptions::new();
options.set_create(false);

match JsFuture::from(parent.get_directory_handle_with_options(name, &options)).await {
Ok(handle) => handle.dyn_into().map(Some).map_err(|e| {
SQLiteWasmDatabaseError::OpfsDeletionFailed(format!(
"directory handle type mismatch: {}",
describe_js_value(&e)
))
}),
Err(_) => Ok(None),
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and read the opfs.rs file
find . -name "opfs.rs" -type f

Repository: rainlanguage/sqlite-web

Length of output: 100


🏁 Script executed:

# Check the current content of the file around lines 60-75
cat -n packages/sqlite-web/src/opfs.rs | sed -n '50,85p'

Repository: rainlanguage/sqlite-web

Length of output: 1506


🏁 Script executed:

# Search for DomException usage in the codebase
rg "DomException" --type rust

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Look for similar error handling patterns with NotFoundError or dyn_ref
rg "NotFoundError\|dyn_ref.*DomException" --type rust -A 3

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Check the imports at the top of opfs.rs
head -30 packages/sqlite-web/src/opfs.rs

Repository: rainlanguage/sqlite-web

Length of output: 1123


🏁 Script executed:

# Check how get_directory_if_exists is used in the code
rg "get_directory_if_exists" --type rust -B 2 -A 2

Repository: rainlanguage/sqlite-web

Length of output: 665


🏁 Script executed:

# Look at the broader context - check if web_sys has DomException or similar
rg "web_sys::" packages/sqlite-web/src/opfs.rs

Repository: rainlanguage/sqlite-web

Length of output: 273


🏁 Script executed:

# Check what web_sys exports - look for any exception/error handling
grep -r "DomException\|Exception" packages/sqlite-web/Cargo.toml

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Check Cargo.toml to understand web_sys version
cat packages/sqlite-web/Cargo.toml | head -50

Repository: rainlanguage/sqlite-web

Length of output: 616


🏁 Script executed:

# Look at other error handling in the file to see patterns
rg "Err\(" packages/sqlite-web/src/opfs.rs -A 1

Repository: rainlanguage/sqlite-web

Length of output: 100


🏁 Script executed:

# Check web-sys version and available exports
cat packages/Cargo.toml | grep -A 10 "web-sys"

Repository: rainlanguage/sqlite-web

Length of output: 118


🏁 Script executed:

# Look for any existing error handling with js_sys or exceptions
rg "js_sys::" packages/sqlite-web/src/ -A 2

Repository: rainlanguage/sqlite-web

Length of output: 16156


🏁 Script executed:

# Search the entire codebase for NotFoundError handling pattern
rg "NotFound" --type rust

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Check if there's documentation or examples in the repo about error handling
find . -name "*.rs" -type f -exec grep -l "DomException\|get_directory_handle" {} \;

Repository: rainlanguage/sqlite-web

Length of output: 100


🏁 Script executed:

# Let's check web-sys docs indirectly by looking at what's available
# First, check if there's a Cargo.lock file that shows actual versions
head -100 Cargo.lock | grep -A 5 "name = \"web-sys\""

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Check the workspace Cargo.toml to see web-sys configuration
cat Cargo.toml | grep -A 20 "web-sys"

Repository: rainlanguage/sqlite-web

Length of output: 551


🏁 Script executed:

# Look at the errors.rs file to understand error handling patterns
cat -n packages/sqlite-web/src/errors.rs | head -50

Repository: rainlanguage/sqlite-web

Length of output: 1826


🏁 Script executed:

# Check if there's any error name checking pattern in the codebase
rg "name()\|\.name\|\.type" packages/sqlite-web/src/ --type rust

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Search for how File System API errors are handled elsewhere
rg "get_directory_handle\|get_file_handle\|remove_entry" packages/sqlite-web/src/ --type rust -B 2 -A 5

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Check web-sys documentation in a comment or see if there's information about DomException
python3 << 'EOF'
# Query web for web_sys DomException availability
import subprocess
result = subprocess.run(['rg', 'DomException', '--type', 'rust'], cwd='.', capture_output=True, text=True)
print("DomException search result (should be empty):", result.returncode)
print("stdout:", result.stdout)
print("stderr:", result.stderr)
EOF

Repository: rainlanguage/sqlite-web

Length of output: 153


🏁 Script executed:

# Let's check if there's a way to check error names in web-sys
# Look for any pattern matching on error types
rg "dyn_ref\|dyn_into" packages/sqlite-web/src/ --type rust -B 1 -A 1

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Check web-sys features more comprehensively
rg "DomException" --type rust

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Let's verify if there's an alternative way to check error names
# Search for how errors are handled with Reflect pattern
rg "Reflect::get.*name\|error.*name" --type rust -B 2 -A 2

Repository: rainlanguage/sqlite-web

Length of output: 49


🏁 Script executed:

# Check if there are any examples of checking JS error types in the codebase
cat -n packages/sqlite-web/src/opfs.rs | sed -n '1,120p'

Repository: rainlanguage/sqlite-web

Length of output: 4969


🏁 Script executed:

# Verify web-sys version and check if DomException feature exists
web_sys_version=$(rg 'web-sys.*version' Cargo.toml | head -1)
echo "Web-sys configuration: $web_sys_version"

Repository: rainlanguage/sqlite-web

Length of output: 131


🌐 Web query:

web-sys 0.3 DomException feature rust

💡 Result:

In web-sys 0.3, DomException is behind a Cargo feature named DomException. To use web_sys::DomException, enable that feature in your Cargo.toml:

[dependencies.web-sys]
version = "0.3"
features = ["DomException"]

web-sys gates most Web API types behind per-type features, and the docs for DomException explicitly state it requires the DomException feature. [1], [2], [3]


Add DomException feature and distinguish NotFoundError from other errors.

The current implementation swallows all errors with Err(_) => Ok(None), which masks permission issues, type mismatches, and other failures. Only NotFoundError should return Ok(None); other errors must be propagated.

The suggested fix requires adding the DomException feature to web-sys in Cargo.toml since it is not currently enabled. Update the dependencies and implement the error type check in get_directory_if_exists:

🛠️ Required changes

In Cargo.toml, update web-sys features:

web-sys = { version = "0.3", features = [
    "Blob",
    "BlobPropertyBag",
    "Url",
    "Worker",
    "WorkerOptions",
    "WorkerType",
    "BroadcastChannel",
    "MessageEvent",
    "DedicatedWorkerGlobalScope",
    "Navigator",
    "Window",
    "Location",
    "StorageManager",
    "FileSystemDirectoryHandle",
    "FileSystemHandle",
    "FileSystemRemoveOptions",
    "FileSystemGetDirectoryOptions",
    "DomException"
]}

In opfs.rs, update the import:

-use web_sys::{FileSystemDirectoryHandle, FileSystemGetDirectoryOptions, FileSystemRemoveOptions};
+use web_sys::{
+    DomException, FileSystemDirectoryHandle, FileSystemGetDirectoryOptions, FileSystemRemoveOptions,
+};

Update get_directory_if_exists to check error type:

         match JsFuture::from(parent.get_directory_handle_with_options(name, &options)).await {
             Ok(handle) => handle.dyn_into().map(Some).map_err(|e| {
                 SQLiteWasmDatabaseError::OpfsDeletionFailed(format!(
                     "directory handle type mismatch: {}",
                     describe_js_value(&e)
                 ))
             }),
-            Err(_) => Ok(None),
+            Err(err) => {
+                if let Some(dom) = err.dyn_ref::<DomException>() {
+                    if dom.name() == "NotFoundError" {
+                        return Ok(None);
+                    }
+                }
+                Err(SQLiteWasmDatabaseError::OpfsDeletionFailed(format!(
+                    "failed to access directory {name}: {}",
+                    describe_js_value(&err)
+                )))
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async fn get_directory_if_exists(
parent: &FileSystemDirectoryHandle,
name: &str,
) -> Result<Option<FileSystemDirectoryHandle>, SQLiteWasmDatabaseError> {
let options = FileSystemGetDirectoryOptions::new();
options.set_create(false);
match JsFuture::from(parent.get_directory_handle_with_options(name, &options)).await {
Ok(handle) => handle.dyn_into().map(Some).map_err(|e| {
SQLiteWasmDatabaseError::OpfsDeletionFailed(format!(
"directory handle type mismatch: {}",
describe_js_value(&e)
))
}),
Err(_) => Ok(None),
}
async fn get_directory_if_exists(
parent: &FileSystemDirectoryHandle,
name: &str,
) -> Result<Option<FileSystemDirectoryHandle>, SQLiteWasmDatabaseError> {
let options = FileSystemGetDirectoryOptions::new();
options.set_create(false);
match JsFuture::from(parent.get_directory_handle_with_options(name, &options)).await {
Ok(handle) => handle.dyn_into().map(Some).map_err(|e| {
SQLiteWasmDatabaseError::OpfsDeletionFailed(format!(
"directory handle type mismatch: {}",
describe_js_value(&e)
))
}),
Err(err) => {
if let Some(dom) = err.dyn_ref::<DomException>() {
if dom.name() == "NotFoundError" {
return Ok(None);
}
}
Err(SQLiteWasmDatabaseError::OpfsDeletionFailed(format!(
"failed to access directory {name}: {}",
describe_js_value(&err)
)))
}
}
}
🤖 Prompt for AI Agents
In `@packages/sqlite-web/src/opfs.rs` around lines 60 - 75, The function
get_directory_if_exists currently treats all JS errors as "not found"; add the
web-sys "DomException" feature in Cargo.toml and change the error handling in
get_directory_if_exists to detect DomException and only treat a "NotFoundError"
as Ok(None), while propagating other errors as SQLiteWasmDatabaseError
(preserving existing describe_js_value-based messages); keep the creation
options via FileSystemGetDirectoryOptions and return handle.dyn_into().map(Some)
on success, but on Err(e) use e.dyn_ref::<web_sys::DomException>() to inspect
.name() and return Ok(None) only when name == "NotFoundError", otherwise convert
the error into an appropriate SQLiteWasmDatabaseError and return Err.

- Reject existing promise before dropping to prevent hangs
- Fix test to capture promise before calling mark_ready()
- Add DomException web-sys feature
- Distinguish NotFoundError from other errors in get_directory_if_exists()
- Remove redundant test
- Remove request ID reset to avoid ID collisions with stale replies
- Consolidate wipe tests into single test function
- Add NaN/Infinity parameter validation tests
- Remove redundant tests from tests.rs
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/sqlite-web/src/db.rs`:
- Around line 186-214: wipe_and_recreate currently terminates the worker and
returns early if delete_opfs_sahpool_directory() fails, leaving the instance
without a worker; change the flow to capture the OPFS deletion error (call
delete_opfs_sahpool_directory().await and store any Err), then always proceed to
recreate the worker via create_worker_from_code, install_onmessage_handler,
replace *self.worker, and call self.wait_until_ready().await before finally
propagating the stored deletion error (if any) as the Result; keep mapping
create_worker_from_code errors to SQLiteWasmDatabaseError::JsError and ensure
any pending_queries handling and ready_signal.reset() remain unchanged.

Capture OPFS deletion error and propagate it after worker recreation
to avoid leaving the database in an unusable state.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/sqlite-web/src/db.rs`:
- Around line 333-404: Split the large wasm_bindgen_test function
wipe_and_recreate_tests into focused tests: keep one async test (e.g.,
wipe_and_recreate_happy_path) that exercises SQLiteWasmDatabase::new,
SQLiteWasmDatabase::wipe_and_recreate, creation/insertion/select, repeated wipes
(the t0..t2 loop) and checks on sqlite_master; move the parameter validation
checks that call db.query with NaN/Infinity into a separate async test (e.g.,
query_rejects_invalid_numbers) that only asserts errors for
JsValue::from_f64(NaN/INFINITY/NEG_INFINITY). Ensure both functions retain
#[wasm_bindgen_test(async)] and reuse the same
SQLiteWasmDatabase::new("test_wipe") setup or create distinct named DBs to avoid
interference.

Comment on lines +333 to +404
#[wasm_bindgen_test(async)]
async fn wipe_and_recreate_tests() {
let db = SQLiteWasmDatabase::new("test_wipe").await.unwrap();
db.wipe_and_recreate().await.unwrap();

db.query(
"CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)",
None,
)
.await
.unwrap();
db.query("INSERT INTO users (name) VALUES ('Alice')", None)
.await
.unwrap();

let result = db
.query("SELECT COUNT(*) as count FROM users", None)
.await
.unwrap();
assert!(result.contains("\"count\": 1"));

db.wipe_and_recreate().await.unwrap();

let result = db.query("SELECT * FROM users", None).await;
assert!(result.is_err() || result.unwrap().contains("no such table"));

let create_result = db
.query(
"CREATE TABLE new_table (id INTEGER PRIMARY KEY, value TEXT)",
None,
)
.await;
assert!(create_result.is_ok());

let insert_result = db
.query("INSERT INTO new_table (value) VALUES ('test')", None)
.await;
assert!(insert_result.is_ok());

let select_result = db.query("SELECT * FROM new_table", None).await.unwrap();
assert!(select_result.contains("test"));

for i in 0..3 {
db.query(&format!("CREATE TABLE t{} (id INTEGER)", i), None)
.await
.unwrap();
db.wipe_and_recreate().await.unwrap();
}

let result = db
.query("SELECT name FROM sqlite_master WHERE type='table'", None)
.await
.unwrap();
assert!(!result.contains("t0"));
assert!(!result.contains("t1"));
assert!(!result.contains("t2"));

let arr = Array::new();
arr.push(&JsValue::from_f64(f64::NAN));
let res = db.query("SELECT ?", Some(arr)).await;
assert!(res.is_err(), "NaN should be rejected");

let arr = Array::new();
arr.push(&JsValue::from_f64(f64::INFINITY));
let res = db.query("SELECT ?", Some(arr)).await;
assert!(res.is_err(), "+Infinity should be rejected");

let arr = Array::new();
arr.push(&JsValue::from_f64(f64::NEG_INFINITY));
let res = db.query("SELECT ?", Some(arr)).await;
assert!(res.is_err(), "-Infinity should be rejected");
}
Copy link

@coderabbitai coderabbitai bot Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider splitting the test into focused test cases.

This test bundles wipe/recreate scenarios, consecutive wipes, and parameter validation (NaN/Infinity) into a single function. While functional, isolated tests improve debuggability and make failures easier to diagnose. The invalid parameter tests (lines 390-403) are unrelated to wipe_and_recreate and could be a separate test.

🤖 Prompt for AI Agents
In `@packages/sqlite-web/src/db.rs` around lines 333 - 404, Split the large
wasm_bindgen_test function wipe_and_recreate_tests into focused tests: keep one
async test (e.g., wipe_and_recreate_happy_path) that exercises
SQLiteWasmDatabase::new, SQLiteWasmDatabase::wipe_and_recreate,
creation/insertion/select, repeated wipes (the t0..t2 loop) and checks on
sqlite_master; move the parameter validation checks that call db.query with
NaN/Infinity into a separate async test (e.g., query_rejects_invalid_numbers)
that only asserts errors for JsValue::from_f64(NaN/INFINITY/NEG_INFINITY).
Ensure both functions retain #[wasm_bindgen_test(async)] and reuse the same
SQLiteWasmDatabase::new("test_wipe") setup or create distinct named DBs to avoid
interference.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have opted to use a single test because the database persists between the tests and it was causing us issues. By doing it this way, we make sure that we have we are testing everything properly.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@findolor, thank you for the clarification! That makes perfect sense—if the database state persists between test functions in your WASM test environment, consolidating the related scenarios into a single test is the right approach to ensure proper isolation and test correctness. I appreciate you explaining the reasoning behind this design decision.


✏️ Learnings added
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 27
File: packages/sqlite-web/src/db.rs:333-404
Timestamp: 2026-01-27T10:13:33.826Z
Learning: In the sqlite-web WASM test environment, database state can persist between test functions, causing test interference. To ensure proper test isolation, related test scenarios for features like wipe_and_recreate should be consolidated into a single test function rather than split into multiple independent test cases.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/lib.rs : Expose an async, promise-based query interface from the SQLiteWasmDatabase public API

Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 14
File: svelte-test/tests/database-functions/float-is-zero.test.ts:39-57
Timestamp: 2025-09-19T07:06:20.173Z
Learning: PRAGMA function_list is not available in the WASM SQLite test environment used by svelte-test, so availability testing should rely on direct function calls rather than PRAGMA queries.

Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/database_functions.rs : Integrate and expose rain.math.float-backed custom database functions for SQLite queries

Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/worker_template.rs : Ensure the worker JavaScript generated by worker_template.rs embeds the WASM (base64) and does not fetch external WASM files

Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 9
File: packages/sqlite-web-core/src/coordination.rs:52-65
Timestamp: 2025-09-15T06:11:31.781Z
Learning: In packages/sqlite-web-core/src/coordination.rs, the pattern of using `db.borrow_mut().take()` followed by async database operations and then putting the database back with `*db.borrow_mut() = Some(database)` is safe in their use case and doesn't cause concurrent access issues or "Database not initialized" errors.

Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/worker.rs : Keep worker.rs as the main worker entry point invoked by worker_main()

Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 6
File: svelte-test/tests/database-functions/float-sum.test.ts:78-86
Timestamp: 2025-09-17T08:04:44.062Z
Learning: In svelte-test/tests/database-functions/float-sum.test.ts, the schema defines `amount TEXT NOT NULL` but the NULL value handling tests attempt to insert NULL values, creating a constraint violation that prevents the tests from passing.

Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 15
File: svelte-test/tests/integration/error-handling.test.ts:418-434
Timestamp: 2025-09-17T17:44:54.855Z
Learning: In the sqlite-web test harness, parameter binding is not available for the db.query() method, so string interpolation with manual SQL escaping using value.replace(/'/g, "''") is the necessary approach for handling special characters in test data.

Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 5
File: packages/sqlite-web-core/src/database_functions/bigint_sum.rs:76-93
Timestamp: 2025-08-27T05:55:40.481Z
Learning: In the sqlite-web-core codebase, for SQLite aggregate function context initialization, the maintainer prefers using byte-scanning to detect zero-initialized memory rather than using explicit initialization flags or Option wrappers.

Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/messages.rs : Use structured message types for worker communication as defined in messages.rs

Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 12
File: svelte-test/tests/fixtures/test-helpers.ts:11-11
Timestamp: 2025-09-15T10:01:17.743Z
Learning: In the sqlite-web project, test database names in createTestDatabase function do not need timestamp suffixes for uniqueness. The maintainer has confirmed that test isolation via unique naming is not required.

Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/embedded_worker.js : Do not manually edit embedded_worker.js; it is generated by the build and should be treated as read-only

Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Maintain self-contained workers with no external WASM file dependencies across the project

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants