ModelConnector/crates/claude-connector/tests/config_document.rs
Meta Vens bb8909f2c2 connector-usage-only: regenerate v4 goldens and schemas, format, fix tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 04:51:05 +01:00

288 lines
9 KiB
Rust

//! The claude-connector's configuration document and binary: what the
//! document accepts, what it refuses, and that a document of the dollar era
//! fails closed.
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::process::{Command, Output};
use std::sync::atomic::{AtomicUsize, Ordering};
use claude_connector::{
ClaudeConnectorConfig, ConfigError, LeaseCallerConfig, load_config, write_config,
};
use cultcache_rs::{CultCache, DatabaseEntry, SingleFileMessagePackBackingStore};
use model_connector::{CallerConfig, LeaseTurn};
const OPUS: &str = "claude-opus-5-1";
const HAIKU: &str = "claude-haiku-5-5";
static SEQUENCE: AtomicUsize = AtomicUsize::new(0);
struct Scratch(PathBuf);
impl Scratch {
fn new() -> Self {
let dir = std::env::temp_dir().join(format!(
"claude-connector-config-{}-{}",
std::process::id(),
SEQUENCE.fetch_add(1, Ordering::SeqCst)
));
std::fs::create_dir_all(&dir).unwrap();
Self(dir)
}
fn path(&self, name: &str) -> PathBuf {
self.0.join(name)
}
}
impl Drop for Scratch {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.0);
}
}
fn valid() -> ClaudeConnectorConfig {
ClaudeConnectorConfig {
listen: "127.0.0.1:7443".to_string(),
replay_store: "/var/lib/claude-connector/replay.cc".into(),
claude_binary: "/opt/claude/bin/claude".into(),
claude_binary_sha256: [7; 32],
work_dir: "/var/lib/claude-connector/work".into(),
config_dir: "/var/lib/claude-connector/home".into(),
prompt_dir: "/var/lib/claude-connector/prompts".into(),
instance: "persona-a".to_string(),
token_credential: "claude-token".to_string(),
handle_secret_credential: "claude-handle-secret".to_string(),
deadline_ms: 600_000,
max_line_bytes: 4 << 20,
max_total_bytes: 64 << 20,
stderr_tail_bytes: 8192,
max_frame_bytes: 2 << 20,
max_connections: 8,
socket_timeout_ms: 10_000,
max_expiry_skew_ms: 300_000,
callers: vec![CallerConfig {
caller_runtime_id: "persona-a-caller".to_string(),
connection_key_file: "/etc/claude-connector/caller.key".into(),
connection_key_epoch: 1,
allowed_models: vec![HAIKU.to_string(), OPUS.to_string()],
max_concurrent_requests: 4,
max_payload_bytes: 1 << 20,
max_output_tokens: 32_768,
}],
lease_socket: Some("/run/claude-connector/lease.sock".into()),
lease_callers: vec![LeaseCallerConfig {
unix_user: "mind-eureka-self".to_string(),
caller_id: "persona-a-caller".to_string(),
turn: LeaseTurn::SelfRun,
unit_instance: "eureka".to_string(),
}],
lease_max_ms: 3_600_000,
}
}
fn refused(config: &ClaudeConnectorConfig) -> &'static str {
match config.validate() {
Err(ConfigError::Invalid(field)) => field,
other => panic!("expected a refusal naming a field, got {other:?}"),
}
}
#[test]
fn config_round_trips_through_cultcache() {
let scratch = Scratch::new();
let path = scratch.path("connector.cc");
let config = valid();
write_config(&path, &config).unwrap();
assert_eq!(load_config(&path).unwrap(), config);
}
#[test]
fn a_valid_document_validates() {
assert!(valid().validate().is_ok());
}
/// A document of the dollar era: the first type id, carrying a price table.
#[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)]
#[cultcache(type = "gamecult.model_connector.claude_connector_config.v1")]
struct DollarEraConfig {
#[cultcache(key = 0)]
listen: String,
#[cultcache(key = 18)]
prices: BTreeMap<String, u64>,
}
#[test]
fn a_dollar_era_config_is_refused_at_load() {
let scratch = Scratch::new();
let path = scratch.path("old.cc");
{
let mut cache = CultCache::new();
cache.register_entry_type::<DollarEraConfig>().unwrap();
cache
.add_generic_backing_store(SingleFileMessagePackBackingStore::new(&path))
.unwrap();
cache
.put(
"runtime".to_string(),
&DollarEraConfig {
listen: "127.0.0.1:7443".to_string(),
prices: BTreeMap::from([(OPUS.to_string(), 5_000_000)]),
},
)
.unwrap();
}
assert!(matches!(load_config(&path), Err(ConfigError::Store)));
}
#[test]
fn the_known_models_are_the_allowed_models_by_form_alone() {
for good in [
"claude-opus-5-1",
"claude-haiku-5-5",
"claude-opus-5",
"claude-opus-5-1-20260101",
] {
let mut config = valid();
config.callers[0].allowed_models = vec![good.to_string()];
assert!(config.validate().is_ok(), "{good}");
}
for bad in [
"sonnet",
"claude-",
"claude-sonnet-latest",
"claude-Opus-5-1",
"claude-opus-5-1-2026",
"claude-opus-5-1-20260101-x",
] {
let mut config = valid();
config.callers[0].allowed_models = vec![bad.to_string()];
assert_eq!(refused(&config), "allowed_models", "{bad}");
}
let mut twice = valid();
twice.callers[0].allowed_models = vec![OPUS.to_string(), OPUS.to_string()];
assert_eq!(refused(&twice), "allowed_models");
let mut none = valid();
none.callers[0].allowed_models.clear();
assert_eq!(refused(&none), "allowed_models");
}
#[test]
fn caller_bounds_are_required_nonzero() {
let edits: [fn(&mut ClaudeConnectorConfig); 3] = [
|c| c.callers[0].max_concurrent_requests = 0,
|c| c.callers[0].max_output_tokens = 0,
|c| c.callers[0].max_payload_bytes = 0,
];
for edit in edits {
let mut config = valid();
edit(&mut config);
assert_eq!(refused(&config), "caller bounds");
}
}
#[test]
fn a_lease_caller_names_a_configured_caller_and_a_socket_exists_exactly_when_it_leases() {
let mut stranger = valid();
stranger.lease_callers[0].caller_id = "nobody".to_string();
assert_eq!(refused(&stranger), "lease_callers");
let mut no_callers = valid();
no_callers.lease_callers.clear();
assert_eq!(refused(&no_callers), "lease_callers");
let mut no_socket = valid();
no_socket.lease_socket = None;
assert_eq!(refused(&no_socket), "lease_callers");
no_socket.lease_callers.clear();
assert!(no_socket.validate().is_ok());
let mut twice = valid();
twice.lease_callers.push(twice.lease_callers[0].clone());
assert_eq!(refused(&twice), "lease_callers");
}
fn run(args: &[&str]) -> Output {
Command::new(env!("CARGO_BIN_EXE_claude-connector"))
.args(args)
.env_clear()
.output()
.unwrap()
}
fn init_flags(scratch: &Scratch, extra: &[&str]) -> Vec<String> {
let path = |name: &str| scratch.path(name).to_string_lossy().into_owned();
let mut flags: Vec<String> = [
"--listen",
"127.0.0.1:0",
"--replay-store",
&path("replay.cc"),
"--claude-binary",
&path("claude"),
"--claude-sha256",
&"ab".repeat(32),
"--work-dir",
&path("work"),
"--config-dir",
&path("home"),
"--prompt-dir",
&path("prompts"),
"--instance",
"persona-a",
"--token-credential",
"claude-token",
"--handle-secret-credential",
"claude-handle-secret",
"--handle-secret-file",
&path("handle.secret"),
"--caller-id",
"persona-a-caller",
"--connection-key-file",
&path("caller.key"),
"--connection-key-epoch",
"1",
"--allowed-model",
HAIKU,
]
.map(String::from)
.to_vec();
flags.extend(extra.iter().map(|flag| flag.to_string()));
flags
}
fn init(scratch: &Scratch, flags: &[String]) -> Output {
let config = scratch.path("connector.cc");
let mut args = vec!["config", "init", config.to_str().unwrap()];
args.extend(flags.iter().map(String::as_str));
run(&args)
}
#[test]
fn config_init_writes_the_flags_into_the_document() {
let scratch = Scratch::new();
let made = init(&scratch, &init_flags(&scratch, &[]));
assert!(made.status.success(), "{:?}", made);
let config = load_config(&scratch.path("connector.cc")).unwrap();
assert_eq!(config.callers[0].allowed_models, [HAIKU]);
assert_eq!(config.claude_binary_sha256, [0xab; 32]);
assert!(config.lease_callers.is_empty());
}
#[test]
fn the_dollar_flags_are_not_flags() {
let dollar_flags: [&[&str]; 4] = [
&["--spend-ceiling-micro-usd", "50000000"],
&["--spend-window-ms", "3600000"],
&["--price", "claude-haiku-5-5:1:1:1:1:1"],
&["--lease-reservation-micro-usd", "5000000"],
];
for extra in dollar_flags {
let scratch = Scratch::new();
let made = init(&scratch, &init_flags(&scratch, extra));
assert!(!made.status.success(), "{extra:?} was accepted");
assert!(
!Path::new(&scratch.path("connector.cc")).exists(),
"{extra:?}"
);
}
}