use std::env;
use std::path::PathBuf;
use crate::prelude::*;
use nostr::{EventBuilder, Metadata};
use rand_09::{distr::Alphanumeric, Rng};
use rstest::*;
struct TestEnv {
keys: Keys,
store_m: RdfEventsStore,
}
#[fixture]
fn test_meta() -> Metadata {
Metadata::new()
.about("About")
.name("Test")
.display_name("Test")
}
#[fixture]
fn test_env() -> TestEnv {
let rng = rand_09::rng();
let temp_id: String = rng
.sample_iter(&Alphanumeric)
.take(128)
.map(char::from)
.collect();
let mut store_path = PathBuf::from(env::temp_dir());
store_path.push(temp_id);
let store = Store::open(store_path).expect("Cannot open store");
let store_m = RdfEventsStore::new(store).unwrap();
let keys = Keys::generate();
TestEnv { keys, store_m }
}
#[fixture]
fn test_env_2() -> TestEnv {
let rng = rand_09::rng();
let temp_id: String = rng
.sample_iter(&Alphanumeric)
.take(128)
.map(char::from)
.collect();
let mut store_path = PathBuf::from(env::temp_dir());
store_path.push(temp_id);
let store_m = RdfEventsStore::new(Store::new().unwrap())
.unwrap()
.with_dump_path(Some(PathBuf::from("src/tests/store.nq")));
let keys = Keys::generate();
TestEnv { keys, store_m }
}
#[fixture]
fn hello_note() -> Note {
NoteBuilder::default()
.language(Language::EnGb)
.headline(Some("test".to_string()))
.body(Some("Hello".to_string()))
.build()
.unwrap()
}
#[rstest]
#[tokio::test]
async fn test_note_inject(
test_env: TestEnv,
test_meta: Metadata,
mut hello_note: Note,
) -> Result<(), RdfStoreError> {
let event = EventBuilder::metadata(&test_meta)
.sign(&test_env.keys)
.await
.unwrap();
test_env.store_m.insert_event(&event)?;
let event = hello_note.event_builder()?.sign(&test_env.keys).await?;
test_env.store_m.insert_event(&event)?;
let subs = [
subl(
"created_at_since",
Literal::from((Timestamp::now() - 3600).as_u64()),
)?,
subl("select_nostra", Literal::from(1))?,
subl("only_followees", Literal::from(0))?,
subl("filter_e_tag", Literal::from(0))?,
subl("lang_tag_filter", Literal::from("en-GB"))?,
];
let set =
test_env
.store_m
.run_query(&nrq_get("content_union")?, subs, None)?;
assert_eq!(set.count(), 1);
let row = set.first().unwrap();
let hl = row.get("headline").unwrap().to_string();
assert_eq!(hl, "test");
Ok(())
}
#[rstest]
#[tokio::test]
async fn test_reactions(
test_env: TestEnv,
test_meta: Metadata,
mut hello_note: Note,
) -> Result<(), RdfStoreError> {
let event_meta = EventBuilder::metadata(&test_meta)
.sign(&test_env.keys)
.await
.unwrap();
test_env.store_m.insert_event(&event_meta)?;
let note_event = hello_note.event_builder()?.sign(&test_env.keys).await?;
test_env.store_m.insert_event(¬e_event)?;
let mut reaction = EventReactionBuilder::default()
.event_id(note_event.id)
.reaction_id(ReactionType::Like)
.build()
.unwrap();
let mut reaction2 = EventReactionBuilder::default()
.event_id(note_event.id)
.reaction_id(ReactionType::Sweet)
.build()
.unwrap();
let r_event = reaction.event_builder()?.sign(&test_env.keys).await?;
test_env.store_m.insert_event(&r_event)?;
let r2_event = reaction2.event_builder()?.sign(&test_env.keys).await?;
test_env.store_m.insert_event(&r2_event)?;
let set =
test_env
.store_m
.run_query(&nrq_get("reactions_count")?, [], None)?;
assert_eq!(set.count(), 2);
let subs = [
subl(
"created_at_since",
Literal::from((Timestamp::now() - 3600).as_u64()),
)?,
subl("select_nostra", Literal::from(1))?,
subl("only_followees", Literal::from(0))?,
subl("filter_e_tag", Literal::from(0))?,
subl("lang_tag_filter", Literal::from("en-GB"))?,
];
let set =
test_env
.store_m
.run_query(&nrq_get("content_union")?, subs, None)?;
assert_eq!(set.count(), 1);
for row in &set.rows {
println!("{:?}", row.get("count_likes"));
println!("{:?}", row.get("reaction_id"));
}
Ok(())
}
#[rstest]
fn test_query_union(test_env_2: TestEnv) -> Result<(), RdfStoreError> {
if let Err(e) = test_env_2.store_m.load_from_dump() {
println!("ERR loading: {e}");
}
let subs = [
subl("filter_e_tag", Literal::from(0))?,
subl("lang_tag_filter", Literal::from(""))?,
];
match test_env_2
.store_m
.run_query(&nrq_get("content_union")?, subs, None)
{
Ok(set) => {
println!("Results count: {}", set.count());
for row in &set.rows {
println!("{row:?}");
}
}
Err(e) => println!("{e:?}"),
}
Ok(())
}
#[rstest]
fn explain_query_union(test_env_2: TestEnv) -> Result<(), RdfStoreError> {
use serde_json_path::JsonPath;
if let Err(e) = test_env_2.store_m.load_from_dump() {
eprintln!("Load error: {e}");
}
let subs = [
subl("filter_e_tag", Literal::from(0))?,
subl("lang_tag_filter", Literal::from(""))?,
];
match test_env_2
.store_m
.explain_with_subs(&nrq_get("content_union_nostr")?, subs)
{
Ok(expl) => {
let path = JsonPath::parse("$..[?(@.name)]")
.map_err(|_| RdfStoreError::QueryError)?;
let nodes = path.query(&expl).all();
for node in &nodes {
let name = node.get("name").unwrap();
let rcount = node.get("number of results").unwrap();
if let Some(dur) = node.get("duration in seconds") {
println!(
"{}: Duration: {} Count: {}",
name.as_str().unwrap(),
dur.as_f64().unwrap(),
rcount.as_i64().unwrap(),
);
}
}
Ok(())
}
Err(e) => Err(e),
}
}
#[rstest]
#[tokio::test]
async fn test_delete_previous(
test_env: TestEnv,
test_meta: Metadata,
) -> Result<(), RdfStoreError> {
for _x in 0..3 {
let meta = Metadata::new().about(_x.to_string()).name("Test");
let event_meta = EventBuilder::metadata(&meta)
.sign(&test_env.keys)
.await
.unwrap();
test_env.store_m.insert_event(&event_meta)?;
std::thread::sleep(std::time::Duration::from_secs(2));
}
let subs = [subl("query_followees", Literal::from(0))?];
let set = test_env.store_m.run_query(
&nrq_get("user_metadata")?,
subs.clone(),
None,
)?;
assert_eq!(set.count(), 3);
let event = EventBuilder::metadata(&test_meta)
.sign(&test_env.keys)
.await
.unwrap();
let _ = test_env.store_m.delete_previous_events(&event);
let set =
test_env
.store_m
.run_query(&nrq_get("user_metadata")?, subs, None)?;
assert_eq!(set.count(), 0);
Ok(())
}
#[rstest]
#[tokio::test]
async fn test_callbacks(test_env: TestEnv) -> Result<(), RdfStoreError> {
test_env
.store_m
.register_event_callback(Kind::TextNote, |_event, quads| {
println!("{quads:?}");
})
.expect("Cannot register event callback");
let note_event = EventBuilder::text_note("test")
.sign(&test_env.keys)
.await
.unwrap();
test_env.store_m.insert_event(¬e_event)?;
Ok(())
}
#[rstest]
#[tokio::test]
async fn test_database(test_env: TestEnv) -> Result<(), RdfStoreError> {
let note_event = EventBuilder::text_note("test")
.sign(&test_env.keys)
.await
.unwrap();
let _quadc = store_event(¬e_event, &test_env.store_m.store).unwrap();
let event = test_env.store_m.event_by_id(¬e_event.id).await?.unwrap();
assert_eq!(event.id, note_event.id);
event.verify().expect("Invalid event");
Ok(())
}
#[rstest]
#[tokio::test]
async fn test_delete_events_older_than(
test_env: TestEnv,
) -> Result<(), RdfStoreError> {
let note_event = EventBuilder::text_note("test")
.sign(&test_env.keys)
.await
.unwrap();
test_env.store_m.insert_event(¬e_event)?;
let long_note_event = EventBuilder::long_form_text_note("long")
.sign(&test_env.keys)
.await
.unwrap();
test_env.store_m.insert_event(&long_note_event)?;
std::thread::sleep(std::time::Duration::from_secs(2));
test_env.store_m.delete_events_older_than(
Timestamp::now(),
Some(vec![Kind::TextNote, Kind::LongFormTextNote]),
None,
)?;
let kp = NamedNode::new_unchecked("https://w3id.org/nostr#kind");
let results = test_env
.store_m
.store
.quads_for_pattern(None, Some((&kp).into()), None, None)
.collect::<Result<Vec<_>, _>>()
.map_err(|_| RdfStoreError::QueryError)?;
assert_eq!(results.len(), 0);
Ok(())
}