Four audit findings on CLI contract violations per locked §Runtime schema:
- crit#7: invoke returned Ok with error payload — now returns
Err(InvokeError::NotImplemented) → exit 64
- crit#5: typed errors collapsed via anyhow::anyhow!("{e}") in kei-task —
replaced with CliError { code, msg } + classify_*_error helpers;
validation errors exit 2, storage errors exit 1 (spec-compliant)
- crit#8: lint.rs wikilink parser accepted [[[foo]] — strict parse_wikilink
from kei-atom-discovery used; emits finding for malformed entries
- crit#15: draft-07 detection was substring match — is_draft07_uri exact
match against canonical URIs only
Tests: 4/4 kei-runtime (was 2; +2 invoke exit-code tests) + 8/8 kei-task
(was 7; +1 empty-title exit-2 test) = 12/12 green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
32 lines
1.1 KiB
Rust
32 lines
1.1 KiB
Rust
//! kei-task CLI exit-code smoke tests (§Runtime contract).
|
|
//!
|
|
//! Atom-layer errors (validation / semantic) → exit 2.
|
|
//! Storage/IO errors → exit 1.
|
|
//!
|
|
//! `create --title ""` is the canonical validation-failure case: the
|
|
//! atom's typed Error enum returns `InvalidTitle`, which main.rs maps
|
|
//! to exit 2, NOT the old anyhow collapse at exit 1.
|
|
|
|
use std::process::Command;
|
|
|
|
const BIN: &str = env!("CARGO_BIN_EXE_kei-task");
|
|
|
|
#[test]
|
|
fn create_empty_title_exits_2() {
|
|
let tmp = tempfile::tempdir().unwrap();
|
|
let db = tmp.path().join("task.sqlite");
|
|
let out = Command::new(BIN)
|
|
.arg("--db")
|
|
.arg(&db)
|
|
.arg("create")
|
|
.arg("")
|
|
.output()
|
|
.expect("spawn kei-task");
|
|
assert_eq!(out.status.code(), Some(2),
|
|
"expected exit 2 on InvalidTitle; stdout: {}, stderr: {}",
|
|
String::from_utf8_lossy(&out.stdout),
|
|
String::from_utf8_lossy(&out.stderr));
|
|
let stderr = String::from_utf8_lossy(&out.stderr);
|
|
assert!(stderr.contains("InvalidTitle"),
|
|
"expected 'InvalidTitle' in stderr: {stderr}");
|
|
}
|