#![cfg(unix)]
#![forbid(unsafe_code)]
use std::error::Error;
use std::io::Write;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use std::process::{Command, Output, Stdio};
use serde_json::{Value, json};
#[path = "support/served_child.rs"]
mod served_child;
use served_child::Running;
#[path = "support/stand_in.rs"]
mod support;
use support::{StandIn, answer, stand_in};
type TestResult = Result<(), Box<dyn Error>>;
fn start(dir: &Path) -> Result<(Running, PathBuf), Box<dyn Error>> {
std::fs::set_permissions(dir, std::fs::Permissions::from_mode(0o700))?;
let config = dir.join("serve.json");
std::fs::write(
&config,
serde_json::to_vec(&json!({
"data_dir":dir.join("database"), "socket":dir.join("serve.sock"),
"name":dir.file_name().and_then(|name| name.to_str()).ok_or("fixture name is not text")?,
"branch_cache_bytes_per_shard":1_048_576, "max_frame":1_048_576,
"max_clients":4, "max_page":20, "timeout_ms":2000,
"create":{"shards":2,"cache_bytes_total":2_097_152,"executor_threads":2}
}))?,
)?;
let running = Running::start(&config, true)?;
Ok((running, config))
}
fn haematite(command: &str, args: &[&str], input: &str) -> Result<Output, Box<dyn Error>> {
let mut child = Command::new(env!("CARGO_BIN_EXE_haematite"))
.arg(command)
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
child
.stdin
.take()
.ok_or("missing child stdin")?
.write_all(input.as_bytes())?;
Ok(child.wait_with_output()?)
}
fn text(bytes: &[u8]) -> Result<&str, Box<dyn Error>> {
Ok(std::str::from_utf8(bytes)?)
}
fn answered(output: &Output) -> Result<Value, Box<dyn Error>> {
assert_eq!(output.status.code(), Some(0), "{}", text(&output.stderr)?);
Ok(serde_json::from_slice(&output.stdout)?)
}
fn head(socket: &str) -> Result<Value, Box<dyn Error>> {
let branches = answered(&haematite(
"call",
&["--socket", socket, "branches.list", r#"{"limit":5}"#],
"",
)?)?;
let rows = branches["entries"].as_array().ok_or("missing branches")?;
Ok(rows
.iter()
.find(|row| row["branch"] == "main")
.ok_or("main missing")?["version"]
.clone())
}
fn socket_of(dir: &Path) -> Result<String, Box<dyn Error>> {
Ok(dir
.join("serve.sock")
.to_str()
.ok_or("socket path is not text")?
.to_owned())
}
fn write(socket: &str, flags: &[&str], kind: &str, value: &str) -> Result<Output, Box<dyn Error>> {
write_on(socket, "main", flags, kind, value)
}
fn write_on(
socket: &str,
branch: &str,
flags: &[&str],
kind: &str,
value: &str,
) -> Result<Output, Box<dyn Error>> {
let mut args = vec!["--socket", socket, "--branch", branch];
args.extend_from_slice(flags);
args.extend_from_slice(&[kind, "-"]);
haematite("write", &args, value)
}
fn branches(socket: &str) -> Result<Vec<String>, Box<dyn Error>> {
let page = answered(&haematite(
"call",
&["--socket", socket, "branches.list", r#"{"limit":20}"#],
"",
)?)?;
let rows = page["entries"].as_array().ok_or("missing branches")?;
Ok(rows
.iter()
.filter_map(|row| row["branch"].as_str().map(str::to_owned))
.collect())
}
fn fork_value(socket: &str, name: &str) -> Result<String, Box<dyn Error>> {
let value = json!({"name":name, "from":head(socket)?});
Ok(value.to_string())
}
fn expected(version: &Value) -> Result<String, Box<dyn Error>> {
let generation = version["generation"].as_str().ok_or("no generation")?;
let sequence = version["sequence"].as_str().ok_or("no sequence")?;
Ok(format!("{generation}.{sequence}"))
}
fn records(socket: &str) -> TestResult {
let value = r#"{"collection":"records","schema":null}"#;
answered(&write(socket, &[], "collection.put", value)?)?;
Ok(())
}
#[test]
fn a_document_put_reads_back_as_the_text_given() -> TestResult {
let dir = tempfile::tempdir()?;
let (_service, _) = start(dir.path())?;
let socket = socket_of(dir.path())?;
records(&socket)?;
let document = r#"{"big":9007199254740993,"price":1.00}"#;
let value = format!(r#"{{"collection":"records","document_id":"one","document":{document}}}"#);
let receipt = answered(&write(&socket, &[], "document.put", &value)?)?;
assert_eq!(receipt["version"], head(&socket)?);
let read = answered(&haematite(
"call",
&[
"--socket",
&socket,
"documents.get",
r#"{"selection":{"branch":"main"},"collection":"records","document_id":"one"}"#,
],
"",
)?)?;
assert_eq!(read["document"]["raw_json"], document);
Ok(())
}
#[test]
fn a_vector_put_reads_back() -> TestResult {
let dir = tempfile::tempdir()?;
let (_service, _) = start(dir.path())?;
let socket = socket_of(dir.path())?;
records(&socket)?;
let document = r#"{"collection":"records","document_id":"one","document":{}}"#;
answered(&write(&socket, &[], "document.put", document)?)?;
let vector = concat!(
r#"{"collection":"records","document_id":"one","field":"embedding","#,
r#""vector":[1.5,-2.25,0.0]}"#
);
answered(&write(&socket, &[], "vector.put", vector)?)?;
let read = answered(&haematite(
"call",
&[
"--socket",
&socket,
"vectors.get",
concat!(
r#"{"selection":{"branch":"main"},"collection":"records","#,
r#""document_id":"one","field":"embedding"}"#
),
],
"",
)?)?;
assert_eq!(read["vector"], json!([1.5, -2.25, 0.0]));
Ok(())
}
#[test]
fn a_stale_expected_is_refused_by_the_services_code() -> TestResult {
let dir = tempfile::tempdir()?;
let (_service, _) = start(dir.path())?;
let socket = socket_of(dir.path())?;
let stale = expected(&head(&socket)?)?;
records(&socket)?;
let value = r#"{"collection":"other","schema":null}"#;
let output = write(&socket, &["--expected", &stale], "collection.put", value)?;
let stderr = text(&output.stderr)?;
assert_eq!(output.status.code(), Some(2), "{stderr}");
assert!(output.stdout.is_empty());
assert!(
stderr.contains("haematite: refused version_conflict: "),
"{stderr}"
);
Ok(())
}
#[test]
fn the_same_request_id_twice_gives_the_same_receipt_and_one_version() -> TestResult {
let dir = tempfile::tempdir()?;
let (_service, _) = start(dir.path())?;
let socket = socket_of(dir.path())?;
let at = expected(&head(&socket)?)?;
let flags = ["--expected", &at, "--request-id", "note-7"];
let value = r#"{"collection":"records","schema":null}"#;
let first = write(&socket, &flags, "collection.put", value)?;
let second = write(&socket, &flags, "collection.put", value)?;
let receipt = answered(&first)?;
assert_eq!(answered(&second)?, receipt);
assert_eq!(first.stdout, second.stdout);
assert_eq!(receipt["version"], head(&socket)?);
assert!(
text(&first.stderr)?.is_empty(),
"a given request id is not printed"
);
Ok(())
}
#[test]
fn no_branch_is_a_usage_error() -> TestResult {
let output = haematite(
"write",
&["--socket", "/a.sock", "collection.put", "{}"],
"",
)?;
let stderr = text(&output.stderr)?;
assert_eq!(output.status.code(), Some(1));
assert!(output.stdout.is_empty());
assert!(
stderr.starts_with("haematite: name the branch with --branch\n"),
"{stderr}"
);
assert!(stderr.contains("usage: haematite write"), "{stderr}");
Ok(())
}
fn refuse(code: &str) -> String {
format!(r#"{{"id":"haem-call","error":{{"code":"{code}","message":"stand-in","path":null}}}}"#)
}
fn requests(service: StandIn) -> Result<Vec<Value>, Box<dyn Error>> {
let mut requests = Vec::new();
for frame in service.frames()? {
requests.push(serde_json::from_slice(
&frame.ok_or("no request was sent")?,
)?);
}
Ok(requests)
}
fn methods(requests: &[Value]) -> Vec<&str> {
requests
.iter()
.filter_map(|request| request["operation"]["method"].as_str())
.collect()
}
const RECEIPT: &str =
r#"{"request_id":"note-7","branch":"main","version":{"generation":"1","sequence":"8"}}"#;
#[test]
fn a_close_after_sending_is_settled_by_the_receipt_on_a_new_connection() -> TestResult {
let dir = tempfile::tempdir()?;
let service = stand_in(
dir.path(),
1_048_576,
vec![vec![None], vec![Some(answer(RECEIPT))]],
)?;
let socket = socket_of(dir.path())?;
let flags = ["--expected", "1.7", "--request-id", "note-7"];
let output = write(
&socket,
&flags,
"collection.put",
r#"{"collection":"records"}"#,
)?;
assert_eq!(output.status.code(), Some(0), "{}", text(&output.stderr)?);
assert_eq!(text(&output.stdout)?, format!("{RECEIPT}\n"));
let requests = requests(service)?;
assert_eq!(requests[0]["operation"]["method"], "mutation.apply");
assert_eq!(
requests[0]["operation"]["params"]["expected"],
json!({"generation":"1", "sequence":"7"})
);
assert_eq!(
requests[1]["operation"],
json!({"method":"mutation.receipt", "params":{"branch":"main", "request_id":"note-7"}})
);
Ok(())
}
#[test]
fn a_close_after_sending_with_no_receipt_is_not_known_and_names_the_request() -> TestResult {
let dir = tempfile::tempdir()?;
let service = stand_in(
dir.path(),
1_048_576,
vec![vec![None], vec![Some(answer("null"))]],
)?;
let socket = socket_of(dir.path())?;
let output = write(&socket, &["--expected", "1.7"], "collection.put", "{}")?;
let stderr = text(&output.stderr)?;
assert_eq!(output.status.code(), Some(3), "{stderr}");
assert!(output.stdout.is_empty());
let mut lines = stderr.lines();
let id = lines
.next()
.and_then(|line| line.strip_prefix("haematite: request "))
.ok_or("the made request id was not printed first")?;
assert_eq!(id.len(), 32);
assert!(id.chars().all(|digit| digit.is_ascii_hexdigit()), "{id}");
let last = lines.next().ok_or("no outcome line")?;
assert!(
last.starts_with("haematite: the outcome is not known: "),
"{last}"
);
assert!(
last.contains(&format!("--request-id {id} is safe to repeat")),
"{last}"
);
assert!(!last.contains("not applied"), "{last}");
let requests = requests(service)?;
assert_eq!(requests[0]["operation"]["params"]["request_id"], id);
assert_eq!(requests[1]["operation"]["params"]["request_id"], id);
Ok(())
}
const PAGE: &str = concat!(
r#"{"entries":[{"branch":"main","version":{"generation":"1","sequence":"4"}}],"#,
r#""next_after":null}"#
);
#[test]
fn a_given_id_with_no_receipt_reads_the_head_after_asking() -> TestResult {
let dir = tempfile::tempdir()?;
let script = vec![vec![
Some(answer("null")),
Some(answer(PAGE)),
Some(answer(RECEIPT)),
]];
let service = stand_in(dir.path(), 1_048_576, script)?;
let socket = socket_of(dir.path())?;
let output = write(&socket, &["--request-id", "note-7"], "collection.put", "{}")?;
assert_eq!(output.status.code(), Some(0), "{}", text(&output.stderr)?);
let requests = requests(service)?;
assert_eq!(
methods(&requests),
["mutation.receipt", "branches.list", "mutation.apply"]
);
assert_eq!(
requests[2]["operation"]["params"]["expected"],
json!({"generation":"1", "sequence":"4"})
);
Ok(())
}
#[test]
fn a_given_id_with_a_receipt_sends_the_version_it_was_applied_on() -> TestResult {
let dir = tempfile::tempdir()?;
let script = vec![vec![Some(answer(RECEIPT)), Some(answer(RECEIPT))]];
let service = stand_in(dir.path(), 1_048_576, script)?;
let socket = socket_of(dir.path())?;
let output = write(&socket, &["--request-id", "note-7"], "collection.put", "{}")?;
assert_eq!(output.status.code(), Some(0), "{}", text(&output.stderr)?);
assert_eq!(text(&output.stdout)?, format!("{RECEIPT}\n"));
let requests = requests(service)?;
assert_eq!(methods(&requests), ["mutation.receipt", "mutation.apply"]);
assert_eq!(
requests[1]["operation"]["params"]["expected"],
json!({"generation":"1", "sequence":"7"})
);
Ok(())
}
#[test]
fn a_made_id_asks_no_receipt_first() -> TestResult {
let dir = tempfile::tempdir()?;
let script = vec![vec![Some(answer(PAGE)), Some(answer(RECEIPT))]];
let service = stand_in(dir.path(), 1_048_576, script)?;
let socket = socket_of(dir.path())?;
let output = write(&socket, &[], "collection.put", "{}")?;
assert_eq!(output.status.code(), Some(0), "{}", text(&output.stderr)?);
assert_eq!(
methods(&requests(service)?),
["branches.list", "mutation.apply"]
);
Ok(())
}
#[test]
fn a_fork_with_a_given_id_reads_the_head_and_lets_the_service_answer() -> TestResult {
let dir = tempfile::tempdir()?;
let script = vec![vec![Some(answer(PAGE)), Some(answer(RECEIPT))]];
let service = stand_in(dir.path(), 1_048_576, script)?;
let socket = socket_of(dir.path())?;
let value = r#"{"name":"work","from":{"generation":"1","sequence":"4"}}"#;
let output = write(&socket, &["--request-id", "note-7"], "branch.fork", value)?;
assert_eq!(output.status.code(), Some(0), "{}", text(&output.stderr)?);
assert_eq!(
methods(&requests(service)?),
["branches.list", "mutation.apply"]
);
Ok(())
}
#[test]
fn a_refused_receipt_read_is_passed_on_and_nothing_is_sent_after_it() -> TestResult {
let dir = tempfile::tempdir()?;
let service = stand_in(
dir.path(),
1_048_576,
vec![vec![Some(refuse("branch_missing"))]],
)?;
let socket = socket_of(dir.path())?;
let output = write(&socket, &["--request-id", "note-7"], "collection.put", "{}")?;
let stderr = text(&output.stderr)?;
assert_eq!(output.status.code(), Some(2), "{stderr}");
assert!(
stderr.starts_with("haematite: refused branch_missing: "),
"{stderr}"
);
assert_eq!(methods(&requests(service)?), ["mutation.receipt"]);
Ok(())
}
#[test]
fn a_receipt_read_that_fails_is_could_not_ask() -> TestResult {
let dir = tempfile::tempdir()?;
let service = stand_in(dir.path(), 1_048_576, vec![vec![None]])?;
let socket = socket_of(dir.path())?;
let output = write(&socket, &["--request-id", "note-7"], "collection.put", "{}")?;
let stderr = text(&output.stderr)?;
assert_eq!(output.status.code(), Some(1), "{stderr}");
assert!(stderr.starts_with("haematite: could not ask: "), "{stderr}");
assert_eq!(methods(&requests(service)?), ["mutation.receipt"]);
Ok(())
}
#[test]
fn a_repeat_without_expected_gives_the_same_receipt() -> TestResult {
let dir = tempfile::tempdir()?;
let (_service, _) = start(dir.path())?;
let socket = socket_of(dir.path())?;
let value = r#"{"collection":"records","schema":null}"#;
let flags = ["--request-id", "declare-records"];
let first = write(&socket, &flags, "collection.put", value)?;
let second = write(&socket, &flags, "collection.put", value)?;
let receipt = answered(&first)?;
assert_eq!(second.status.code(), Some(0), "{}", text(&second.stderr)?);
assert_eq!(first.stdout, second.stdout);
assert_eq!(receipt["version"], head(&socket)?);
Ok(())
}
#[test]
fn a_repeat_without_expected_and_different_content_is_refused() -> TestResult {
let dir = tempfile::tempdir()?;
let (_service, _) = start(dir.path())?;
let socket = socket_of(dir.path())?;
let flags = ["--request-id", "declare-records"];
let value = r#"{"collection":"records","schema":null}"#;
answered(&write(&socket, &flags, "collection.put", value)?)?;
let before = head(&socket)?;
let other = r#"{"collection":"other","schema":null}"#;
let output = write(&socket, &flags, "collection.put", other)?;
let stderr = text(&output.stderr)?;
assert_eq!(output.status.code(), Some(2), "{stderr}");
assert!(
stderr.starts_with("haematite: refused request_conflict: "),
"{stderr}"
);
assert_eq!(head(&socket)?, before);
Ok(())
}
#[test]
fn an_ordinary_change_is_written_one_sequence_after_its_expected() -> TestResult {
let dir = tempfile::tempdir()?;
let (_service, _) = start(dir.path())?;
let socket = socket_of(dir.path())?;
let changes = [
(
"collection.put",
r#"{"collection":"records","schema":null}"#,
),
(
"document.put",
r#"{"collection":"records","document_id":"one","document":{}}"#,
),
(
"vector.put",
r#"{"collection":"records","document_id":"one","field":"embedding","vector":[1.0]}"#,
),
(
"documents.put_many",
r#"{"collection":"records","documents":[{"document_id":"two","document":{}}]}"#,
),
];
for (kind, value) in changes {
let before = head(&socket)?;
let at = expected(&before)?;
let receipt = answered(&write(&socket, &["--expected", &at], kind, value)?)?;
let sequence: u64 = before["sequence"].as_str().ok_or("no sequence")?.parse()?;
assert_eq!(
receipt["version"]["generation"], before["generation"],
"{kind}"
);
assert_eq!(
receipt["version"]["sequence"],
(sequence + 1).to_string(),
"{kind}"
);
}
Ok(())
}
const EMPTY: &str = r#"{"entries":[],"next_after":null}"#;
#[test]
fn an_unlisted_branch_holding_this_ids_receipt_is_not_known() -> TestResult {
let dir = tempfile::tempdir()?;
let script = vec![vec![Some(answer(EMPTY)), Some(answer(RECEIPT))]];
let service = stand_in(dir.path(), 1_048_576, script)?;
let socket = socket_of(dir.path())?;
let flags = ["--request-id", "note-7"];
let value = r#"{"name":"work"}"#;
let output = write_on(&socket, "work", &flags, "branch.discard", value)?;
let stderr = text(&output.stderr)?;
assert_eq!(output.status.code(), Some(3), "{stderr}");
assert!(output.stdout.is_empty());
assert!(
stderr.starts_with("haematite: the outcome is not known: work is not listed"),
"{stderr}"
);
assert!(
stderr.contains("holds a receipt for request note-7"),
"{stderr}"
);
assert!(
stderr.contains("with the --expected first sent"),
"{stderr}"
);
assert_eq!(
methods(&requests(service)?),
["branches.list", "mutation.receipt"]
);
Ok(())
}
#[test]
fn an_unlisted_branch_with_no_receipt_is_could_not_ask() -> TestResult {
let dir = tempfile::tempdir()?;
let script = vec![vec![Some(answer(EMPTY)), Some(answer("null"))]];
let service = stand_in(dir.path(), 1_048_576, script)?;
let socket = socket_of(dir.path())?;
let flags = ["--request-id", "note-7"];
let value = r#"{"name":"work"}"#;
let output = write_on(&socket, "work", &flags, "branch.discard", value)?;
let stderr = text(&output.stderr)?;
assert_eq!(output.status.code(), Some(1), "{stderr}");
assert!(stderr.starts_with("haematite: could not ask: "), "{stderr}");
assert!(stderr.contains("work is not listed"), "{stderr}");
assert_eq!(
methods(&requests(service)?),
["branches.list", "mutation.receipt"]
);
Ok(())
}
fn refused(output: &Output, code: &str) -> TestResult {
let stderr = text(&output.stderr)?;
assert_eq!(output.status.code(), Some(2), "{stderr}");
assert!(output.stdout.is_empty());
let refusal = format!("haematite: refused {code}: ");
let refused = stderr.lines().any(|line| line.starts_with(&refusal));
assert!(refused, "{stderr}");
Ok(())
}
#[test]
fn a_fork_from_the_head_is_listed_and_leaves_its_source_unchanged() -> TestResult {
let dir = tempfile::tempdir()?;
let (_service, _) = start(dir.path())?;
let socket = socket_of(dir.path())?;
let old = write(
&socket,
&[],
"branch.fork",
r#"{"name":"work","from":null}"#,
)?;
refused(&old, "invalid_params")?;
let before = head(&socket)?;
let value = fork_value(&socket, "work")?;
answered(&write(&socket, &[], "branch.fork", &value)?)?;
assert_eq!(branches(&socket)?, ["main", "work"]);
assert_eq!(head(&socket)?, before);
Ok(())
}
#[test]
fn a_discard_is_no_longer_listed() -> TestResult {
let dir = tempfile::tempdir()?;
let (_service, _) = start(dir.path())?;
let socket = socket_of(dir.path())?;
let work = fork_value(&socket, "work")?;
answered(&write(&socket, &[], "branch.fork", &work)?)?;
let old = write_on(&socket, "work", &[], "branch.discard", "{}")?;
refused(&old, "invalid_params")?;
let value = r#"{"name":"work"}"#;
answered(&write_on(&socket, "work", &[], "branch.discard", value)?)?;
assert_eq!(branches(&socket)?, ["main"]);
Ok(())
}
#[test]
fn a_fork_repeated_under_the_same_request_id_gives_the_same_receipt() -> TestResult {
let dir = tempfile::tempdir()?;
let (_service, _) = start(dir.path())?;
let socket = socket_of(dir.path())?;
let value = fork_value(&socket, "work")?;
let flags = ["--request-id", "fork-work"];
let first = write(&socket, &flags, "branch.fork", &value)?;
let second = write(&socket, &flags, "branch.fork", &value)?;
answered(&first)?;
answered(&second)?;
assert_eq!(first.stdout, second.stdout);
assert_eq!(branches(&socket)?, ["main", "work"]);
Ok(())
}
#[test]
fn a_discard_naming_another_branch_than_its_own_is_refused() -> TestResult {
let dir = tempfile::tempdir()?;
let (_service, _) = start(dir.path())?;
let socket = socket_of(dir.path())?;
let work = fork_value(&socket, "work")?;
answered(&write(&socket, &[], "branch.fork", &work)?)?;
let other = fork_value(&socket, "other")?;
answered(&write(&socket, &[], "branch.fork", &other)?)?;
let named = r#"{"name":"other"}"#;
let mismatch = write_on(&socket, "work", &[], "branch.discard", named)?;
refused(&mismatch, "discard_target_mismatch")?;
let main = write(&socket, &[], "branch.discard", r#"{"name":"main"}"#)?;
refused(&main, "main_protected")?;
assert_eq!(branches(&socket)?, ["main", "other", "work"]);
Ok(())
}
#[test]
fn durability_unknown_is_settled_by_the_receipt() -> TestResult {
let dir = tempfile::tempdir()?;
let script = vec![
vec![Some(refuse("durability_unknown"))],
vec![Some(answer(RECEIPT))],
];
let service = stand_in(dir.path(), 1_048_576, script)?;
let socket = socket_of(dir.path())?;
let flags = ["--expected", "1.7", "--request-id", "note-7"];
let output = write(&socket, &flags, "collection.put", "{}")?;
assert_eq!(output.status.code(), Some(0), "{}", text(&output.stderr)?);
assert_eq!(text(&output.stdout)?, format!("{RECEIPT}\n"));
let requests = requests(service)?;
assert_eq!(methods(&requests), ["mutation.apply", "mutation.receipt"]);
Ok(())
}
#[test]
fn durability_unknown_with_no_receipt_is_not_known_and_names_the_request() -> TestResult {
let dir = tempfile::tempdir()?;
let script = vec![
vec![Some(refuse("durability_unknown"))],
vec![Some(answer("null"))],
];
let service = stand_in(dir.path(), 1_048_576, script)?;
let socket = socket_of(dir.path())?;
let flags = ["--expected", "1.7", "--request-id", "note-7"];
let output = write(&socket, &flags, "collection.put", "{}")?;
let stderr = text(&output.stderr)?;
assert_eq!(output.status.code(), Some(3), "{stderr}");
assert!(output.stdout.is_empty());
assert!(
stderr.starts_with("haematite: the outcome is not known: request note-7: "),
"{stderr}"
);
assert!(stderr.contains("durability_unknown"), "{stderr}");
assert!(
stderr.contains("--request-id note-7 is safe to repeat"),
"{stderr}"
);
let requests = requests(service)?;
assert_eq!(methods(&requests), ["mutation.apply", "mutation.receipt"]);
Ok(())
}
#[test]
fn a_code_this_client_does_not_know_on_a_write_is_settled_by_the_receipt() -> TestResult {
let dir = tempfile::tempdir()?;
let script = vec![
vec![Some(refuse("brand_new_code"))],
vec![Some(answer(RECEIPT))],
];
let service = stand_in(dir.path(), 1_048_576, script)?;
let socket = socket_of(dir.path())?;
let flags = ["--expected", "1.7", "--request-id", "note-7"];
let output = write(&socket, &flags, "collection.put", "{}")?;
assert_eq!(output.status.code(), Some(0), "{}", text(&output.stderr)?);
assert_eq!(text(&output.stdout)?, format!("{RECEIPT}\n"));
let requests = requests(service)?;
assert_eq!(methods(&requests), ["mutation.apply", "mutation.receipt"]);
Ok(())
}