From f446feaac63f99546ad57c24dde5c468766ffa2c Mon Sep 17 00:00:00 2001 From: Sayan Nandan Date: Fri, 18 Mar 2022 22:31:56 -0700 Subject: [PATCH] Add persistence tests for lists --- .../src/tests/{persist.rs => persist/kv.rs} | 112 +++------ server/src/tests/persist/kvlist.rs | 166 ++++++++++++++ server/src/tests/persist/mod.rs | 217 ++++++++++++++++++ 3 files changed, 409 insertions(+), 86 deletions(-) rename server/src/tests/{persist.rs => persist/kv.rs} (53%) create mode 100644 server/src/tests/persist/kvlist.rs create mode 100644 server/src/tests/persist/mod.rs diff --git a/server/src/tests/persist.rs b/server/src/tests/persist/kv.rs similarity index 53% rename from server/src/tests/persist.rs rename to server/src/tests/persist/kv.rs index 32ae6ea0..b25be08c 100644 --- a/server/src/tests/persist.rs +++ b/server/src/tests/persist/kv.rs @@ -1,5 +1,5 @@ /* - * Created on Thu Mar 17 2022 + * Created on Sat Mar 19 2022 * * This file is a part of Skytable * Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source @@ -24,79 +24,19 @@ * */ +use super::{persist_load, persist_store, PERSIST_TEST_SET_SIZE}; use sky_macros::dbtest_func as dbtest; -use skytable::{aio::Connection, query, types::RawString, Element, Query, RespCode}; - -const PERIST_TEST_SET_SIZE: usize = 4; - -trait AsQueryItem { - fn push_into_query(&self, query: &mut Query); - fn as_element(&self) -> Element; -} - -impl AsQueryItem for &'static [u8] { - fn push_into_query(&self, query: &mut Query) { - query.push(RawString::from(self.to_vec())); - } - fn as_element(&self) -> Element { - Element::Binstr(self.to_vec()) - } -} - -impl AsQueryItem for &'static str { - fn push_into_query(&self, query: &mut Query) { - query.push(*self); - } - fn as_element(&self) -> Element { - Element::String(self.to_string()) - } -} - -async fn persist_store( - con: &mut Connection, - table_id: &str, - declaration: &str, - input: [(K, V); PERIST_TEST_SET_SIZE], -) { - create_table_and_switch!(con, table_id, declaration); - for (key, value) in input { - let mut query = Query::from("set"); - key.push_into_query(&mut query); - value.push_into_query(&mut query); - runeq!(con, query, Element::RespCode(RespCode::Okay)) - } -} - -async fn persist_load( - con: &mut Connection, - table_id: &str, - input: [(K, V); PERIST_TEST_SET_SIZE], -) { - switch_entity!(con, table_id); - for (key, value) in input { - let mut q = Query::from("get"); - key.push_into_query(&mut q); - runeq!(con, q, value.as_element()); - } - // now delete this table, freeing it up for the next suite run - switch_entity!(con, "default:default"); - runeq!( - con, - query!("drop", "table", table_id), - Element::RespCode(RespCode::Okay) - ); -} const PERSIST_CFG_KEYMAP_BIN_BIN_TABLE: &str = "testsuite:persist_bin_bin_tbl"; -const PERSIST_DATA_KEYMAP_BIN_BIN_TABLE: [(&[u8], &[u8]); PERIST_TEST_SET_SIZE] = [ - (b"mykey1\xF0\x90\x80", b"myval1\xF0\x90\x80"), - (b"mykey2\xF0\x90\x80", b"myval2\xF0\x90\x80"), - (b"mykey3\xF0\x90\x80", b"myval3\xF0\x90\x80"), - (b"mykey4\xF0\x90\x80", b"myval4\xF0\x90\x80"), +const PERSIST_DATA_KEYMAP_BIN_BIN_TABLE: [(&[u8], &[u8]); PERSIST_TEST_SET_SIZE] = [ + (bin!(b"mykey1"), bin!(b"myval1")), + (bin!(b"mykey2"), bin!(b"myval2")), + (bin!(b"mykey3"), bin!(b"myval3")), + (bin!(b"mykey4"), bin!(b"myval4")), ]; #[dbtest(skip_if_cfg = "persist-suite", norun = true)] -async fn persist_store_keymap_bin_bin() { +async fn store_keymap_bin_bin() { persist_store( &mut con, PERSIST_CFG_KEYMAP_BIN_BIN_TABLE, @@ -107,7 +47,7 @@ async fn persist_store_keymap_bin_bin() { } #[dbtest(run_if_cfg = "persist-suite", norun = true)] -async fn persist_load_keymap_bin_bin() { +async fn load_keymap_bin_bin() { persist_load( &mut con, PERSIST_CFG_KEYMAP_BIN_BIN_TABLE, @@ -117,15 +57,15 @@ async fn persist_load_keymap_bin_bin() { } const PERSIST_CFG_KEYMAP_BIN_STR_TABLE: &str = "testsuite:persist_bin_str_tbl"; -const PERSIST_DATA_KEYMAP_BIN_STR_TABLE: [(&[u8], &str); PERIST_TEST_SET_SIZE] = [ - (b"mykey1\xF0\x90\x80", "myval1"), - (b"mykey2\xF0\x90\x80", "myval2"), - (b"mykey3\xF0\x90\x80", "myval3"), - (b"mykey4\xF0\x90\x80", "myval4"), +const PERSIST_DATA_KEYMAP_BIN_STR_TABLE: [(&[u8], &str); PERSIST_TEST_SET_SIZE] = [ + (bin!(b"mykey1"), "myval1"), + (bin!(b"mykey2"), "myval2"), + (bin!(b"mykey3"), "myval3"), + (bin!(b"mykey4"), "myval4"), ]; #[dbtest(skip_if_cfg = "persist-suite", norun = true)] -async fn persist_store_keymap_bin_str() { +async fn store_keymap_bin_str() { persist_store( &mut con, PERSIST_CFG_KEYMAP_BIN_STR_TABLE, @@ -136,7 +76,7 @@ async fn persist_store_keymap_bin_str() { } #[dbtest(run_if_cfg = "persist-suite", norun = true)] -async fn persist_load_keymap_bin_str() { +async fn load_keymap_bin_str() { persist_load( &mut con, PERSIST_CFG_KEYMAP_BIN_STR_TABLE, @@ -146,7 +86,7 @@ async fn persist_load_keymap_bin_str() { } const PERSIST_CFG_KEYMAP_STR_STR_TABLE: &str = "testsuite:persist_str_str_tbl"; -const PERSIST_DATA_KEYMAP_STR_STR_TABLE: [(&str, &str); PERIST_TEST_SET_SIZE] = [ +const PERSIST_DATA_KEYMAP_STR_STR_TABLE: [(&str, &str); PERSIST_TEST_SET_SIZE] = [ ("mykey1", "myval1"), ("mykey2", "myval2"), ("mykey3", "myval3"), @@ -154,7 +94,7 @@ const PERSIST_DATA_KEYMAP_STR_STR_TABLE: [(&str, &str); PERIST_TEST_SET_SIZE] = ]; #[dbtest(skip_if_cfg = "persist-suite", norun = true)] -async fn persist_store_keymap_str_str() { +async fn store_keymap_str_str() { persist_store( &mut con, PERSIST_CFG_KEYMAP_STR_STR_TABLE, @@ -165,7 +105,7 @@ async fn persist_store_keymap_str_str() { } #[dbtest(run_if_cfg = "persist-suite", norun = true)] -async fn persist_load_keymap_str_str() { +async fn load_keymap_str_str() { persist_load( &mut con, PERSIST_CFG_KEYMAP_STR_STR_TABLE, @@ -175,15 +115,15 @@ async fn persist_load_keymap_str_str() { } const PERSIST_CFG_KEYMAP_STR_BIN_TABLE: &str = "testsuite:persist_str_bin_tbl"; -const PERSIST_DATA_KEYMAP_STR_BIN_TABLE: [(&str, &[u8]); PERIST_TEST_SET_SIZE] = [ - ("mykey1", b"myval1\xF0\x90\x80"), - ("mykey2", b"myval2\xF0\x90\x80"), - ("mykey3", b"myval3\xF0\x90\x80"), - ("mykey4", b"myval4\xF0\x90\x80"), +const PERSIST_DATA_KEYMAP_STR_BIN_TABLE: [(&str, &[u8]); PERSIST_TEST_SET_SIZE] = [ + ("mykey1", bin!(b"myval1")), + ("mykey2", bin!(b"myval2")), + ("mykey3", bin!(b"myval3")), + ("mykey4", bin!(b"myval4")), ]; #[dbtest(skip_if_cfg = "persist-suite", norun = true)] -async fn persist_store_keymap_str_bin() { +async fn store_keymap_str_bin() { persist_store( &mut con, PERSIST_CFG_KEYMAP_STR_BIN_TABLE, @@ -194,7 +134,7 @@ async fn persist_store_keymap_str_bin() { } #[dbtest(run_if_cfg = "persist-suite", norun = true)] -async fn persist_load_keymap_str_bin() { +async fn load_keymap_str_bin() { persist_load( &mut con, PERSIST_CFG_KEYMAP_STR_BIN_TABLE, diff --git a/server/src/tests/persist/kvlist.rs b/server/src/tests/persist/kvlist.rs new file mode 100644 index 00000000..850f6c58 --- /dev/null +++ b/server/src/tests/persist/kvlist.rs @@ -0,0 +1,166 @@ +/* + * Created on Sat Mar 19 2022 + * + * This file is a part of Skytable + * Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source + * NoSQL database written by Sayan Nandan ("the Author") with the + * vision to provide flexibility in data modelling without compromising + * on performance, queryability or scalability. + * + * Copyright (c) 2022, Sayan Nandan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * +*/ + +use super::{persist_load, persist_store, Bin, ListIDBin, ListIDStr, Str, PERSIST_TEST_SET_SIZE}; +use sky_macros::dbtest_func as dbtest; + +type ListData = [(K, [V; PERSIST_TEST_SET_SIZE]); PERSIST_TEST_SET_SIZE]; + +macro_rules! listdata { + ( + $( + $listid:expr => $element:expr + ),* + ) => { + [ + $( + ( + $listid, + $element + ), + )* + ] + }; +} + +macro_rules! binid { + ($id:expr) => { + ListIDBin(bin!($id)) + }; +} + +macro_rules! binlist { + ($($elem:expr),*) => { + [ + $( + bin!($elem), + )* + ] + }; +} + +// bin,list +const DATA_BIN_LISTBIN: ListData = listdata!( + binid!(b"list1") => binlist!(b"e1", b"e2", b"e3", b"e4"), + binid!(b"list2") => binlist!(b"e1", b"e2", b"e3", b"e4"), + binid!(b"list3") => binlist!(b"e1", b"e2", b"e3", b"e4"), + binid!(b"list4") => binlist!(b"e1", b"e2", b"e3", b"e4") +); +const TABLE_BIN_LISTBIN: &str = "testsuite:persist_bin_listbin"; + +#[dbtest(skip_if_cfg = "persist-suite", norun = true)] +async fn store_bin_bin() { + persist_store( + &mut con, + TABLE_BIN_LISTBIN, + "keymap(binstr,list)", + DATA_BIN_LISTBIN, + ) + .await; +} + +#[dbtest(run_if_cfg = "persist-suite", norun = true)] +async fn load_bin_bin() { + persist_load(&mut con, TABLE_BIN_LISTBIN, DATA_BIN_LISTBIN).await; +} + +// bin,list +const DATA_BIN_LISTSTR: ListData = listdata!( + binid!(b"list1") => ["e1", "e2", "e3", "e4"], + binid!(b"list2") => ["e1", "e2", "e3", "e4"], + binid!(b"list3") => ["e1", "e2", "e3", "e4"], + binid!(b"list4") => ["e1", "e2", "e3", "e4"] +); + +const TABLE_BIN_LISTSTR: &str = "testsuite:persist_bin_liststr"; + +#[dbtest(skip_if_cfg = "persist-suite", norun = true)] +async fn store_bin_str() { + persist_store( + &mut con, + TABLE_BIN_LISTSTR, + "keymap(binstr,list)", + DATA_BIN_LISTSTR, + ) + .await; +} + +#[dbtest(run_if_cfg = "persist-suite", norun = true)] +async fn load_bin_str() { + persist_load(&mut con, TABLE_BIN_LISTSTR, DATA_BIN_LISTSTR).await; +} + +// str,list +const DATA_STR_LISTBIN: ListData = listdata!( + ListIDStr("list1") => binlist!(b"e1", b"e2", b"e3", b"e4"), + ListIDStr("list2") => binlist!(b"e1", b"e2", b"e3", b"e4"), + ListIDStr("list3") => binlist!(b"e1", b"e2", b"e3", b"e4"), + ListIDStr("list4") => binlist!(b"e1", b"e2", b"e3", b"e4") +); + +const TABLE_STR_LISTBIN: &str = "testsuite:persist_str_listbin"; + +#[dbtest(skip_if_cfg = "persist-suite", norun = true)] +async fn store_str_bin() { + persist_store( + &mut con, + TABLE_STR_LISTBIN, + "keymap(str,list)", + DATA_STR_LISTBIN, + ) + .await; +} + +#[dbtest(run_if_cfg = "persist-suite", norun = true)] +async fn load_str_bin() { + persist_load(&mut con, TABLE_STR_LISTBIN, DATA_STR_LISTBIN).await; +} + +// str,list +const DATA_STR_LISTSTR: ListData = listdata!( + ListIDStr("list1") => ["e1", "e2", "e3", "e4"], + ListIDStr("list2") => ["e1", "e2", "e3", "e4"], + ListIDStr("list3") => ["e1", "e2", "e3", "e4"], + ListIDStr("list4") => ["e1", "e2", "e3", "e4"] +); + +const TABLE_STR_LISTSTR: &str = "testsuite:persist_str_liststr"; + +#[dbtest(skip_if_cfg = "persist-suite", norun = true)] +async fn store_str_str() { + persist_store( + &mut con, + TABLE_STR_LISTSTR, + "keymap(str,list)", + DATA_STR_LISTSTR, + ) + .await; +} + +#[dbtest(run_if_cfg = "persist-suite", norun = true)] +async fn load_str_str() { + persist_load(&mut con, TABLE_STR_LISTSTR, DATA_STR_LISTSTR).await; +} diff --git a/server/src/tests/persist/mod.rs b/server/src/tests/persist/mod.rs new file mode 100644 index 00000000..645a04f0 --- /dev/null +++ b/server/src/tests/persist/mod.rs @@ -0,0 +1,217 @@ +/* + * Created on Thu Mar 17 2022 + * + * This file is a part of Skytable + * Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source + * NoSQL database written by Sayan Nandan ("the Author") with the + * vision to provide flexibility in data modelling without compromising + * on performance, queryability or scalability. + * + * Copyright (c) 2022, Sayan Nandan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * +*/ + +use skytable::{ + aio::Connection, + query, + types::{Array, RawString}, + Element, Query, RespCode, +}; + +macro_rules! bin { + ($input:expr) => {{ + const INVALID_SEQ: [u8; 2] = *b"\x80\x81"; + const RETLEN: usize = 2 + $input.len(); + const RET0: [u8; RETLEN] = { + let mut iret: [u8; RETLEN] = [0u8; RETLEN]; + let mut idx = 0; + while idx < $input.len() { + iret[idx] = $input[idx]; + idx += 1; + } + iret[RETLEN - 2] = INVALID_SEQ[0]; + iret[RETLEN - 1] = INVALID_SEQ[1]; + iret + }; + &RET0 + }}; +} + +mod kv; +mod kvlist; + +const PERSIST_TEST_SET_SIZE: usize = 4; + +trait PushIntoQuery { + fn push_into(&self, query: &mut Query); +} + +impl PushIntoQuery for &str { + fn push_into(&self, q: &mut Query) { + q.push(*self); + } +} + +impl PushIntoQuery for &[u8] { + fn push_into(&self, q: &mut Query) { + q.push(RawString::from(self.to_vec())) + } +} + +impl PushIntoQuery for [T; N] { + fn push_into(&self, q: &mut Query) { + for element in self { + element.push_into(q) + } + } +} + +impl PushIntoQuery for &[T] { + fn push_into(&self, q: &mut Query) { + for element in self.iter() { + element.push_into(q) + } + } +} + +trait PersistKey: PushIntoQuery { + fn action_store() -> &'static str; + fn action_load() -> &'static str; +} + +macro_rules! impl_persist_key { + ($($ty:ty => ($store:expr, $load:expr)),*) => { + $(impl PersistKey for $ty { + fn action_store() -> &'static str { + $store + } + fn action_load() -> &'static str { + $load + } + })* + }; +} + +impl_persist_key!( + &str => ("set", "get"), + &[u8] => ("set", "get"), + ListIDBin => ("lset", "lget"), + ListIDStr => ("lset", "lget") +); + +trait PersistValue: PushIntoQuery { + fn response_store(&self) -> Element; + fn response_load(&self) -> Element; +} + +impl PersistValue for &str { + fn response_store(&self) -> Element { + Element::RespCode(RespCode::Okay) + } + fn response_load(&self) -> Element { + Element::String(self.to_string()) + } +} + +impl PersistValue for &[u8] { + fn response_store(&self) -> Element { + Element::RespCode(RespCode::Okay) + } + fn response_load(&self) -> Element { + Element::Binstr(self.to_vec()) + } +} + +impl PersistValue for [&[u8]; N] { + fn response_store(&self) -> Element { + Element::RespCode(RespCode::Okay) + } + fn response_load(&self) -> Element { + let mut flat = Vec::with_capacity(N); + for item in self { + flat.push(Some(item.to_vec())); + } + Element::Array(Array::Bin(flat)) + } +} + +impl PersistValue for [&str; N] { + fn response_store(&self) -> Element { + Element::RespCode(RespCode::Okay) + } + fn response_load(&self) -> Element { + let mut flat = Vec::with_capacity(N); + for item in self { + flat.push(Some(item.to_string())); + } + Element::Array(Array::Str(flat)) + } +} + +type Bin = &'static [u8]; +type Str = &'static str; + +#[derive(Debug)] +struct ListIDStr(Str); +#[derive(Debug)] +struct ListIDBin(Bin); + +impl PushIntoQuery for ListIDStr { + fn push_into(&self, q: &mut Query) { + self.0.push_into(q) + } +} + +impl PushIntoQuery for ListIDBin { + fn push_into(&self, q: &mut Query) { + self.0.push_into(q) + } +} + +async fn persist_store( + con: &mut Connection, + table_id: &str, + declaration: &str, + input: [(K, V); PERSIST_TEST_SET_SIZE], +) { + create_table_and_switch!(con, table_id, declaration); + for (key, value) in input { + let mut query = Query::from(K::action_store()); + key.push_into(&mut query); + value.push_into(&mut query); + runeq!(con, query, value.response_store()) + } +} + +async fn persist_load( + con: &mut Connection, + table_id: &str, + input: [(K, V); PERSIST_TEST_SET_SIZE], +) { + switch_entity!(con, table_id); + for (key, value) in input { + let mut q = Query::from(K::action_load()); + key.push_into(&mut q); + runeq!(con, q, value.response_load()); + } + // now delete this table, freeing it up for the next suite run + switch_entity!(con, "default:default"); + runeq!( + con, + query!("drop", "table", table_id), + Element::RespCode(RespCode::Okay) + ); +}