Skip to content

Commit

Permalink
Added some test cases for d1 optionals support aka as nulls support.
Browse files Browse the repository at this point in the history
  • Loading branch information
spigaz committed Dec 9, 2024
1 parent 74678a4 commit f2c20e5
Show file tree
Hide file tree
Showing 7 changed files with 357 additions and 6 deletions.
1 change: 1 addition & 0 deletions worker-sandbox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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'] }
wasm-bindgen-test.workspace = true
worker-kv = { path = "../worker-kv" }

[dependencies.axum]
Expand Down
211 changes: 210 additions & 1 deletion worker-sandbox/src/d1.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::{js_sys::{Object, Reflect}, wasm_bindgen};
use crate::SomeSharedData;
use serde::Deserialize;
use worker::*;
use wasm_bindgen::JsValue;
use wasm_bindgen_test::wasm_bindgen_test;
use worker::{D1PreparedArgument, D1Type, Env, Error, Request, Response, Result};

#[derive(Deserialize)]
struct Person {
Expand Down Expand Up @@ -125,3 +128,209 @@ pub async fn error(_req: Request, env: Env, _data: SomeSharedData) -> Result<Res

Response::ok("")
}

#[derive(Debug, Deserialize)]
struct NullablePerson {
id: u32,
name: Option<String>,
age: Option<u32>,
}

#[wasm_bindgen_test]
pub fn test_js_value_is_null() {
assert!(JsValue::NULL.is_null());
}

#[wasm_bindgen_test]
pub fn test_serialize_option_none() {
let serializer = serde_wasm_bindgen::Serializer::new().serialize_missing_as_null(true);

let none_value: Option<String> = None;
let js_value = ::serde::ser::Serialize::serialize(&none_value, &serializer).unwrap();

assert!(js_value.is_null(), "Expected null, got {:?}", js_value);
}

#[wasm_bindgen_test]
pub fn test_deserialize_option_none() {
let js_value = Object::new();
Reflect::set(&js_value, &JsValue::from_str("id"), &JsValue::from_f64(1.0)).unwrap();
Reflect::set(&js_value, &JsValue::from_str("name"), &JsValue::NULL).unwrap();
Reflect::set(&js_value, &JsValue::from_str("age"), &JsValue::NULL).unwrap();

let js_value: JsValue = js_value.into();

let value: NullablePerson = serde_wasm_bindgen::from_value(js_value).unwrap();

assert_eq!(value.id, 1);
assert_eq!(value.name, None);
assert_eq!(value.age, None);
}

#[worker::send]
pub async fn jsvalue_null_is_null(
_req: Request,
_env: Env,
_data: SomeSharedData,
) -> Result<Response> {
console_error_panic_hook::set_once();

assert!(wasm_bindgen::JsValue::NULL.is_null());

Response::ok("ok")
}

#[worker::send]
pub async fn serialize_optional_none(
_req: Request,
_env: Env,
_data: SomeSharedData,
) -> Result<Response> {
console_error_panic_hook::set_once();
let serializer = serde_wasm_bindgen::Serializer::new().serialize_missing_as_null(true);

let none: Option<String> = None;
let js_none = ::serde::ser::Serialize::serialize(&none, &serializer).unwrap();
assert!(js_none.is_null());

Response::ok("ok")
}

#[worker::send]
pub async fn serialize_optional_some(
_req: Request,
_env: Env,
_data: SomeSharedData,
) -> Result<Response> {
console_error_panic_hook::set_once();
let serializer = serde_wasm_bindgen::Serializer::new().serialize_missing_as_null(true);

let some: Option<String> = Some("Hello".to_string());
let js_some = ::serde::ser::Serialize::serialize(&some, &serializer).unwrap();
assert!(js_some.is_string());

Response::ok("ok")
}

#[worker::send]
pub async fn deserialize_optional_none(
_req: Request,
_env: Env,
_data: SomeSharedData,
) -> Result<Response> {
console_error_panic_hook::set_once();

let js_value = Object::new();
Reflect::set(&js_value, &JsValue::from_str("id"), &JsValue::from_f64(1.0)).unwrap();
Reflect::set(&js_value, &JsValue::from_str("name"), &JsValue::NULL).unwrap();
Reflect::set(&js_value, &JsValue::from_str("age"), &JsValue::NULL).unwrap();

let js_value: JsValue = js_value.into();

let value: NullablePerson = serde_wasm_bindgen::from_value(js_value).unwrap();

assert_eq!(value.id, 1);
assert_eq!(value.name, None);
assert_eq!(value.age, None);

Response::ok("ok")
}

#[worker::send]
pub async fn insert_and_retrieve_optional_none(
_req: Request,
env: Env,
_data: SomeSharedData,
) -> Result<Response> {
let db = env.d1("DB")?;

let query = worker::query!(
&db,
"INSERT INTO nullable_people (id, name, age) VALUES (?1, ?2, ?3)",
&3,
&None::<String>,
&None::<u32>
)?;
query.run().await?;

let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 3");
let person = stmt.first::<NullablePerson>(None).await?.unwrap();
assert_eq!(person.id, 3);
assert_eq!(person.name, None);
assert_eq!(person.age, None);

Response::ok("ok")
}

#[worker::send]
pub async fn insert_and_retrieve_optional_some(
_req: Request,
env: Env,
_data: SomeSharedData,
) -> Result<Response> {
let db = env.d1("DB")?;
let query = worker::query!(
&db,
"INSERT INTO nullable_people (id, name, age) VALUES (?1, ?2, ?3)",
&4,
&"Dude",
&12
)?;
query.run().await?;

let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 4");
let person = stmt.first::<NullablePerson>(None).await?.unwrap();
assert_eq!(person.id, 4);
assert_eq!(person.name, Some("Dude".to_string()));
assert_eq!(person.age, Some(12));

Response::ok("ok")
}

#[worker::send]
pub async fn retrieve_optional_none(
_req: Request,
env: Env,
_data: SomeSharedData,
) -> Result<Response> {
let db = env.d1("DB")?;

let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 1");
let person = stmt.first::<NullablePerson>(None).await?.unwrap();
assert_eq!(person.id, 1);
assert_eq!(person.name, None);
assert_eq!(person.age, None);

Response::ok("ok")
}

#[worker::send]
pub async fn retrieve_optional_some(
_req: Request,
env: Env,
_data: SomeSharedData,
) -> Result<Response> {
let db = env.d1("DB")?;

let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 2");
let person = stmt.first::<NullablePerson>(None).await?.unwrap();
assert_eq!(person.id, 2);
assert_eq!(person.name, Some("Wynne Ogley".to_string()));
assert_eq!(person.age, Some(67));

Response::ok("ok")
}

#[worker::send]
pub async fn retrive_first_none(
_req: Request,
env: Env,
_data: SomeSharedData,
) -> Result<Response> {
let db = env.d1("DB")?;

let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 9999");
assert!(stmt.first::<NullablePerson>(None).await?.is_none());

Response::ok("ok")
}
2 changes: 1 addition & 1 deletion worker-sandbox/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ type HandlerResponse = http::Response<axum::body::Body>;
#[cfg(not(feature = "http"))]
type HandlerResponse = Response;

#[event(fetch)]
#[event(fetch, respond_with_errors)]
pub async fn main(
request: HandlerRequest,
env: Env,
Expand Down
69 changes: 69 additions & 0 deletions worker-sandbox/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,42 @@ 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(
"/d1/jsvalue_null_is_null",
get(handler!(d1::jsvalue_null_is_null)),
)
.route(
"/d1/serialize_optional_none",
get(handler!(d1::serialize_optional_none)),
)
.route(
"/d1/serialize_optional_some",
get(handler!(d1::serialize_optional_some)),
)
.route(
"/d1/deserialize_optional_none",
get(handler!(d1::deserialize_optional_none)),
)
.route(
"/d1/insert_and_retrieve_optional_none",
get(handler!(d1::insert_and_retrieve_optional_none)),
)
.route(
"/d1/insert_and_retrieve_optional_some",
get(handler!(d1::insert_and_retrieve_optional_some)),
)
.route(
"/d1/retrieve_optional_none",
get(handler!(d1::retrieve_optional_none)),
)
.route(
"/d1/retrieve_optional_some",
get(handler!(d1::retrieve_optional_some)),
)
.route(
"/d1/retrive_first_none",
get(handler!(d1::retrive_first_none)),
)
.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)))
Expand Down Expand Up @@ -337,6 +373,39 @@ 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(
"/d1/jsvalue_null_is_null",
handler!(d1::jsvalue_null_is_null),
)
.get_async(
"/d1/serialize_optional_none",
handler!(d1::serialize_optional_none),
)
.get_async(
"/d1/serialize_optional_some",
handler!(d1::serialize_optional_some),
)
.get_async(
"/d1/deserialize_optional_none",
handler!(d1::deserialize_optional_none),
)
.get_async(
"/d1/insert_and_retrieve_optional_none",
handler!(d1::insert_and_retrieve_optional_none),
)
.get_async(
"/d1/insert_and_retrieve_optional_some",
handler!(d1::insert_and_retrieve_optional_some),
)
.get_async(
"/d1/retrieve_optional_none",
handler!(d1::retrieve_optional_none),
)
.get_async(
"/d1/retrieve_optional_some",
handler!(d1::retrieve_optional_some),
)
.get_async("/d1/retrive_first_none", handler!(d1::retrive_first_none))
.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))
Expand Down
72 changes: 72 additions & 0 deletions worker-sandbox/tests/d1.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,76 @@ describe("d1", () => {
const resp = await mf.dispatchFetch("http://fake.host/d1/error");
expect(resp.status).toBe(200);
});

test("create table nullable", async () => {
let query = `CREATE TABLE IF NOT EXISTS nullable_people (
id INTEGER PRIMARY KEY,
name TEXT,
age INTEGER
);`;

expect(await exec(query)).toBe(1);

query = `INSERT OR IGNORE INTO nullable_people
(id, name, age)
VALUES
(1, NULL, NULL),
(2, 'Wynne Ogley', 67)`;

expect(await exec(query)).toBe(1);
});

test("jsvalue_null_is_null", async () => {
const resp = await mf.dispatchFetch("http://fake.host/d1/jsvalue_null_is_null");
expect(await resp.text()).toBe("ok");
expect(resp.status).toBe(200);
});

test("serialize_optional_none", async () => {
const resp = await mf.dispatchFetch("http://fake.host/d1/serialize_optional_none");
expect(await resp.text()).toBe("ok");
expect(resp.status).toBe(200);
});

test("serialize_optional_some", async () => {
const resp = await mf.dispatchFetch("http://fake.host/d1/serialize_optional_some");
expect(await resp.text()).toBe("ok");
expect(resp.status).toBe(200);
});

test("deserialize_optional_none", async () => {
const resp = await mf.dispatchFetch("http://fake.host/d1/deserialize_optional_none");
expect(await resp.text()).toBe("ok");
expect(resp.status).toBe(200);
});

test("insert_and_retrieve_optional_none", async () => {
const resp = await mf.dispatchFetch("http://fake.host/d1/insert_and_retrieve_optional_none");
expect(await resp.text()).toBe("ok");
expect(resp.status).toBe(200);
});

test("insert_and_retrieve_optional_some", async () => {
const resp = await mf.dispatchFetch("http://fake.host/d1/insert_and_retrieve_optional_some");
expect(await resp.text()).toBe("ok");
expect(resp.status).toBe(200);
});

test("retrieve_optional_none", async () => {
const resp = await mf.dispatchFetch("http://fake.host/d1/retrieve_optional_none");
expect(await resp.text()).toBe("ok");
expect(resp.status).toBe(200);
});

test("retrieve_optional_some", async () => {
const resp = await mf.dispatchFetch("http://fake.host/d1/retrieve_optional_some");
expect(await resp.text()).toBe("ok");
expect(resp.status).toBe(200);
});

test("retrive_first_none", async () => {
const resp = await mf.dispatchFetch("http://fake.host/d1/retrive_first_none");
expect(await resp.text()).toBe("ok");
expect(resp.status).toBe(200);
});
});
Loading

0 comments on commit f2c20e5

Please sign in to comment.