use std::path::PathBuf;
use cli::{
cli::commands::{
build_command_catalog, command_persists_op_id, command_uses_bootstrap_op_id_store,
observe_only_root_commands,
},
operation_id::supports_local_op_id,
};
#[test]
fn op_id_validation_is_centralized_before_dispatch() {
let main_rs = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("src")
.join("main.rs");
let source = std::fs::read_to_string(&main_rs)
.unwrap_or_else(|e| panic!("read {}: {e}", main_rs.display()));
let arms = extract_command_arms(&source);
assert!(
!arms.is_empty(),
"no Commands::<Variant> arms found in {} — has the dispatch shape changed?",
main_rs.display()
);
let resolver_call = "resolve_operation_id(&cli)?";
let resolver_count = source.matches(resolver_call).count();
assert_eq!(
resolver_count, 1,
"`{resolver_call}` must be called exactly once from the command-contract gate before dispatch"
);
let idempotency_gate = source
.find("match run_local_idempotency_if_requested")
.expect("main.rs must run the local op-id idempotency gate");
let centralized_resolver = source
.find(resolver_call)
.expect("main.rs must centrally validate op-id before dispatch");
let dispatch = source
.find("let result = match &cli.command")
.expect("main.rs must contain command dispatch");
assert!(
idempotency_gate < centralized_resolver && centralized_resolver < dispatch,
"`{resolver_call}` must run after replay/reservation and before command dispatch"
);
let mut offenders = Vec::new();
for arm in &arms {
if arm.body.contains("resolve_operation_id(") {
offenders.push(arm.variant.clone());
}
}
assert!(
offenders.is_empty(),
"op-id validation must stay centralized; remove arm-level resolver calls from: {offenders:?}"
);
}
#[test]
fn command_contract_table_drives_op_id_and_read_only_classification() {
let catalog = build_command_catalog();
let root_entries = catalog
.commands
.iter()
.filter(|entry| entry.path.len() == 1)
.collect::<Vec<_>>();
assert!(
!root_entries.is_empty(),
"command catalog returned no root entries"
);
for entry in root_entries {
let root = entry.path[0].as_str();
assert_eq!(
entry.supports_op_id,
supports_local_op_id(root),
"op-id runtime support for `{root}` must come from the command contract table"
);
assert_eq!(
entry.persists_op_id,
command_persists_op_id(root),
"op-id persistence for `{root}` must come from the command contract table"
);
assert_eq!(
entry.op_id_store_scope == "bootstrap",
command_uses_bootstrap_op_id_store(root),
"op-id store scope for `{root}` must come from the command contract table"
);
if entry.persists_op_id {
assert!(
entry.supports_op_id,
"`{root}` cannot persist op-id state unless it supports op-id replay"
);
}
if observe_only_root_commands().contains(&root) {
assert!(
entry.observe_only,
"`{root}` is read-only in op-id coverage but not observe_only in command catalog"
);
assert!(
!entry.mutates,
"`{root}` is read-only in op-id coverage but mutates in command catalog"
);
}
}
for read_only in [
"thread list",
"thread show",
"status",
"bridge git status",
"hook list",
"remote list",
"context get",
"review show",
"agent list",
] {
let entry = catalog
.commands
.iter()
.find(|entry| entry.display == read_only)
.unwrap_or_else(|| panic!("missing command catalog entry for `{read_only}`"));
assert!(
entry.observe_only,
"`{read_only}` must be observe-only in the command contract table"
);
assert!(
!entry.supports_op_id,
"`{read_only}` must not reserve local op-id slots"
);
assert_eq!(
entry.op_id_store_scope, "none",
"`{read_only}` must not use an op-id store"
);
assert!(
!supports_local_op_id(read_only),
"runtime op-id support for `{read_only}` must come from the exact command contract"
);
}
for mutating in [
"init",
"adopt",
"clone",
"thread switch",
"thread drop",
"bridge git init",
"bridge git export",
"bridge git import",
"context set",
"review sign",
"agent capture",
] {
let entry = catalog
.commands
.iter()
.find(|entry| entry.display == mutating)
.unwrap_or_else(|| panic!("missing command catalog entry for `{mutating}`"));
assert!(
entry.mutates,
"`{mutating}` must be mutating in the command contract table"
);
assert!(
supports_local_op_id(mutating),
"runtime op-id support for `{mutating}` must come from the exact command contract"
);
let expected_scope = if entry.may_initialize {
"bootstrap"
} else {
"repository"
};
assert_eq!(
entry.op_id_store_scope, expected_scope,
"`{mutating}` op-id store scope must come from the exact command contract"
);
}
}
#[derive(Debug)]
struct Arm {
variant: String,
body: String,
}
fn extract_command_arms(source: &str) -> Vec<Arm> {
let bytes = source.as_bytes();
let mut arms = Vec::new();
let dispatch_marker = "match &cli.command";
let dispatch_start = source
.find(dispatch_marker)
.expect("main.rs must contain a `match &cli.command` dispatch");
let dispatch_open_brace = dispatch_start
+ dispatch_marker.len()
+ source[dispatch_start + dispatch_marker.len()..]
.find('{')
.expect("dispatch match must have an opening brace");
let dispatch_close = match_close_brace(bytes, dispatch_open_brace)
.expect("dispatch match must have a balanced closing brace");
let scope_end = dispatch_close;
let needle = "Commands::";
let mut cursor = dispatch_open_brace + 1;
while cursor < scope_end {
let Some(rel) = source[cursor..scope_end].find(needle) else {
break;
};
let start = cursor + rel;
cursor = start + needle.len();
if cfg_attr_before_arm_is_disabled(source, start) {
continue;
}
if start > 0 {
let prev = bytes[start - 1];
if prev.is_ascii_alphanumeric() || prev == b'_' {
continue;
}
}
let variant = read_variant_name(&source[cursor..]);
if variant.is_empty() {
continue;
}
let after_variant = cursor + variant.len();
let Some(arrow_off) = find_match_arrow(&source[after_variant..]) else {
continue;
};
let arrow = after_variant + arrow_off;
let body_start = arrow + 2;
let body_text =
if let Some(brace_off) = first_non_whitespace_is_brace(&source[body_start..]) {
let block_open = body_start + brace_off;
let close = match_close_brace(bytes, block_open).unwrap_or(bytes.len());
source[block_open..=close.min(bytes.len() - 1)].to_string()
} else {
let end = expr_arm_end(&source[body_start..]);
source[body_start..body_start + end].to_string()
};
arms.push(Arm {
variant: variant.to_string(),
body: body_text,
});
}
arms
}
fn cfg_attr_before_arm_is_disabled(source: &str, arm_start: usize) -> bool {
for line in source[..arm_start].lines().rev() {
let trimmed = line.trim();
if trimmed.is_empty() {
continue;
}
if !trimmed.starts_with("#[") {
return false;
}
if trimmed.contains("cfg(feature = \"client\")") && !cfg!(feature = "client") {
return true;
}
if trimmed.contains("cfg(feature = \"git-overlay\")") && !cfg!(feature = "git-overlay") {
return true;
}
if trimmed.contains("cfg(feature = \"semantic\")") && !cfg!(feature = "semantic") {
return true;
}
}
false
}
fn read_variant_name(s: &str) -> String {
s.chars()
.take_while(|c| c.is_ascii_alphanumeric() || *c == '_')
.collect()
}
fn find_match_arrow(s: &str) -> Option<usize> {
let bytes = s.as_bytes();
let mut depth_paren: i32 = 0;
let mut depth_brace: i32 = 0;
let mut i = 0;
while i + 1 < bytes.len() {
match bytes[i] {
b'(' => depth_paren += 1,
b')' => depth_paren -= 1,
b'{' => depth_brace += 1,
b'}' => {
if depth_brace == 0 {
return None;
}
depth_brace -= 1;
}
b'=' if bytes[i + 1] == b'>' && depth_paren == 0 && depth_brace == 0 => return Some(i),
_ => {}
}
i += 1;
}
None
}
fn first_non_whitespace_is_brace(s: &str) -> Option<usize> {
for (i, c) in s.char_indices() {
if c.is_whitespace() {
continue;
}
return if c == '{' { Some(i) } else { None };
}
None
}
fn match_close_brace(bytes: &[u8], open: usize) -> Option<usize> {
let mut depth: i32 = 0;
let mut i = open;
while i < bytes.len() {
match bytes[i] {
b'{' => depth += 1,
b'}' => {
depth -= 1;
if depth == 0 {
return Some(i);
}
}
_ => {}
}
i += 1;
}
None
}
fn expr_arm_end(s: &str) -> usize {
let bytes = s.as_bytes();
let mut depth_paren: i32 = 0;
let mut depth_brace: i32 = 0;
let mut i = 0;
while i < bytes.len() {
match bytes[i] {
b'(' => depth_paren += 1,
b')' => depth_paren -= 1,
b'{' => depth_brace += 1,
b'}' => depth_brace -= 1,
b',' if depth_paren == 0 && depth_brace == 0 => return i,
_ => {}
}
i += 1;
}
bytes.len()
}