Single-commit clean baseline after security scrub of niche-tells, project codenames, internal jargon, and contributor-email leaks. Contents: - 100 Rust crates (_primitives/_rust/) - 37 agent manifests (_manifests/) + generated specs (_generated/) - 67 user-invocable skills (skills/) - 33 hooks (hooks/) - Composition blocks (_blocks/) - Documentation (docs/, README.md) - TS adapter packages (_ts_packages/) - Assembler (_assembler/) - Roles (_roles/) - Templates (_templates/) - Forgejo CI (.forgejo/) Author: Denis Parfionovich <info@greendragon.info> License: see LICENSE.
142 lines
5 KiB
Rust
142 lines
5 KiB
Rust
//! v7 CLI binary tests for `record-cost` (Wave 44c, 2026-04-24).
|
|
//!
|
|
//! Constructor Pattern: extracted from `v6_cost.rs` so each test file
|
|
//! stays under the 200-LOC ceiling. Loads source modules via `#[path]`
|
|
//! to avoid forcing all callers through the public lib API.
|
|
|
|
#[path = "../src/migrations_list.rs"]
|
|
mod migrations_list;
|
|
#[path = "../src/schema.rs"]
|
|
mod schema;
|
|
#[path = "../src/error.rs"]
|
|
mod error;
|
|
#[path = "../src/row.rs"]
|
|
mod row;
|
|
#[path = "../src/ledger.rs"]
|
|
mod ledger;
|
|
#[path = "../src/descendants.rs"]
|
|
mod descendants;
|
|
#[path = "../src/cost.rs"]
|
|
mod cost;
|
|
|
|
use std::process::Command;
|
|
|
|
/// v6-T6: `record-cost` CLI subcommand round-trips a real binary build.
|
|
/// Under additive semantics, a single call on a fresh row still yields
|
|
/// the call's `cents` value (0 + N = N).
|
|
#[test]
|
|
fn record_cost_cli_roundtrips_through_binary() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let db = dir.path().join("ledger.sqlite");
|
|
{
|
|
let conn = ledger::open(&db).unwrap();
|
|
ledger::fork(&conn, "cli-1", "br-cli-1", None, "sha", None, None, None, None).unwrap();
|
|
}
|
|
let bin = env!("CARGO_BIN_EXE_kei-ledger");
|
|
let out = Command::new(bin)
|
|
.args([
|
|
"--db", db.to_str().unwrap(),
|
|
"record-cost", "cli-1",
|
|
"--cents", "777",
|
|
"--provider", "anthropic",
|
|
"--model", "claude-haiku-4-5-20251001",
|
|
])
|
|
.output()
|
|
.expect("kei-ledger binary failed to spawn");
|
|
assert!(
|
|
out.status.success(),
|
|
"stdout={}\nstderr={}",
|
|
String::from_utf8_lossy(&out.stdout),
|
|
String::from_utf8_lossy(&out.stderr)
|
|
);
|
|
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
assert!(stdout.contains("\"ok\":true"), "stdout={stdout}");
|
|
assert!(stdout.contains("\"agent_id\":\"cli-1\""), "stdout={stdout}");
|
|
assert!(stdout.contains("\"total_cost_cents\":777"), "stdout={stdout}");
|
|
let conn = ledger::open(&db).unwrap();
|
|
let (c, _, _) = cost::read_cost(&conn, "cli-1").unwrap().unwrap();
|
|
assert_eq!(c, 777);
|
|
}
|
|
|
|
/// v7-T6b (Wave 44c): `record-cost` CLI defaults to ADDITIVE; three
|
|
/// successive calls accumulate.
|
|
#[test]
|
|
fn record_cost_cli_accumulates_by_default() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let db = dir.path().join("ledger.sqlite");
|
|
{
|
|
let conn = ledger::open(&db).unwrap();
|
|
ledger::fork(&conn, "cli-acc", "br-cli-acc", None, "sha", None, None, None, None)
|
|
.unwrap();
|
|
}
|
|
let bin = env!("CARGO_BIN_EXE_kei-ledger");
|
|
for cents in ["100", "250", "75"] {
|
|
let out = Command::new(bin)
|
|
.args([
|
|
"--db", db.to_str().unwrap(),
|
|
"record-cost", "cli-acc",
|
|
"--cents", cents,
|
|
"--provider", "anthropic",
|
|
"--model", "claude-haiku-4-5-20251001",
|
|
])
|
|
.output()
|
|
.expect("kei-ledger binary failed to spawn");
|
|
assert!(out.status.success());
|
|
}
|
|
let conn = ledger::open(&db).unwrap();
|
|
let (c, _, _) = cost::read_cost(&conn, "cli-acc").unwrap().unwrap();
|
|
assert_eq!(c, 425, "100 + 250 + 75 accumulates");
|
|
}
|
|
|
|
/// v7-T6c: `--replace` flag restores last-write-wins behavior for
|
|
/// callers that need it (retry / amend flows).
|
|
#[test]
|
|
fn record_cost_cli_replace_flag_overrides() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let db = dir.path().join("ledger.sqlite");
|
|
{
|
|
let conn = ledger::open(&db).unwrap();
|
|
ledger::fork(&conn, "cli-rep", "br-cli-rep", None, "sha", None, None, None, None)
|
|
.unwrap();
|
|
cost::record_cost(&conn, "cli-rep", 999, "anthropic", "claude").unwrap();
|
|
}
|
|
let bin = env!("CARGO_BIN_EXE_kei-ledger");
|
|
let out = Command::new(bin)
|
|
.args([
|
|
"--db", db.to_str().unwrap(),
|
|
"record-cost", "cli-rep",
|
|
"--cents", "50",
|
|
"--provider", "openai",
|
|
"--model", "gpt-4o",
|
|
"--replace",
|
|
])
|
|
.output()
|
|
.expect("kei-ledger binary failed to spawn");
|
|
assert!(out.status.success());
|
|
let conn = ledger::open(&db).unwrap();
|
|
let (c, p, _) = cost::read_cost(&conn, "cli-rep").unwrap().unwrap();
|
|
assert_eq!(c, 50, "--replace overrides 999, not adds to it");
|
|
assert_eq!(p, "openai");
|
|
}
|
|
|
|
/// v6-T7: `record-cost` CLI on a missing agent prints to stderr and exits 1.
|
|
#[test]
|
|
fn record_cost_cli_missing_agent_exits_nonzero() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let db = dir.path().join("ledger.sqlite");
|
|
let _ = ledger::open(&db).unwrap();
|
|
let bin = env!("CARGO_BIN_EXE_kei-ledger");
|
|
let out = Command::new(bin)
|
|
.args([
|
|
"--db", db.to_str().unwrap(),
|
|
"record-cost", "ghost",
|
|
"--cents", "10",
|
|
"--provider", "x",
|
|
"--model", "y",
|
|
])
|
|
.output()
|
|
.expect("kei-ledger binary failed to spawn");
|
|
assert!(!out.status.success());
|
|
let stderr = String::from_utf8_lossy(&out.stderr);
|
|
assert!(stderr.contains("no agent with id ghost"), "stderr={stderr}");
|
|
}
|