#[cfg(test)]
mod tests {
use nu_protocol::{PipelineData, Span, Value};
use serde_json::json;
use std::str::FromStr;
use tempfile::TempDir;
use crate::error::Error;
use crate::nu::{commands, util, Engine};
use crate::store::{Frame, Store};
fn setup_test_env() -> (Store, Engine) {
let temp_dir = TempDir::new().unwrap();
let store = Store::new(temp_dir.keep()).unwrap();
let engine = Engine::new().unwrap();
(store, engine)
}
fn nu_eval(engine: &Engine, input: PipelineData, command: impl Into<String>) -> Value {
let engine = engine.clone();
let command = command.into();
std::thread::spawn(move || {
engine
.eval(input, command)
.unwrap()
.into_value(Span::test_data())
.unwrap()
})
.join()
.unwrap()
}
fn value_to_frame(value: Value) -> Frame {
let value = util::value_to_json(&value);
serde_json::from_value(value).expect("Failed to deserialize JSON into Frame")
}
fn setup_scru128_test_env() -> Engine {
let (_store, mut engine) = setup_test_env();
engine
.add_commands(vec![Box::new(
commands::scru128_command::Scru128Command::new(),
)])
.unwrap();
engine
}
#[test]
fn test_append_command() {
let (store, mut engine) = setup_test_env();
engine
.add_commands(vec![Box::new(
commands::append_command::AppendCommand::new(
store.clone(),
json!({"base": "meta"}),
),
)])
.unwrap();
let frame = nu_eval(
&engine,
PipelineData::empty(),
r#""test content" | .append topic"#,
);
let frame = value_to_frame(frame);
assert_eq!(frame.topic, "topic");
assert_eq!(frame.meta.unwrap(), json!({"base": "meta"}));
let content = store.cas_read_sync(&frame.hash.unwrap()).unwrap();
assert_eq!(String::from_utf8(content).unwrap(), "test content");
let frame = nu_eval(
&engine,
PipelineData::empty(),
r#"{data: 123} | .append arecord"#,
);
let frame = value_to_frame(frame);
assert_eq!(frame.topic, "arecord");
assert_eq!(frame.meta.unwrap(), json!({"base": "meta"}));
let content = store.cas_read_sync(&frame.hash.unwrap()).unwrap();
let expected_json = serde_json::json!({"data": 123});
assert_eq!(
serde_json::from_slice::<serde_json::Value>(&content).unwrap(),
expected_json
);
let frame = nu_eval(
&engine,
PipelineData::empty(),
r#".append custom-meta --meta {foo: "bar"}"#,
);
let frame = value_to_frame(frame);
assert_eq!(frame.topic, "custom-meta");
assert_eq!(frame.meta.unwrap(), json!({"base": "meta", "foo": "bar"}));
assert!(frame.hash.is_none());
}
#[test]
fn test_cas_command_string() {
let (store, mut engine) = setup_test_env();
engine
.add_commands(vec![Box::new(commands::cas_command::CasCommand::new(
store.clone(),
))])
.unwrap();
let hash = store.cas_insert_sync("test content").unwrap();
let value = nu_eval(&engine, PipelineData::empty(), format!(".cas {hash}"));
let content = value.as_str().unwrap();
assert_eq!(content, "test content");
}
#[test]
fn test_cas_command_binary() {
let (store, mut engine) = setup_test_env();
engine
.add_commands(vec![Box::new(commands::cas_command::CasCommand::new(
store.clone(),
))])
.unwrap();
let binary_data = vec![0, 159, 146, 150]; let hash = store.cas_insert_sync(&binary_data).unwrap();
let value = nu_eval(&engine, PipelineData::empty(), format!(".cas {hash}"));
assert!(matches!(value, Value::Binary { .. }));
let retrieved_data = value.as_binary().unwrap();
assert_eq!(retrieved_data, &binary_data);
}
#[test]
fn test_last_command() -> Result<(), Error> {
let (store, mut engine) = setup_test_env();
engine
.add_commands(vec![Box::new(commands::last_command::LastCommand::new(
store.clone(),
))])
.unwrap();
let _frame1 = store
.append(
Frame::builder("topic")
.hash(store.cas_insert_sync("content1")?)
.build(),
)
.unwrap();
let frame2 = store
.append(
Frame::builder("topic")
.hash(store.cas_insert_sync("content2")?)
.build(),
)
.unwrap();
let last_frame = nu_eval(&engine, PipelineData::empty(), ".last topic");
assert_eq!(
last_frame.get_data_by_key("id").unwrap().as_str().unwrap(),
frame2.id.to_string()
);
Ok(())
}
#[test]
fn test_last_command_no_topic() -> Result<(), Error> {
let (store, mut engine) = setup_test_env();
engine
.add_commands(vec![Box::new(commands::last_command::LastCommand::new(
store.clone(),
))])
.unwrap();
let _frame1 = store
.append(
Frame::builder("topic_a")
.hash(store.cas_insert_sync("content1")?)
.build(),
)
.unwrap();
let frame2 = store
.append(
Frame::builder("topic_b")
.hash(store.cas_insert_sync("content2")?)
.build(),
)
.unwrap();
let last_frame = nu_eval(&engine, PipelineData::empty(), ".last");
assert_eq!(
last_frame.get_data_by_key("id").unwrap().as_str().unwrap(),
frame2.id.to_string()
);
Ok(())
}
#[test]
fn test_last_command_count() -> Result<(), Error> {
let (store, mut engine) = setup_test_env();
engine
.add_commands(vec![Box::new(commands::last_command::LastCommand::new(
store.clone(),
))])
.unwrap();
let frame1 = store
.append(
Frame::builder("topic")
.hash(store.cas_insert_sync("content1")?)
.build(),
)
.unwrap();
let frame2 = store
.append(
Frame::builder("topic")
.hash(store.cas_insert_sync("content2")?)
.build(),
)
.unwrap();
let frame3 = store
.append(
Frame::builder("topic")
.hash(store.cas_insert_sync("content3")?)
.build(),
)
.unwrap();
let result = nu_eval(&engine, PipelineData::empty(), ".last 2");
let frames = result.as_list().unwrap();
assert_eq!(frames.len(), 2);
assert_eq!(
frames[0].get_data_by_key("id").unwrap().as_str().unwrap(),
frame2.id.to_string()
);
assert_eq!(
frames[1].get_data_by_key("id").unwrap().as_str().unwrap(),
frame3.id.to_string()
);
let result = nu_eval(&engine, PipelineData::empty(), ".last 1");
assert_eq!(
result.get_data_by_key("id").unwrap().as_str().unwrap(),
frame3.id.to_string()
);
let result = nu_eval(&engine, PipelineData::empty(), ".last 10");
let frames = result.as_list().unwrap();
assert_eq!(frames.len(), 3);
assert_eq!(
frames[0].get_data_by_key("id").unwrap().as_str().unwrap(),
frame1.id.to_string()
);
Ok(())
}
#[test]
fn test_last_command_topic_with_count() -> Result<(), Error> {
let (store, mut engine) = setup_test_env();
engine
.add_commands(vec![Box::new(commands::last_command::LastCommand::new(
store.clone(),
))])
.unwrap();
let _other = store
.append(
Frame::builder("other")
.hash(store.cas_insert_sync("other")?)
.build(),
)
.unwrap();
let frame1 = store
.append(
Frame::builder("target")
.hash(store.cas_insert_sync("content1")?)
.build(),
)
.unwrap();
let frame2 = store
.append(
Frame::builder("target")
.hash(store.cas_insert_sync("content2")?)
.build(),
)
.unwrap();
let result = nu_eval(&engine, PipelineData::empty(), ".last target 2");
let frames = result.as_list().unwrap();
assert_eq!(frames.len(), 2);
assert_eq!(
frames[0].get_data_by_key("id").unwrap().as_str().unwrap(),
frame1.id.to_string()
);
assert_eq!(
frames[1].get_data_by_key("id").unwrap().as_str().unwrap(),
frame2.id.to_string()
);
Ok(())
}
#[test]
fn test_cat_command() -> Result<(), Error> {
let (store, mut engine) = setup_test_env();
engine
.add_commands(vec![Box::new(commands::cat_command::CatCommand::new(
store.clone(),
))])
.unwrap();
let _frame1 = store
.append(
Frame::builder("topic1")
.hash(store.cas_insert_sync("content1")?)
.build(),
)
.unwrap();
let _frame2 = store
.append(
Frame::builder("topic2")
.hash(store.cas_insert_sync("content2")?)
.build(),
)
.unwrap();
let value = nu_eval(&engine, PipelineData::empty(), ".cat");
let frames = value.as_list().unwrap();
assert_eq!(frames.len(), 2);
let value = nu_eval(&engine, PipelineData::empty(), ".cat --limit 1");
let frames = value.as_list().unwrap();
assert_eq!(frames.len(), 1);
let value = nu_eval(&engine, PipelineData::empty(), ".cat --topic topic2");
let frames = value.as_list().unwrap();
assert_eq!(frames.len(), 1);
assert_eq!(
frames[0]
.get_data_by_key("topic")
.unwrap()
.as_str()
.unwrap(),
"topic2"
);
Ok(())
}
#[test]
fn test_remove_command() -> Result<(), Error> {
let (store, mut engine) = setup_test_env();
engine
.add_commands(vec![Box::new(
commands::remove_command::RemoveCommand::new(store.clone()),
)])
.unwrap();
let frame = store
.append(
Frame::builder("topic")
.hash(store.cas_insert_sync("test")?)
.build(),
)
.unwrap();
nu_eval(
&engine,
PipelineData::empty(),
format!(".remove {}", frame.id),
);
assert!(store.get(&frame.id).is_none());
Ok(())
}
#[test]
fn test_get_command() -> Result<(), Error> {
let (store, mut engine) = setup_test_env();
engine
.add_commands(vec![Box::new(commands::get_command::GetCommand::new(
store.clone(),
))])
.unwrap();
let frame = store
.append(
Frame::builder("topic")
.hash(store.cas_insert_sync("test")?)
.build(),
)
.unwrap();
let retrieved_frame = nu_eval(&engine, PipelineData::empty(), format!(".get {}", frame.id));
assert_eq!(
retrieved_frame
.get_data_by_key("id")
.unwrap()
.as_str()
.unwrap(),
frame.id.to_string()
);
Ok(())
}
#[test]
fn test_scru128_generate() {
let engine = setup_scru128_test_env();
let id_value = nu_eval(&engine, PipelineData::empty(), ".id");
let id_string = id_value.as_str().unwrap();
assert!(id_string.len() > 20); assert!(scru128::Scru128Id::from_str(id_string).is_ok()); }
#[test]
fn test_scru128_unpack() {
let engine = setup_scru128_test_env();
let test_id = "03d4q1qhbiv09ovtuhokw5yxv";
let unpacked = nu_eval(
&engine,
PipelineData::empty(),
format!(".id unpack {}", test_id),
);
assert!(unpacked.as_record().is_ok());
let record = unpacked.as_record().unwrap();
assert!(record.get("timestamp").is_some());
assert!(record.get("counter_hi").is_some());
assert!(record.get("counter_lo").is_some());
assert!(record.get("node").is_some());
assert!(record.get("timestamp").unwrap().as_date().is_ok());
}
#[test]
fn test_scru128_unpack_pipeline() {
let engine = setup_scru128_test_env();
let test_id = "03d4q1qhbiv09ovtuhokw5yxv";
let unpacked = nu_eval(
&engine,
PipelineData::empty(),
format!("\"{}\" | .id unpack", test_id),
);
assert!(unpacked.as_record().is_ok());
let record = unpacked.as_record().unwrap();
assert!(record.get("timestamp").is_some());
assert!(record.get("counter_hi").is_some());
assert!(record.get("counter_lo").is_some());
assert!(record.get("node").is_some());
}
#[test]
fn test_scru128_pack() {
let engine = setup_scru128_test_env();
let components =
r#"{timestamp: (date now), counter_hi: 1234, counter_lo: 5678, node: "abcd1234"}"#;
let packed = nu_eval(
&engine,
PipelineData::empty(),
format!(".id pack {}", components),
);
let id_string = packed.as_str().unwrap();
assert!(id_string.len() > 20); assert!(scru128::Scru128Id::from_str(id_string).is_ok()); }
#[test]
fn test_scru128_pack_pipeline() {
let engine = setup_scru128_test_env();
let components =
r#"{timestamp: (date now), counter_hi: 1234, counter_lo: 5678, node: "abcd1234"}"#;
let packed = nu_eval(
&engine,
PipelineData::empty(),
format!("{} | .id pack", components),
);
let id_string = packed.as_str().unwrap();
assert!(id_string.len() > 20); assert!(scru128::Scru128Id::from_str(id_string).is_ok()); }
#[test]
fn test_scru128_round_trip() {
let engine = setup_scru128_test_env();
let original_id = nu_eval(&engine, PipelineData::empty(), ".id");
let original_id_str = original_id.as_str().unwrap();
let unpacked = nu_eval(
&engine,
PipelineData::empty(),
format!("\"{}\" | .id unpack", original_id_str),
);
let repacked = nu_eval(&engine, PipelineData::Value(unpacked, None), ".id pack");
let repacked_id_str = repacked.as_str().unwrap();
assert_eq!(original_id_str, repacked_id_str);
}
#[test]
fn test_scru128_invalid_id() {
let engine = setup_scru128_test_env();
let engine_clone = engine.clone();
let result = std::thread::spawn(move || {
engine_clone.eval(PipelineData::empty(), ".id unpack invalid_id".to_string())
})
.join();
assert!(result.is_ok());
assert!(result.unwrap().is_err());
}
#[test]
fn test_last_command_with_timestamp() -> Result<(), Error> {
let (store, mut engine) = setup_test_env();
engine
.add_commands(vec![Box::new(commands::last_command::LastCommand::new(
store.clone(),
))])
.unwrap();
let frame = store
.append(
Frame::builder("topic")
.hash(store.cas_insert_sync("content")?)
.build(),
)
.unwrap();
let result = nu_eval(&engine, PipelineData::empty(), ".last topic");
assert!(result.get_data_by_key("timestamp").is_none());
let result = nu_eval(
&engine,
PipelineData::empty(),
".last topic --with-timestamp",
);
let timestamp = result.get_data_by_key("timestamp");
assert!(timestamp.is_some());
assert!(timestamp.unwrap().as_date().is_ok());
assert_eq!(
result.get_data_by_key("id").unwrap().as_str().unwrap(),
frame.id.to_string()
);
Ok(())
}
}