🐛🧪 cli, vanth: Fix SQLite store bug and add CLI tests
- Modified `Sqlite::get_all_of_ty` to return stored rows instead of empty results. - Added `Send` requirement to `Backend` trait. - Implemented `ContentHash::hex` method for hexadecimal representation. - Added integration test for CLI `write` and `get` commands with `run_vanth` helper. - Configured CLI tracing to log to stderr instead of stdout. - Added `serde` dependency to `cli` crate for test struct serialization. - Modified `handle_write` to print content hash after successful storage.
This commit is contained in:
parent
af6bfadff8
commit
1001819e4c
7 changed files with 97 additions and 5 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2107,6 +2107,7 @@ name = "vanth_cli"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|
|
@ -5,6 +5,7 @@ edition.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
vanth = { path = "../vanth" }
|
vanth = { path = "../vanth" }
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
|
|
|
@ -137,6 +137,8 @@ fn handle_write(args: &WriteArgs) {
|
||||||
eprintln!("Error writing to store: {:?}", e);
|
eprintln!("Error writing to store: {:?}", e);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
println!("{}", content_hash.hex());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_get(args: &GetArgs) {
|
fn handle_get(args: &GetArgs) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ pub use cli::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
|
.with_writer(std::io::stderr)
|
||||||
.with_max_level(tracing::Level::TRACE)
|
.with_max_level(tracing::Level::TRACE)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::{Value, json};
|
||||||
|
use tempfile::tempdir;
|
||||||
|
use vanth::{ContentHash, Vanth, hash as vanth_hash};
|
||||||
|
|
||||||
|
fn run_vanth(args: &[&str], input: Option<&str>) -> (String, String, i32) {
|
||||||
|
let mut cmd = Command::new(env!("CARGO_BIN_EXE_vanth_cli"));
|
||||||
|
cmd.args(args);
|
||||||
|
if let Some(inp) = input {
|
||||||
|
let mut child = cmd
|
||||||
|
.stdin(std::process::Stdio::piped())
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
{
|
||||||
|
let stdin = child.stdin.as_mut().unwrap();
|
||||||
|
stdin.write_all(inp.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
let output = child.wait_with_output().unwrap();
|
||||||
|
(
|
||||||
|
String::from_utf8(output.stdout).unwrap(),
|
||||||
|
String::from_utf8(output.stderr).unwrap(),
|
||||||
|
output.status.code().unwrap(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let output = cmd.output().unwrap();
|
||||||
|
(
|
||||||
|
String::from_utf8(output.stdout).unwrap(),
|
||||||
|
String::from_utf8(output.stderr).unwrap(),
|
||||||
|
output.status.code().unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, Vanth)]
|
||||||
|
struct Foo {
|
||||||
|
inner: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, Vanth)]
|
||||||
|
struct Bar {
|
||||||
|
inner: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a value in the database with the `write` command, gets its hash from the CLI stdout output, and retrieve it
|
||||||
|
/// again with the `get` command.
|
||||||
|
#[test]
|
||||||
|
fn test_write_get() {
|
||||||
|
let tempdir = tempdir().unwrap();
|
||||||
|
let db_path = tempdir.path().join("test.sqlite").to_str().unwrap().to_string();
|
||||||
|
|
||||||
|
let foo = Foo { inner: 6 };
|
||||||
|
|
||||||
|
let (stdout, stderr, exit) = run_vanth(
|
||||||
|
&[
|
||||||
|
"write",
|
||||||
|
"--db",
|
||||||
|
&db_path,
|
||||||
|
"--ty",
|
||||||
|
&Foo::ty().to_string(),
|
||||||
|
"--value",
|
||||||
|
&serde_json::to_string(&foo).unwrap(),
|
||||||
|
],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
if exit != 0 {
|
||||||
|
panic!("{}", stderr);
|
||||||
|
}
|
||||||
|
let hash = stdout.trim();
|
||||||
|
println!("x{}x", hash);
|
||||||
|
|
||||||
|
let (stdout, stderr, exit) = run_vanth(&["get", "--db", &db_path, "--ty", &Foo::ty().to_string(), &hash], None);
|
||||||
|
if exit != 0 {
|
||||||
|
panic!("{}", stderr);
|
||||||
|
}
|
||||||
|
let recovered_foo = serde_json::from_str(&stdout).unwrap();
|
||||||
|
assert_eq!(foo, recovered_foo);
|
||||||
|
}
|
|
@ -127,6 +127,12 @@ pub struct ContentHash {
|
||||||
pub hash: [u8; 32],
|
pub hash: [u8; 32],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ContentHash {
|
||||||
|
pub fn hex(&self) -> String {
|
||||||
|
self.hash.iter().map(|b| format!("{:02x}", b)).collect::<String>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Component, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Component, Serialize)]
|
||||||
pub struct Reference<T: Clone + Serialize> {
|
pub struct Reference<T: Clone + Serialize> {
|
||||||
value: ReferenceValue,
|
value: ReferenceValue,
|
||||||
|
|
|
@ -152,7 +152,7 @@ pub struct Cache {
|
||||||
// backend: Backend,
|
// backend: Backend,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Backend: std::fmt::Debug {
|
pub trait Backend: std::fmt::Debug + Send {
|
||||||
fn get_from_hash(&mut self, ty: Ty, content_hash: ContentHash) -> Result<Option<Vec<u8>>>;
|
fn get_from_hash(&mut self, ty: Ty, content_hash: ContentHash) -> Result<Option<Vec<u8>>>;
|
||||||
|
|
||||||
fn get_all_of_ty(&mut self, ty: Ty) -> Result<Vec<(ContentHash, Vec<u8>)>>;
|
fn get_all_of_ty(&mut self, ty: Ty) -> Result<Vec<(ContentHash, Vec<u8>)>>;
|
||||||
|
@ -208,8 +208,6 @@ impl Backend for Sqlite {
|
||||||
let table_name = Self::table_name(&ty);
|
let table_name = Self::table_name(&ty);
|
||||||
let query = format!("SELECT content_hash, content FROM \"{}\"", table_name);
|
let query = format!("SELECT content_hash, content FROM \"{}\"", table_name);
|
||||||
|
|
||||||
trace!("Reading table {}", table_name);
|
|
||||||
|
|
||||||
let mut statement = match transaction.prepare(&query).map_err(Into::into) {
|
let mut statement = match transaction.prepare(&query).map_err(Into::into) {
|
||||||
Err(Error::SqliteTableDoesNotExist { .. }) => return Ok(Vec::new()),
|
Err(Error::SqliteTableDoesNotExist { .. }) => return Ok(Vec::new()),
|
||||||
other => other?,
|
other => other?,
|
||||||
|
@ -229,9 +227,9 @@ impl Backend for Sqlite {
|
||||||
}
|
}
|
||||||
|
|
||||||
drop(statement);
|
drop(statement);
|
||||||
|
|
||||||
transaction.commit()?;
|
transaction.commit()?;
|
||||||
Ok(Vec::new())
|
|
||||||
|
Ok(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, ty: Ty, content_hash: ContentHash, content: Vec<u8>) -> Result<()> {
|
fn write(&mut self, ty: Ty, content_hash: ContentHash, content: Vec<u8>) -> Result<()> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue