use anyhow::Result;
use crux_core::{macros::Effect, render::Render, testing::AppTester};
use serde::{Deserialize, Serialize};
use crate::{KeyValue, KeyValueOperation, KeyValueResponse, KeyValueResult};
#[derive(Default)]
pub struct App;
#[derive(Debug, Serialize, Deserialize)]
pub enum Event {
Get,
Set,
Delete,
Exists,
GetThenSet,
Response(KeyValueResult),
}
#[derive(Debug, Default)]
pub struct Model {
pub value: i32,
pub successful: bool,
}
#[derive(Serialize, Deserialize, Default)]
pub struct ViewModel {
pub result: String,
}
impl crux_core::App for App {
type Event = Event;
type Model = Model;
type ViewModel = ViewModel;
type Capabilities = Capabilities;
fn update(&self, event: Event, model: &mut Model, caps: &Capabilities) {
let key = "test".to_string();
match event {
Event::Get => caps.key_value.get(key, Event::Response),
Event::Set => {
caps.key_value
.set(key, 42i32.to_ne_bytes().to_vec(), Event::Response);
}
Event::Delete => caps.key_value.delete(key, Event::Response),
Event::Exists => caps.key_value.exists(key, Event::Response),
Event::GetThenSet => caps.compose.spawn(|ctx| {
let kv = caps.key_value.clone();
async move {
let KeyValueResult::Ok { response } =
kv.get_async("test_num".to_string()).await
else {
panic!("expected get response");
};
let KeyValueResponse::Get { value } = response else {
panic!("expected data response");
};
let num = i32::from_ne_bytes(value.try_into().unwrap());
let result = kv
.set_async("test_num".to_string(), (num + 1).to_ne_bytes().to_vec())
.await;
ctx.update_app(Event::Response(result))
}
}),
Event::Response(KeyValueResult::Ok { response }) => {
match response {
KeyValueResponse::Get { value } => {
let (int_bytes, _rest) = value.split_at(std::mem::size_of::<i32>());
model.value = i32::from_ne_bytes(int_bytes.try_into().unwrap());
}
KeyValueResponse::Set { previous: _ } => {
model.successful = true;
}
KeyValueResponse::Delete { previous: _ } => {
model.successful = true;
}
KeyValueResponse::Exists { is_present: _ } => {
model.successful = true;
}
}
caps.render.render()
}
Event::Response(KeyValueResult::Err { error }) => {
panic!("Error: {:?}", error);
}
}
}
fn view(&self, model: &Self::Model) -> Self::ViewModel {
ViewModel {
result: format!("Success: {}, Value: {}", model.successful, model.value),
}
}
}
#[derive(Effect)]
pub struct Capabilities {
pub key_value: KeyValue<Event>,
pub render: Render<Event>,
#[effect(skip)]
pub compose: crux_core::compose::Compose<Event>,
}
#[test]
fn test_get() {
let app = AppTester::<App, _>::default();
let mut model = Model::default();
let updated = app.update(Event::Get, &mut model);
let effect = updated.into_effects().next().unwrap();
let Effect::KeyValue(mut request) = effect else {
panic!("Expected KeyValue effect");
};
let KeyValueOperation::Get { key } = request.operation.clone() else {
panic!("Expected get operation");
};
assert_eq!(key, "test");
let updated = app
.resolve(
&mut request,
KeyValueResult::Ok {
response: KeyValueResponse::Get {
value: 42i32.to_ne_bytes().to_vec(),
},
},
)
.unwrap();
let event = updated.events.into_iter().next().unwrap();
app.update(event, &mut model);
assert_eq!(model.value, 42);
}
#[test]
fn test_set() {
let app = AppTester::<App, _>::default();
let mut model = Model::default();
let updated = app.update(Event::Set, &mut model);
let effect = updated.into_effects().next().unwrap();
let Effect::KeyValue(mut request) = effect else {
panic!("Expected KeyValue effect");
};
let KeyValueOperation::Set { key, value } = request.operation.clone() else {
panic!("Expected set operation");
};
assert_eq!(key, "test");
assert_eq!(value, 42i32.to_ne_bytes().to_vec());
let updated = app
.resolve(
&mut request,
KeyValueResult::Ok {
response: KeyValueResponse::Set { previous: vec![] },
},
)
.unwrap();
let event = updated.events.into_iter().next().unwrap();
app.update(event, &mut model);
assert!(model.successful);
}
#[test]
fn test_delete() {
let app = AppTester::<App, _>::default();
let mut model = Model::default();
let updated = app.update(Event::Delete, &mut model);
let effect = updated.into_effects().next().unwrap();
let Effect::KeyValue(mut request) = effect else {
panic!("Expected KeyValue effect");
};
let KeyValueOperation::Delete { key } = request.operation.clone() else {
panic!("Expected delete operation");
};
assert_eq!(key, "test");
let updated = app
.resolve(
&mut request,
KeyValueResult::Ok {
response: KeyValueResponse::Delete { previous: vec![] },
},
)
.unwrap();
let event = updated.events.into_iter().next().unwrap();
app.update(event, &mut model);
assert!(model.successful);
}
#[test]
fn test_exists() {
let app = AppTester::<App, _>::default();
let mut model = Model::default();
let updated = app.update(Event::Exists, &mut model);
let effect = updated.into_effects().next().unwrap();
let Effect::KeyValue(mut request) = effect else {
panic!("Expected KeyValue effect");
};
let KeyValueOperation::Exists { key } = request.operation.clone() else {
panic!("Expected exists operation");
};
assert_eq!(key, "test");
let updated = app
.resolve(
&mut request,
KeyValueResult::Ok {
response: KeyValueResponse::Exists { is_present: true },
},
)
.unwrap();
let event = updated.events.into_iter().next().unwrap();
app.update(event, &mut model);
assert!(model.successful);
}
#[test]
pub fn test_kv_async() -> Result<()> {
let app = AppTester::<App, _>::default();
let mut model = Model::default();
let update = app.update(Event::GetThenSet, &mut model);
let effect = update.into_effects().next().unwrap();
let Effect::KeyValue(mut request) = effect else {
panic!("Expected KeyValue effect");
};
let KeyValueOperation::Get { key } = request.operation.clone() else {
panic!("Expected get operation");
};
assert_eq!(key, "test_num");
let update = app
.resolve(
&mut request,
KeyValueResult::Ok {
response: KeyValueResponse::Get {
value: 17u32.to_ne_bytes().to_vec(),
},
},
)
.unwrap();
let effect = update.into_effects().next().unwrap();
let Effect::KeyValue(mut request) = effect else {
panic!("Expected KeyValue effect");
};
let KeyValueOperation::Set { key, value } = request.operation.clone() else {
panic!("Expected get operation");
};
assert_eq!(key, "test_num".to_string());
assert_eq!(value, 18u32.to_ne_bytes().to_vec());
let update = app
.resolve(
&mut request,
KeyValueResult::Ok {
response: KeyValueResponse::Set { previous: vec![] },
},
)
.unwrap();
let event = update.events.into_iter().next().unwrap();
app.update(event, &mut model);
assert!(model.successful);
Ok(())
}