Skip to content

Commit 36b0343

Browse files
committed
Added some test cases for d1 optionals support aka as nulls support.
1 parent 8d01f4c commit 36b0343

File tree

7 files changed

+356
-5
lines changed

7 files changed

+356
-5
lines changed

worker-sandbox/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ serde-wasm-bindgen = "0.6.1"
3939
md5 = "0.7.0"
4040
tokio-stream = "0.1.15"
4141
tokio = { version = "1.28", default-features = false, features=['io-util'] }
42+
wasm-bindgen-test.workspace = true
4243
worker-kv = { path = "../worker-kv" }
4344

4445
[dependencies.axum]

worker-sandbox/src/d1.rs

Lines changed: 210 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
use crate::{js_sys::{Object, Reflect}, wasm_bindgen};
12
use crate::SomeSharedData;
23
use serde::Deserialize;
3-
use worker::*;
4+
use wasm_bindgen::JsValue;
5+
use wasm_bindgen_test::wasm_bindgen_test;
6+
use worker::{D1PreparedArgument, D1Type, Env, Error, Request, Response, Result};
47

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

126129
Response::ok("")
127130
}
131+
132+
#[derive(Debug, Deserialize)]
133+
struct NullablePerson {
134+
id: u32,
135+
name: Option<String>,
136+
age: Option<u32>,
137+
}
138+
139+
#[wasm_bindgen_test]
140+
pub fn test_js_value_is_null() {
141+
assert!(JsValue::NULL.is_null());
142+
}
143+
144+
#[wasm_bindgen_test]
145+
pub fn test_serialize_option_none() {
146+
let serializer = serde_wasm_bindgen::Serializer::new().serialize_missing_as_null(true);
147+
148+
let none_value: Option<String> = None;
149+
let js_value = ::serde::ser::Serialize::serialize(&none_value, &serializer).unwrap();
150+
151+
assert!(js_value.is_null(), "Expected null, got {:?}", js_value);
152+
}
153+
154+
#[wasm_bindgen_test]
155+
pub fn test_deserialize_option_none() {
156+
let js_value = Object::new();
157+
Reflect::set(&js_value, &JsValue::from_str("id"), &JsValue::from_f64(1.0)).unwrap();
158+
Reflect::set(&js_value, &JsValue::from_str("name"), &JsValue::NULL).unwrap();
159+
Reflect::set(&js_value, &JsValue::from_str("age"), &JsValue::NULL).unwrap();
160+
161+
let js_value: JsValue = js_value.into();
162+
163+
let value: NullablePerson = serde_wasm_bindgen::from_value(js_value).unwrap();
164+
165+
assert_eq!(value.id, 1);
166+
assert_eq!(value.name, None);
167+
assert_eq!(value.age, None);
168+
}
169+
170+
#[worker::send]
171+
pub async fn jsvalue_null_is_null(
172+
_req: Request,
173+
_env: Env,
174+
_data: SomeSharedData,
175+
) -> Result<Response> {
176+
console_error_panic_hook::set_once();
177+
178+
assert!(wasm_bindgen::JsValue::NULL.is_null());
179+
180+
Response::ok("ok")
181+
}
182+
183+
#[worker::send]
184+
pub async fn serialize_optional_none(
185+
_req: Request,
186+
_env: Env,
187+
_data: SomeSharedData,
188+
) -> Result<Response> {
189+
console_error_panic_hook::set_once();
190+
let serializer = serde_wasm_bindgen::Serializer::new().serialize_missing_as_null(true);
191+
192+
let none: Option<String> = None;
193+
let js_none = ::serde::ser::Serialize::serialize(&none, &serializer).unwrap();
194+
assert!(js_none.is_null());
195+
196+
Response::ok("ok")
197+
}
198+
199+
#[worker::send]
200+
pub async fn serialize_optional_some(
201+
_req: Request,
202+
_env: Env,
203+
_data: SomeSharedData,
204+
) -> Result<Response> {
205+
console_error_panic_hook::set_once();
206+
let serializer = serde_wasm_bindgen::Serializer::new().serialize_missing_as_null(true);
207+
208+
let some: Option<String> = Some("Hello".to_string());
209+
let js_some = ::serde::ser::Serialize::serialize(&some, &serializer).unwrap();
210+
assert!(js_some.is_string());
211+
212+
Response::ok("ok")
213+
}
214+
215+
#[worker::send]
216+
pub async fn deserialize_optional_none(
217+
_req: Request,
218+
_env: Env,
219+
_data: SomeSharedData,
220+
) -> Result<Response> {
221+
console_error_panic_hook::set_once();
222+
223+
let js_value = Object::new();
224+
Reflect::set(&js_value, &JsValue::from_str("id"), &JsValue::from_f64(1.0)).unwrap();
225+
Reflect::set(&js_value, &JsValue::from_str("name"), &JsValue::NULL).unwrap();
226+
Reflect::set(&js_value, &JsValue::from_str("age"), &JsValue::NULL).unwrap();
227+
228+
let js_value: JsValue = js_value.into();
229+
230+
let value: NullablePerson = serde_wasm_bindgen::from_value(js_value).unwrap();
231+
232+
assert_eq!(value.id, 1);
233+
assert_eq!(value.name, None);
234+
assert_eq!(value.age, None);
235+
236+
Response::ok("ok")
237+
}
238+
239+
#[worker::send]
240+
pub async fn insert_and_retrieve_optional_none(
241+
_req: Request,
242+
env: Env,
243+
_data: SomeSharedData,
244+
) -> Result<Response> {
245+
let db = env.d1("DB")?;
246+
247+
let query = worker::query!(
248+
&db,
249+
"INSERT INTO nullable_people (id, name, age) VALUES (?1, ?2, ?3)",
250+
&3,
251+
&None::<String>,
252+
&None::<u32>
253+
)?;
254+
query.run().await?;
255+
256+
let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 3");
257+
let person = stmt.first::<NullablePerson>(None).await?.unwrap();
258+
assert_eq!(person.id, 3);
259+
assert_eq!(person.name, None);
260+
assert_eq!(person.age, None);
261+
262+
Response::ok("ok")
263+
}
264+
265+
#[worker::send]
266+
pub async fn insert_and_retrieve_optional_some(
267+
_req: Request,
268+
env: Env,
269+
_data: SomeSharedData,
270+
) -> Result<Response> {
271+
let db = env.d1("DB")?;
272+
let query = worker::query!(
273+
&db,
274+
"INSERT INTO nullable_people (id, name, age) VALUES (?1, ?2, ?3)",
275+
&4,
276+
&"Dude",
277+
&12
278+
)?;
279+
query.run().await?;
280+
281+
let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 4");
282+
let person = stmt.first::<NullablePerson>(None).await?.unwrap();
283+
assert_eq!(person.id, 4);
284+
assert_eq!(person.name, Some("Dude".to_string()));
285+
assert_eq!(person.age, Some(12));
286+
287+
Response::ok("ok")
288+
}
289+
290+
#[worker::send]
291+
pub async fn retrieve_optional_none(
292+
_req: Request,
293+
env: Env,
294+
_data: SomeSharedData,
295+
) -> Result<Response> {
296+
let db = env.d1("DB")?;
297+
298+
let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 1");
299+
let person = stmt.first::<NullablePerson>(None).await?.unwrap();
300+
assert_eq!(person.id, 1);
301+
assert_eq!(person.name, None);
302+
assert_eq!(person.age, None);
303+
304+
Response::ok("ok")
305+
}
306+
307+
#[worker::send]
308+
pub async fn retrieve_optional_some(
309+
_req: Request,
310+
env: Env,
311+
_data: SomeSharedData,
312+
) -> Result<Response> {
313+
let db = env.d1("DB")?;
314+
315+
let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 2");
316+
let person = stmt.first::<NullablePerson>(None).await?.unwrap();
317+
assert_eq!(person.id, 2);
318+
assert_eq!(person.name, Some("Wynne Ogley".to_string()));
319+
assert_eq!(person.age, Some(67));
320+
321+
Response::ok("ok")
322+
}
323+
324+
#[worker::send]
325+
pub async fn retrive_first_none(
326+
_req: Request,
327+
env: Env,
328+
_data: SomeSharedData,
329+
) -> Result<Response> {
330+
let db = env.d1("DB")?;
331+
332+
let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 9999");
333+
assert!(stmt.first::<NullablePerson>(None).await?.is_none());
334+
335+
Response::ok("ok")
336+
}

worker-sandbox/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ type HandlerResponse = http::Response<axum::body::Body>;
7272
#[cfg(not(feature = "http"))]
7373
type HandlerResponse = Response;
7474

75-
#[event(fetch)]
75+
#[event(fetch, respond_with_errors)]
7676
pub async fn main(
7777
request: HandlerRequest,
7878
env: Env,

worker-sandbox/src/router.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,42 @@ pub fn make_router(data: SomeSharedData, env: Env) -> axum::Router {
197197
.route("/d1/dump", get(handler!(d1::dump)))
198198
.route("/d1/exec", post(handler!(d1::exec)))
199199
.route("/d1/error", get(handler!(d1::error)))
200+
.route(
201+
"/d1/jsvalue_null_is_null",
202+
get(handler!(d1::jsvalue_null_is_null)),
203+
)
204+
.route(
205+
"/d1/serialize_optional_none",
206+
get(handler!(d1::serialize_optional_none)),
207+
)
208+
.route(
209+
"/d1/serialize_optional_some",
210+
get(handler!(d1::serialize_optional_some)),
211+
)
212+
.route(
213+
"/d1/deserialize_optional_none",
214+
get(handler!(d1::deserialize_optional_none)),
215+
)
216+
.route(
217+
"/d1/insert_and_retrieve_optional_none",
218+
get(handler!(d1::insert_and_retrieve_optional_none)),
219+
)
220+
.route(
221+
"/d1/insert_and_retrieve_optional_some",
222+
get(handler!(d1::insert_and_retrieve_optional_some)),
223+
)
224+
.route(
225+
"/d1/retrieve_optional_none",
226+
get(handler!(d1::retrieve_optional_none)),
227+
)
228+
.route(
229+
"/d1/retrieve_optional_some",
230+
get(handler!(d1::retrieve_optional_some)),
231+
)
232+
.route(
233+
"/d1/retrive_first_none",
234+
get(handler!(d1::retrive_first_none)),
235+
)
200236
.route("/kv/get", get(handler!(kv::get)))
201237
.route("/kv/get-not-found", get(handler!(kv::get_not_found)))
202238
.route("/kv/list-keys", get(handler!(kv::list_keys)))
@@ -339,6 +375,39 @@ pub fn make_router<'a>(data: SomeSharedData) -> Router<'a, SomeSharedData> {
339375
.get_async("/d1/dump", handler!(d1::dump))
340376
.post_async("/d1/exec", handler!(d1::exec))
341377
.get_async("/d1/error", handler!(d1::error))
378+
.get_async(
379+
"/d1/jsvalue_null_is_null",
380+
handler!(d1::jsvalue_null_is_null),
381+
)
382+
.get_async(
383+
"/d1/serialize_optional_none",
384+
handler!(d1::serialize_optional_none),
385+
)
386+
.get_async(
387+
"/d1/serialize_optional_some",
388+
handler!(d1::serialize_optional_some),
389+
)
390+
.get_async(
391+
"/d1/deserialize_optional_none",
392+
handler!(d1::deserialize_optional_none),
393+
)
394+
.get_async(
395+
"/d1/insert_and_retrieve_optional_none",
396+
handler!(d1::insert_and_retrieve_optional_none),
397+
)
398+
.get_async(
399+
"/d1/insert_and_retrieve_optional_some",
400+
handler!(d1::insert_and_retrieve_optional_some),
401+
)
402+
.get_async(
403+
"/d1/retrieve_optional_none",
404+
handler!(d1::retrieve_optional_none),
405+
)
406+
.get_async(
407+
"/d1/retrieve_optional_some",
408+
handler!(d1::retrieve_optional_some),
409+
)
410+
.get_async("/d1/retrive_first_none", handler!(d1::retrive_first_none))
342411
.get_async("/kv/get", handler!(kv::get))
343412
.get_async("/kv/get-not-found", handler!(kv::get_not_found))
344413
.get_async("/kv/list-keys", handler!(kv::list_keys))

worker-sandbox/tests/d1.spec.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,76 @@ describe("d1", () => {
5959
const resp = await mf.dispatchFetch("http://fake.host/d1/error");
6060
expect(resp.status).toBe(200);
6161
});
62+
63+
test("create table nullable", async () => {
64+
let query = `CREATE TABLE IF NOT EXISTS nullable_people (
65+
id INTEGER PRIMARY KEY,
66+
name TEXT,
67+
age INTEGER
68+
);`;
69+
70+
expect(await exec(query)).toBe(1);
71+
72+
query = `INSERT OR IGNORE INTO nullable_people
73+
(id, name, age)
74+
VALUES
75+
(1, NULL, NULL),
76+
(2, 'Wynne Ogley', 67)`;
77+
78+
expect(await exec(query)).toBe(1);
79+
});
80+
81+
test("jsvalue_null_is_null", async () => {
82+
const resp = await mf.dispatchFetch("http://fake.host/d1/jsvalue_null_is_null");
83+
expect(await resp.text()).toBe("ok");
84+
expect(resp.status).toBe(200);
85+
});
86+
87+
test("serialize_optional_none", async () => {
88+
const resp = await mf.dispatchFetch("http://fake.host/d1/serialize_optional_none");
89+
expect(await resp.text()).toBe("ok");
90+
expect(resp.status).toBe(200);
91+
});
92+
93+
test("serialize_optional_some", async () => {
94+
const resp = await mf.dispatchFetch("http://fake.host/d1/serialize_optional_some");
95+
expect(await resp.text()).toBe("ok");
96+
expect(resp.status).toBe(200);
97+
});
98+
99+
test("deserialize_optional_none", async () => {
100+
const resp = await mf.dispatchFetch("http://fake.host/d1/deserialize_optional_none");
101+
expect(await resp.text()).toBe("ok");
102+
expect(resp.status).toBe(200);
103+
});
104+
105+
test("insert_and_retrieve_optional_none", async () => {
106+
const resp = await mf.dispatchFetch("http://fake.host/d1/insert_and_retrieve_optional_none");
107+
expect(await resp.text()).toBe("ok");
108+
expect(resp.status).toBe(200);
109+
});
110+
111+
test("insert_and_retrieve_optional_some", async () => {
112+
const resp = await mf.dispatchFetch("http://fake.host/d1/insert_and_retrieve_optional_some");
113+
expect(await resp.text()).toBe("ok");
114+
expect(resp.status).toBe(200);
115+
});
116+
117+
test("retrieve_optional_none", async () => {
118+
const resp = await mf.dispatchFetch("http://fake.host/d1/retrieve_optional_none");
119+
expect(await resp.text()).toBe("ok");
120+
expect(resp.status).toBe(200);
121+
});
122+
123+
test("retrieve_optional_some", async () => {
124+
const resp = await mf.dispatchFetch("http://fake.host/d1/retrieve_optional_some");
125+
expect(await resp.text()).toBe("ok");
126+
expect(resp.status).toBe(200);
127+
});
128+
129+
test("retrive_first_none", async () => {
130+
const resp = await mf.dispatchFetch("http://fake.host/d1/retrive_first_none");
131+
expect(await resp.text()).toBe("ok");
132+
expect(resp.status).toBe(200);
133+
});
62134
});

0 commit comments

Comments
 (0)