From d7a8e532fe051d7360c4f1d3ac50679afcb637f2 Mon Sep 17 00:00:00 2001 From: Kevin Flansburg Date: Wed, 17 Apr 2024 11:24:36 -0400 Subject: [PATCH 1/3] Move worker-kv test suite to worker-sandbox --- Cargo.lock | 1 + worker-kv/tests/.gitignore | 1 - worker-kv/tests/integration.rs | 97 ------- worker-kv/tests/package-lock.json | 265 ------------------ worker-kv/tests/package.json | 13 - worker-kv/tests/worker_kv_test/.gitignore | 5 - .../worker_kv_test/.mf-init/kv/test/simple | 1 - worker-kv/tests/worker_kv_test/Cargo.toml | 33 --- worker-kv/tests/worker_kv_test/src/lib.rs | 140 --------- worker-kv/tests/worker_kv_test/src/utils.rs | 12 - worker-kv/tests/worker_kv_test/wrangler.toml | 23 -- worker-sandbox/Cargo.toml | 1 + worker-sandbox/src/kv.rs | 126 +++++++++ worker-sandbox/src/router.rs | 17 ++ worker-sandbox/tests/kv.spec.ts | 25 ++ worker-sandbox/tests/mf.ts | 2 +- 16 files changed, 171 insertions(+), 591 deletions(-) delete mode 100644 worker-kv/tests/.gitignore delete mode 100644 worker-kv/tests/integration.rs delete mode 100644 worker-kv/tests/package-lock.json delete mode 100644 worker-kv/tests/package.json delete mode 100644 worker-kv/tests/worker_kv_test/.gitignore delete mode 100644 worker-kv/tests/worker_kv_test/.mf-init/kv/test/simple delete mode 100644 worker-kv/tests/worker_kv_test/Cargo.toml delete mode 100644 worker-kv/tests/worker_kv_test/src/lib.rs delete mode 100644 worker-kv/tests/worker_kv_test/src/utils.rs delete mode 100644 worker-kv/tests/worker_kv_test/wrangler.toml create mode 100644 worker-sandbox/tests/kv.spec.ts diff --git a/Cargo.lock b/Cargo.lock index 3a883f6b3..83ca70908 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2794,6 +2794,7 @@ dependencies = [ "uuid", "wasm-bindgen-test", "worker", + "worker-kv", ] [[package]] diff --git a/worker-kv/tests/.gitignore b/worker-kv/tests/.gitignore deleted file mode 100644 index 40b878db5..000000000 --- a/worker-kv/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules/ \ No newline at end of file diff --git a/worker-kv/tests/integration.rs b/worker-kv/tests/integration.rs deleted file mode 100644 index dcbc16f1c..000000000 --- a/worker-kv/tests/integration.rs +++ /dev/null @@ -1,97 +0,0 @@ -use std::{ - io::{self, ErrorKind}, - net::{SocketAddr, TcpStream}, - path::Path, - process::{Child, Command}, - str::FromStr, - time::{Duration, Instant}, -}; - -use fs_extra::dir::CopyOptions; - -#[tokio::test] -#[allow(clippy::needless_collect)] -async fn integration_test() { - let node_procs_to_ignore = psutil::process::processes() - .unwrap() - .into_iter() - .filter_map(|r| r.ok()) - .filter(|process| process.name().unwrap() == "node") - .map(|p| p.pid()) - .collect::>(); - - start_miniflare().expect("unable to spawn miniflare, did you install node modules?"); - wait_for_worker_to_spawn(); - - let endpoints = [ - "get", - "get-not-found", - "list-keys", - "put-simple", - "put-metadata", - "put-expiration", - ]; - - for endpoint in endpoints { - let text_res = reqwest::get(&format!("http://localhost:8787/{}", endpoint)) - .await - .expect("unable to send request") - .text() - .await; - - assert!(text_res.is_ok(), "{} failed", endpoint); - assert_eq!(text_res.unwrap(), "passed".to_string()); - } - - let processes = psutil::process::processes().unwrap(); - - for process in processes.into_iter().filter_map(|r| r.ok()) { - if !node_procs_to_ignore.contains(&process.pid()) && process.name().unwrap() == "node" { - let _ = process.send_signal(psutil::process::Signal::SIGQUIT); - } - } -} - -/// Waits for wrangler to spawn it's http server. -fn wait_for_worker_to_spawn() { - let now = Instant::now(); - let addr = SocketAddr::from_str("0.0.0.0:8787").unwrap(); - - while Instant::now() - now <= Duration::from_secs(5 * 60) { - match TcpStream::connect_timeout(&addr, Duration::from_secs(5)) { - Ok(_) => return, - Err(e) - if e.kind() == ErrorKind::ConnectionRefused - || e.kind() == ErrorKind::ConnectionReset => - { - eprintln!("Connection refused or reset.") - } - Err(e) => panic!("{1}: {:?}", e, "Unexpected error connecting to worker"), - } - } - - panic!("Timed out connecting to worker.") -} - -fn start_miniflare() -> io::Result { - let mf_path = Path::new("tests/worker_kv_test/.mf"); - - if mf_path.exists() { - std::fs::remove_dir_all(mf_path)?; - } - - fs_extra::dir::copy( - "tests/worker_kv_test/.mf-init", - mf_path, - &CopyOptions { - content_only: true, - ..CopyOptions::new() - }, - ) - .unwrap(); - - Command::new("../node_modules/.bin/miniflare") - .args(["-c", "wrangler.toml", "-k", "test", "--kv-persist"]) - .current_dir("tests/worker_kv_test") - .spawn() -} diff --git a/worker-kv/tests/package-lock.json b/worker-kv/tests/package-lock.json deleted file mode 100644 index 175142044..000000000 --- a/worker-kv/tests/package-lock.json +++ /dev/null @@ -1,265 +0,0 @@ -{ - "name": "worker-kv-test-harness", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@cloudflare/workerd-darwin-64": { - "version": "1.20240320.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240320.1.tgz", - "integrity": "sha512-ioG5k2M17xyiAlK/k3L21NZLMVeSHMjwlmGtZyCyzSLL5/zGINcgZ5yPLV0UuWiysw07/6Jjzm5Sx94hzMVybg==", - "dev": true, - "optional": true - }, - "@cloudflare/workerd-darwin-arm64": { - "version": "1.20240320.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20240320.1.tgz", - "integrity": "sha512-Ga6RDdnFEIsN4WuWsaP9bLGvK9K7pEIVoSIgmw6vweVlD8UK/a2MPGrsF1ogwdeCTCOMY8wUh9poL/Yu48IPpg==", - "dev": true, - "optional": true - }, - "@cloudflare/workerd-linux-64": { - "version": "1.20240320.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20240320.1.tgz", - "integrity": "sha512-KFof5H8eU0NXv+pUAU7Lk/OLtOmfsioTJqu0v6kPL7QsTGsgzj5sEQNcQ8DONSze549Yflu5W00qpA2cPz9eWQ==", - "dev": true, - "optional": true - }, - "@cloudflare/workerd-linux-arm64": { - "version": "1.20240320.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20240320.1.tgz", - "integrity": "sha512-t+kGc6dGdkKvVMGcHCPhlCsUZF5dj8xbAFvLB7DAJ8T79ys30rmY2Lu/C8vKlhjH9TJhbzgKmPaJ0wC/K4euvw==", - "dev": true, - "optional": true - }, - "@cloudflare/workerd-windows-64": { - "version": "1.20240320.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20240320.1.tgz", - "integrity": "sha512-9xDylCOsuzWqGuANkuUByiJ5RHeMqgw37FiI7rn8I6zdGAc/alOB9B4Bh7B73WC2uEpFL+XCEjcHZ6NmsO4NaQ==", - "dev": true, - "optional": true - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - } - }, - "@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "dev": true - }, - "@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true - }, - "acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "dev": true - }, - "as-table": { - "version": "1.0.55", - "resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz", - "integrity": "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==", - "dev": true, - "requires": { - "printable-characters": "^1.0.42" - } - }, - "capnp-ts": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/capnp-ts/-/capnp-ts-0.7.0.tgz", - "integrity": "sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g==", - "dev": true, - "requires": { - "debug": "^4.3.1", - "tslib": "^2.2.0" - } - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true - }, - "data-uri-to-buffer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", - "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "exit-hook": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", - "integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==", - "dev": true - }, - "get-source": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz", - "integrity": "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==", - "dev": true, - "requires": { - "data-uri-to-buffer": "^2.0.0", - "source-map": "^0.6.1" - } - }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "miniflare": { - "version": "3.20240320.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240320.0.tgz", - "integrity": "sha512-4M2QRxs+J5sUsybBzKT++tlbrjjjGZdtWxKmj2sqLsT26dGaKDz7DxjAeF5XIhKa5cADcffygjxx4EvfWocMmw==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "0.8.1", - "acorn": "^8.8.0", - "acorn-walk": "^8.2.0", - "capnp-ts": "^0.7.0", - "exit-hook": "^2.2.1", - "glob-to-regexp": "^0.4.1", - "stoppable": "^1.1.0", - "undici": "^5.28.2", - "workerd": "1.20240320.1", - "ws": "^8.11.0", - "youch": "^3.2.2", - "zod": "^3.20.6" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", - "dev": true - }, - "printable-characters": { - "version": "1.0.42", - "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz", - "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "stacktracey": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz", - "integrity": "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==", - "dev": true, - "requires": { - "as-table": "^1.0.36", - "get-source": "^2.0.12" - } - }, - "stoppable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", - "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", - "dev": true - }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - }, - "undici": { - "version": "5.28.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", - "dev": true, - "requires": { - "@fastify/busboy": "^2.0.0" - } - }, - "workerd": { - "version": "1.20240320.1", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20240320.1.tgz", - "integrity": "sha512-nuavAGGjh0qqM6RF5zxTHyUwEqdLCHchodbrpbh/xlJpFGnJVY5C1YgSi2S9aLkJJoa0/25Ta/+EzXEbApA/3w==", - "dev": true, - "requires": { - "@cloudflare/workerd-darwin-64": "1.20240320.1", - "@cloudflare/workerd-darwin-arm64": "1.20240320.1", - "@cloudflare/workerd-linux-64": "1.20240320.1", - "@cloudflare/workerd-linux-arm64": "1.20240320.1", - "@cloudflare/workerd-windows-64": "1.20240320.1" - } - }, - "ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", - "dev": true - }, - "youch": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/youch/-/youch-3.3.3.tgz", - "integrity": "sha512-qSFXUk3UZBLfggAW3dJKg0BMblG5biqSF8M34E06o5CSsZtH92u9Hqmj2RzGiHDi64fhe83+4tENFP2DB6t6ZA==", - "dev": true, - "requires": { - "cookie": "^0.5.0", - "mustache": "^4.2.0", - "stacktracey": "^2.1.8" - } - }, - "zod": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", - "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", - "dev": true - } - } -} diff --git a/worker-kv/tests/package.json b/worker-kv/tests/package.json deleted file mode 100644 index 1a4e69a5a..000000000 --- a/worker-kv/tests/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "worker-kv-test-harness", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": {}, - "keywords": [], - "author": "", - "license": "ISC", - "devDependencies": { - "miniflare": "^3.20240320.0" - } -} diff --git a/worker-kv/tests/worker_kv_test/.gitignore b/worker-kv/tests/worker_kv_test/.gitignore deleted file mode 100644 index 73abc72f7..000000000 --- a/worker-kv/tests/worker_kv_test/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/target -**/*.rs.bk -wasm-pack.log -build/ -.mf/ \ No newline at end of file diff --git a/worker-kv/tests/worker_kv_test/.mf-init/kv/test/simple b/worker-kv/tests/worker_kv_test/.mf-init/kv/test/simple deleted file mode 100644 index 5453e1246..000000000 --- a/worker-kv/tests/worker_kv_test/.mf-init/kv/test/simple +++ /dev/null @@ -1 +0,0 @@ -passed \ No newline at end of file diff --git a/worker-kv/tests/worker_kv_test/Cargo.toml b/worker-kv/tests/worker_kv_test/Cargo.toml deleted file mode 100644 index ae933039a..000000000 --- a/worker-kv/tests/worker_kv_test/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "worker_kv_test" -version = "0.1.0" -authors = ["Zeb Piasecki "] -edition = "2018" - -[lib] -crate-type = ["cdylib", "rlib"] - -[features] -default = ["console_error_panic_hook"] - -[dependencies] -cfg-if = "0.1.2" -worker = "0.0.13" -worker-kv = { path = "../../" } -serde_json = "1.0.67" - -# The `console_error_panic_hook` crate provides better debugging of panics by -# logging them with `console.error`. This is great for development, but requires -# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for -# code size when deploying. -console_error_panic_hook = { version = "0.1.1", optional = true } - -# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size -# compared to the default allocator's ~10K. It is slower than the default -# allocator, however. -wee_alloc = { version = "0.4.2", optional = true } -thiserror = "1.0.29" - -[profile.release] -# Tell `rustc` to optimize for small code size. -opt-level = "s" diff --git a/worker-kv/tests/worker_kv_test/src/lib.rs b/worker-kv/tests/worker_kv_test/src/lib.rs deleted file mode 100644 index 8567fc404..000000000 --- a/worker-kv/tests/worker_kv_test/src/lib.rs +++ /dev/null @@ -1,140 +0,0 @@ -use std::future::Future; - -use worker::*; -use worker_kv::{KvError, KvStore}; - -type TestResult = std::result::Result; - -mod utils; - -macro_rules! kv_assert_eq { - ($left: expr, $right: expr) => {{ - let left = &$left; - let right = &$right; - if left != right { - Err(TestError::Other(format!("{:#?} != {:#?}", left, right))) - } else { - Ok(()) - } - }}; -} - -#[event(fetch)] -pub async fn main(req: Request, env: Env, _ctx: Context) -> Result { - // Optionally, get more helpful error messages written to the console in the case of a panic. - utils::set_panic_hook(); - - // Create the KV store directly from `worker_kv` as the rust worker sdk uses a published version. - let store = KvStore::from_this(&env, "test").expect("test kv store not bound"); - - Router::with_data(store) - .get_async("/get", |req, ctx| wrap(req, ctx, get)) - .get_async("/get-not-found", |req, ctx| wrap(req, ctx, get_not_found)) - .get_async("/list-keys", |req, ctx| wrap(req, ctx, list_keys)) - .get_async("/put-simple", |req, ctx| wrap(req, ctx, put_simple)) - .get_async("/put-metadata", |req, ctx| wrap(req, ctx, put_metadata)) - .get_async("/put-expiration", |req, ctx| wrap(req, ctx, put_expiration)) - .run(req, env) - .await -} - -async fn get(_: Request, ctx: RouteContext) -> TestResult { - let store = ctx.data; - store - .get("simple") - .text() - .await - .map_err(TestError::from) - .and_then(|v| match v { - Some(e) => Ok(e), - None => Err(TestError::Other("no value found".into())), - }) -} - -async fn get_not_found(_: Request, ctx: RouteContext) -> TestResult { - let store = ctx.data; - let value = store.get("not_found").text().await; - - value.map_err(TestError::from).and_then(|v| match v { - Some(_) => Err(TestError::Other("unexpected value present".into())), - None => Ok("passed".into()), - }) -} - -async fn list_keys(_: Request, ctx: RouteContext) -> TestResult { - let store = ctx.data; - let list_res = store.list().execute().await?; - - // TODO: Test cursor and things. - kv_assert_eq!(list_res.keys.len(), 1)?; - - Ok("passed".into()) -} - -async fn put_simple(_: Request, ctx: RouteContext) -> TestResult { - let store = ctx.data; - store.put("put_a", "test")?.execute().await?; - - let val = store.get("put_a").text().await?.unwrap(); - kv_assert_eq!(val, "test")?; - - Ok("passed".into()) -} - -async fn put_metadata(_: Request, ctx: RouteContext) -> TestResult { - let store = ctx.data; - store.put("put_b", "test")?.metadata(100)?.execute().await?; - - let (val, meta) = store.get("put_b").text_with_metadata::().await?; - kv_assert_eq!(val.unwrap(), "test")?; - kv_assert_eq!(meta.unwrap(), 100)?; - - Ok("passed".into()) -} - -async fn put_expiration(_: Request, ctx: RouteContext) -> TestResult { - const EXPIRATION: u64 = 2000000000; - let store = ctx.data; - store - .put("put_c", "test")? - .expiration(EXPIRATION) - .execute() - .await?; - - let val = store.get("put_a").text().await?.unwrap(); - kv_assert_eq!(val, "test")?; - - let list = store.list().prefix("put_c".into()).execute().await?; - let key = list - .keys - .into_iter() - .find(|key| key.name == "put_c") - .unwrap(); - kv_assert_eq!(key.expiration, Some(EXPIRATION))?; - - Ok("passed".into()) -} - -async fn wrap( - req: Request, - ctx: RouteContext, - func: fn(Request, RouteContext) -> T, -) -> Result -where - T: Future + 'static, -{ - let result = func(req, ctx); - - match result.await { - Ok(value) => Response::ok(value), - Err(e) => Response::ok(e.to_string()).map(|res| res.with_status(500)), - } -} - -#[derive(Debug, thiserror::Error)] -enum TestError { - #[error("{0}")] - Kv(#[from] KvError), - #[error("{0}")] - Other(String), -} diff --git a/worker-kv/tests/worker_kv_test/src/utils.rs b/worker-kv/tests/worker_kv_test/src/utils.rs deleted file mode 100644 index aab768f6e..000000000 --- a/worker-kv/tests/worker_kv_test/src/utils.rs +++ /dev/null @@ -1,12 +0,0 @@ -use cfg_if::cfg_if; - -cfg_if! { - // https://github.com/rustwasm/console_error_panic_hook#readme - if #[cfg(feature = "console_error_panic_hook")] { - extern crate console_error_panic_hook; - pub use self::console_error_panic_hook::set_once as set_panic_hook; - } else { - #[inline] - pub fn set_panic_hook() {} - } -} diff --git a/worker-kv/tests/worker_kv_test/wrangler.toml b/worker-kv/tests/worker_kv_test/wrangler.toml deleted file mode 100644 index ec6a630a1..000000000 --- a/worker-kv/tests/worker_kv_test/wrangler.toml +++ /dev/null @@ -1,23 +0,0 @@ -name = "worker_kv_test" -type = "rust" -workers_dev = true -compatibility_date = "2021-08-27" # required -compatibility_flags = [ "formdata_parser_supports_files" ] # required - -[vars] -WORKERS_RS_VERSION = "0.0.4" - -[build] -command = "cargo install -q worker-build && worker-build --release" # required - -[build.upload] -dir = "build/worker" -format = "modules" -main = "./shim.mjs" - -[[build.upload.rules]] -globs = ["**/*.wasm"] -type = "CompiledWasm" - -# read more about configuring your Worker via wrangler.toml at: -# https://developers.cloudflare.com/workers/cli-wrangler/configuration diff --git a/worker-sandbox/Cargo.toml b/worker-sandbox/Cargo.toml index d1d432afb..ffa81baa8 100644 --- a/worker-sandbox/Cargo.toml +++ b/worker-sandbox/Cargo.toml @@ -39,6 +39,7 @@ serde-wasm-bindgen = "0.6.1" md5 = "0.7.0" tokio-stream = "0.1.15" tokio = { version = "1.28", default-features = false, features=['io-util'] } +worker-kv = { path = "../worker-kv" } [dependencies.axum] version = "0.7" diff --git a/worker-sandbox/src/kv.rs b/worker-sandbox/src/kv.rs index da221ca41..e8098c862 100644 --- a/worker-sandbox/src/kv.rs +++ b/worker-sandbox/src/kv.rs @@ -1,6 +1,22 @@ use super::SomeSharedData; +use serde::{Deserialize, Serialize}; use worker::{Env, Request, Response, Result}; +macro_rules! kv_assert_eq { + ($left: expr, $right: expr) => {{ + let left = &$left; + let right = &$right; + if left != right { + Err(worker::Error::RustError(format!( + "{:#?} != {:#?}", + left, right + ))) + } else { + Ok(()) + } + }}; +} + #[worker::send] pub async fn handle_post_key_value( req: Request, @@ -20,3 +36,113 @@ pub async fn handle_post_key_value( Response::from_json(&kv.list().execute().await?) } + +const TEST_NAMESPACE: &str = "TEST"; + +#[worker::send] +pub async fn get(_req: Request, env: Env, _data: SomeSharedData) -> Result { + let store = env.kv(TEST_NAMESPACE)?; + let value = store.get("simple").text().await?; + match value { + Some(e) => Response::ok(e), + None => Response::error("not found", 404), + } +} + +#[worker::send] +pub async fn get_not_found(_req: Request, env: Env, _data: SomeSharedData) -> Result { + let store = env.kv(TEST_NAMESPACE)?; + let value = store.get("not_found").text().await?; + match value { + Some(_) => Response::error("unexpected value present", 500), + None => Response::ok("passed"), + } +} + +#[worker::send] +pub async fn list_keys(_req: Request, env: Env, _data: SomeSharedData) -> Result { + let store = env.kv(TEST_NAMESPACE)?; + let list_res = store.list().execute().await?; + + // TODO: Test cursor and things. + kv_assert_eq!(list_res.keys.len(), 1)?; + + Response::ok("passed") +} + +#[worker::send] +pub async fn put_simple(_req: Request, env: Env, _data: SomeSharedData) -> Result { + let store = env.kv(TEST_NAMESPACE)?; + store.put("put_a", "test")?.execute().await?; + + let val = store.get("put_a").text().await?.unwrap(); + kv_assert_eq!(val, "test")?; + + Response::ok("passed") +} + +#[worker::send] +pub async fn put_metadata(_req: Request, env: Env, _data: SomeSharedData) -> Result { + let store = env.kv(TEST_NAMESPACE)?; + store.put("put_b", "test")?.metadata(100)?.execute().await?; + + let (val, meta) = store.get("put_b").text_with_metadata::().await?; + kv_assert_eq!(val.unwrap(), "test")?; + kv_assert_eq!(meta.unwrap(), 100)?; + + Response::ok("passed") +} + +#[worker::send] +pub async fn put_metadata_struct( + _req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + #[derive(Debug, Default, Deserialize, Serialize, PartialEq)] + pub struct TestStruct { + pub a: String, + pub b: usize, + } + + let store = env.kv(TEST_NAMESPACE)?; + store + .put("put_b", "test")? + .metadata(TestStruct::default())? + .execute() + .await?; + + let (val, meta) = store + .get("put_b") + .text_with_metadata::() + .await?; + + kv_assert_eq!(val.unwrap(), "test")?; + kv_assert_eq!(meta.unwrap(), TestStruct::default())?; + + Response::ok("passed") +} + +#[worker::send] +pub async fn put_expiration(_req: Request, env: Env, _data: SomeSharedData) -> Result { + const EXPIRATION: u64 = 2000000000; + let store = env.kv(TEST_NAMESPACE)?; + store + .put("put_c", "test")? + .expiration(EXPIRATION) + .execute() + .await?; + + let val = store.get("put_a").text().await?.unwrap(); + kv_assert_eq!(val, "test")?; + + let list = store.list().prefix("put_c".into()).execute().await?; + let key = list + .keys + .into_iter() + .find(|key| key.name == "put_c") + .unwrap(); + kv_assert_eq!(key.expiration, Some(EXPIRATION))?; + + Response::ok("passed") +} diff --git a/worker-sandbox/src/router.rs b/worker-sandbox/src/router.rs index 1bac46223..531ea1d73 100644 --- a/worker-sandbox/src/router.rs +++ b/worker-sandbox/src/router.rs @@ -191,6 +191,16 @@ pub fn make_router(data: SomeSharedData, env: Env) -> axum::Router { .route("/d1/dump", get(handler!(d1::dump))) .route("/d1/exec", post(handler!(d1::exec))) .route("/d1/error", get(handler!(d1::error))) + .route("/kv/get", get(handler!(kv::get))) + .route("/kv/get-not-found", get(handler!(kv::get_not_found))) + .route("/kv/list-keys", get(handler!(kv::list_keys))) + .route("/kv/put-simple", get(handler!(kv::put_simple))) + .route("/kv/put-metadata", get(handler!(kv::put_metadata))) + .route( + "/kv/put-metadata-struct", + get(handler!(kv::put_metadata_struct)), + ) + .route("/kv/put-expiration", get(handler!(kv::put_expiration))) .route("/r2/list-empty", get(handler!(r2::list_empty))) .route("/r2/list", get(handler!(r2::list))) .route("/r2/get-empty", get(handler!(r2::get_empty))) @@ -317,6 +327,13 @@ pub fn make_router<'a>(data: SomeSharedData) -> Router<'a, SomeSharedData> { .get_async("/d1/dump", handler!(d1::dump)) .post_async("/d1/exec", handler!(d1::exec)) .get_async("/d1/error", handler!(d1::error)) + .get_async("/kv/get", handler!(kv::get)) + .get_async("/kv/get-not-found", handler!(kv::get_not_found)) + .get_async("/kv/list-keys", handler!(kv::list_keys)) + .get_async("/kv/put-simple", handler!(kv::put_simple)) + .get_async("/kv/put-metadata", handler!(kv::put_metadata)) + .get_async("/kv/put-metadata-struct", handler!(kv::put_metadata_struct)) + .get_async("/kv/put-expiration", handler!(kv::put_expiration)) .get_async("/r2/list-empty", handler!(r2::list_empty)) .get_async("/r2/list", handler!(r2::list)) .get_async("/r2/get-empty", handler!(r2::get_empty)) diff --git a/worker-sandbox/tests/kv.spec.ts b/worker-sandbox/tests/kv.spec.ts new file mode 100644 index 000000000..d528ad296 --- /dev/null +++ b/worker-sandbox/tests/kv.spec.ts @@ -0,0 +1,25 @@ +import { describe, test, expect } from "vitest"; +import { mf } from "./mf"; + +const CASES = [ + "get", + "get-not-found", + "list-keys", + "put-simple", + "put-metadata", + "put-metadata-struct", + "put-expiration" +] + +async function runTest() { + let store = await mf.getKVNamespace("TEST"); + await store.put("simple", "passed"); + + for (let testCase of CASES) { + test(testCase, async () => { + let response = await mf.dispatchFetch(`http://fake.host/kv/${testCase}`); + expect(response.status).toBe(200); + }); + } +} +describe("kv", runTest); \ No newline at end of file diff --git a/worker-sandbox/tests/mf.ts b/worker-sandbox/tests/mf.ts index 752a2532f..bd5b9fb7f 100644 --- a/worker-sandbox/tests/mf.ts +++ b/worker-sandbox/tests/mf.ts @@ -53,7 +53,7 @@ export const mf = new Miniflare({ COUNTER: "Counter", PUT_RAW_TEST_OBJECT: "PutRawTestObject", }, - kvNamespaces: ["SOME_NAMESPACE", "FILE_SIZES"], + kvNamespaces: ["SOME_NAMESPACE", "FILE_SIZES", "TEST"], serviceBindings: { async remote() { return new Response("hello world"); From e83bf1bc3fb1e7b3de65d37b72f543951a8a6235 Mon Sep 17 00:00:00 2001 From: Kevin Flansburg Date: Wed, 17 Apr 2024 12:19:29 -0400 Subject: [PATCH 2/3] Create explicit struct for KV PutOptions --- worker-kv/src/builder.rs | 27 ++++++++++++++++++--------- worker-sandbox/src/kv.rs | 10 ++++++---- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/worker-kv/src/builder.rs b/worker-kv/src/builder.rs index dfeeb638a..b6321248f 100644 --- a/worker-kv/src/builder.rs +++ b/worker-kv/src/builder.rs @@ -7,24 +7,27 @@ use wasm_bindgen_futures::JsFuture; use crate::{KvError, ListResponse}; /// A builder to configure put requests. -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone)] #[must_use = "PutOptionsBuilder does nothing until you 'execute' it"] pub struct PutOptionsBuilder { - #[serde(skip)] pub(crate) this: Object, - #[serde(skip)] pub(crate) put_function: Function, - #[serde(skip)] pub(crate) name: JsValue, - #[serde(skip)] pub(crate) value: JsValue, - #[serde(skip_serializing_if = "Option::is_none")] pub(crate) expiration: Option, + pub(crate) expiration_ttl: Option, + pub(crate) metadata: Option, +} + +#[derive(Serialize)] +struct PutOptions { + #[serde(skip_serializing_if = "Option::is_none")] + expiration: Option, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "expirationTtl")] - pub(crate) expiration_ttl: Option, + expiration_ttl: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) metadata: Option, + metadata: Option, } impl PutOptionsBuilder { @@ -46,7 +49,13 @@ impl PutOptionsBuilder { } /// Puts the value in the kv store. pub async fn execute(self) -> Result<(), KvError> { - let options_object = serde_wasm_bindgen::to_value(&self).map_err(JsValue::from)?; + let options = PutOptions { + expiration: self.expiration, + expiration_ttl: self.expiration_ttl, + metadata: self.metadata, + }; + + let options_object = serde_wasm_bindgen::to_value(&options).map_err(JsValue::from)?; let promise: Promise = self .put_function .call3(&self.this, &self.name, &self.value, &options_object)? diff --git a/worker-sandbox/src/kv.rs b/worker-sandbox/src/kv.rs index e8098c862..927302ed8 100644 --- a/worker-sandbox/src/kv.rs +++ b/worker-sandbox/src/kv.rs @@ -1,6 +1,6 @@ use super::SomeSharedData; use serde::{Deserialize, Serialize}; -use worker::{Env, Request, Response, Result}; +use worker::{console_debug, Env, Request, Response, Result}; macro_rules! kv_assert_eq { ($left: expr, $right: expr) => {{ @@ -114,11 +114,13 @@ pub async fn put_metadata_struct( let (val, meta) = store .get("put_b") - .text_with_metadata::() + .text_with_metadata::() .await?; kv_assert_eq!(val.unwrap(), "test")?; - kv_assert_eq!(meta.unwrap(), TestStruct::default())?; + console_debug!("{:?}", meta); + assert!(meta.is_some()); + // kv_assert_eq!(meta.unwrap(), TestStruct::default())?; Response::ok("passed") } @@ -133,7 +135,7 @@ pub async fn put_expiration(_req: Request, env: Env, _data: SomeSharedData) -> R .execute() .await?; - let val = store.get("put_a").text().await?.unwrap(); + let val = store.get("put_c").text().await?.unwrap(); kv_assert_eq!(val, "test")?; let list = store.list().prefix("put_c".into()).execute().await?; From e29affefd543810b7868f5e26b48f4381126e336 Mon Sep 17 00:00:00 2001 From: Kevin Flansburg Date: Thu, 18 Apr 2024 10:08:03 -0400 Subject: [PATCH 3/3] Use JSON serializer for KV options --- worker-kv/src/builder.rs | 60 +++++++++++++++++++++++++++--------- worker-sandbox/src/kv.rs | 66 ++++++++++++++++++++-------------------- 2 files changed, 78 insertions(+), 48 deletions(-) diff --git a/worker-kv/src/builder.rs b/worker-kv/src/builder.rs index b6321248f..206d7a805 100644 --- a/worker-kv/src/builder.rs +++ b/worker-kv/src/builder.rs @@ -1,6 +1,7 @@ use js_sys::{ArrayBuffer, Function, Object, Promise, Uint8Array}; use serde::{de::DeserializeOwned, Serialize}; use serde_json::Value; +use serde_wasm_bindgen::Serializer; use wasm_bindgen::{JsCast, JsValue}; use wasm_bindgen_futures::JsFuture; @@ -49,13 +50,15 @@ impl PutOptionsBuilder { } /// Puts the value in the kv store. pub async fn execute(self) -> Result<(), KvError> { - let options = PutOptions { + let ser = Serializer::json_compatible(); + let options_object = PutOptions { expiration: self.expiration, expiration_ttl: self.expiration_ttl, metadata: self.metadata, - }; + } + .serialize(&ser) + .map_err(JsValue::from)?; - let options_object = serde_wasm_bindgen::to_value(&options).map_err(JsValue::from)?; let promise: Promise = self .put_function .call3(&self.this, &self.name, &self.value, &options_object)? @@ -68,13 +71,18 @@ impl PutOptionsBuilder { } /// A builder to configure list requests. -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone)] #[must_use = "ListOptionsBuilder does nothing until you 'execute' it"] pub struct ListOptionsBuilder { - #[serde(skip)] pub(crate) this: Object, - #[serde(skip)] pub(crate) list_function: Function, + pub(crate) limit: Option, + pub(crate) cursor: Option, + pub(crate) prefix: Option, +} + +#[derive(Serialize)] +struct ListOptions { #[serde(skip_serializing_if = "Option::is_none")] pub(crate) limit: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -102,7 +110,15 @@ impl ListOptionsBuilder { } /// Lists the key value pairs in the kv store. pub async fn execute(self) -> Result { - let options_object = serde_wasm_bindgen::to_value(&self).map_err(JsValue::from)?; + let ser = Serializer::json_compatible(); + let options_object = ListOptions { + limit: self.limit, + cursor: self.cursor, + prefix: self.prefix, + } + .serialize(&ser) + .map_err(JsValue::from)?; + let promise: Promise = self .list_function .call1(&self.this, &options_object)? @@ -115,17 +131,19 @@ impl ListOptionsBuilder { } /// A builder to configure get requests. -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone)] #[must_use = "GetOptionsBuilder does nothing until you 'get' it"] pub struct GetOptionsBuilder { - #[serde(skip)] pub(crate) this: Object, - #[serde(skip)] pub(crate) get_function: Function, - #[serde(skip)] pub(crate) get_with_meta_function: Function, - #[serde(skip)] pub(crate) name: JsValue, + pub(crate) cache_ttl: Option, + pub(crate) value_type: Option, +} + +#[derive(Serialize)] +struct GetOptions { #[serde(rename = "cacheTtl", skip_serializing_if = "Option::is_none")] pub(crate) cache_ttl: Option, #[serde(rename = "type", skip_serializing_if = "Option::is_none")] @@ -150,8 +168,19 @@ impl GetOptionsBuilder { self } + fn options(&self) -> Result { + let ser = Serializer::json_compatible(); + Ok(GetOptions { + cache_ttl: self.cache_ttl, + value_type: self.value_type, + } + .serialize(&ser) + .map_err(JsValue::from)?) + } + async fn get(self) -> Result { - let options_object = serde_wasm_bindgen::to_value(&self).map_err(JsValue::from)?; + let options_object = self.options()?; + let promise: Promise = self .get_function .call2(&self.this, &self.name, &options_object)? @@ -194,7 +223,8 @@ impl GetOptionsBuilder { where M: DeserializeOwned, { - let options_object = serde_wasm_bindgen::to_value(&self).map_err(JsValue::from)?; + let options_object = self.options()?; + let promise: Promise = self .get_with_meta_function .call2(&self.this, &self.name, &options_object)? @@ -266,7 +296,7 @@ impl GetOptionsBuilder { } } -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, Copy)] #[serde(rename_all = "camelCase")] pub(crate) enum GetValueType { Text, diff --git a/worker-sandbox/src/kv.rs b/worker-sandbox/src/kv.rs index 927302ed8..44e7c3da7 100644 --- a/worker-sandbox/src/kv.rs +++ b/worker-sandbox/src/kv.rs @@ -1,6 +1,6 @@ use super::SomeSharedData; use serde::{Deserialize, Serialize}; -use worker::{console_debug, Env, Request, Response, Result}; +use worker::{Env, Request, Response, Result}; macro_rules! kv_assert_eq { ($left: expr, $right: expr) => {{ @@ -93,38 +93,6 @@ pub async fn put_metadata(_req: Request, env: Env, _data: SomeSharedData) -> Res Response::ok("passed") } -#[worker::send] -pub async fn put_metadata_struct( - _req: Request, - env: Env, - _data: SomeSharedData, -) -> Result { - #[derive(Debug, Default, Deserialize, Serialize, PartialEq)] - pub struct TestStruct { - pub a: String, - pub b: usize, - } - - let store = env.kv(TEST_NAMESPACE)?; - store - .put("put_b", "test")? - .metadata(TestStruct::default())? - .execute() - .await?; - - let (val, meta) = store - .get("put_b") - .text_with_metadata::() - .await?; - - kv_assert_eq!(val.unwrap(), "test")?; - console_debug!("{:?}", meta); - assert!(meta.is_some()); - // kv_assert_eq!(meta.unwrap(), TestStruct::default())?; - - Response::ok("passed") -} - #[worker::send] pub async fn put_expiration(_req: Request, env: Env, _data: SomeSharedData) -> Result { const EXPIRATION: u64 = 2000000000; @@ -148,3 +116,35 @@ pub async fn put_expiration(_req: Request, env: Env, _data: SomeSharedData) -> R Response::ok("passed") } + +#[worker::send] +pub async fn put_metadata_struct( + _req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + #[derive(Debug, Default, Deserialize, Serialize, PartialEq, Clone)] + pub struct TestStruct { + pub a: String, + pub b: usize, + } + + let put_meta = TestStruct::default(); + + let store = env.kv(TEST_NAMESPACE)?; + store + .put("put_d", "test")? + .metadata(put_meta.clone())? + .execute() + .await?; + + let (val, meta) = store + .get("put_d") + .text_with_metadata::() + .await?; + + kv_assert_eq!(val.unwrap(), "test")?; + kv_assert_eq!(meta.unwrap(), put_meta)?; + + Response::ok("passed") +}