Files
clawmates/crates/cm-config/tests/config.rs
T
Omar SobhandClaude Fable 5 add4f79fed Rebrand: TeamClaw -> Clawmates (clawmates.work)
Full-depth rename per the approved plan; the 'claw' product vocabulary
(claws, /claws routes, clawId, Claw Chat) stays — it is now the brand.

- Display brand: Clawmates (manifest, titles, hero, login/rail logo
  'clawmates'); default host app.clawmates.work; registry
  ghcr.io/clawmates
- Crates tc-* -> cm-* (16 crates + all imports); binaries
  clawmates-server/broker/bundler; images clawmates/*; env prefix
  CLAWMATES_* (+ CM_TEST_DATABASE_URL / CM_LIVE_LLM); config
  clawmates.toml; helm chart deploy/helm/clawmates with clawmates-*
  resources; db names clawmates*; sockets /run/clawmates; cookie
  cm_session; kind cluster clawmates-test; seccomp node profile
  clawmates-agent-profile.json
- All 9 Playwright brand assertions updated in lockstep; historical
  spec document left untouched as the only remaining 'TeamClaw'
- Local env migrated: dev pg clawmates-dev-pg/clawmates_dev, shared
  test server clawmates-test-pg, kind cluster recreated with image +
  profile, compose images rebuilt under clawmates/*

Verified end to end: 161 Rust + 68 frontend tests, 29 Playwright
journeys, 4 live kind tests, helm/install/LOC/placeholder gates, and
the clean-room install rehearsal serving the clawmates login page from
a signed bundle of the rebuilt images.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-10 12:31:25 -05:00

176 lines
5.9 KiB
Rust

// figment::Jail::expect_with dictates closures returning figment's own
// (large) error type; the lint has nothing actionable here.
#![allow(clippy::result_large_err)]
use cm_config::{AppConfig, AuthMode, ConfigError, DeployTarget, LlmProviderKind};
const AIR_GAPPED_TOML: &str = r#"
deploy_target = "air_gapped"
listen_addr = "0.0.0.0:8080"
[database]
url = "postgres://clawmates:pw@db:5432/clawmates"
[llm]
provider = "openai_compat"
base_url = "http://local-llm:8000/v1"
model = "qwen2.5-72b-instruct"
[auth]
mode = "local"
"#;
const CLOUD_TOML: &str = r#"
deploy_target = "cloud"
listen_addr = "0.0.0.0:8080"
[database]
url = "postgres://clawmates:pw@db:5432/clawmates"
max_connections = 32
[llm]
provider = "anthropic"
model = "claude-sonnet-4-5"
[auth]
mode = "oidc"
issuer_url = "https://idp.example.com"
client_id = "clawmates"
"#;
#[test]
fn loads_air_gapped_config_from_toml() {
figment::Jail::expect_with(|jail| {
jail.create_file("clawmates.toml", AIR_GAPPED_TOML)?;
let cfg = AppConfig::load_from(&jail.directory().join("clawmates.toml")).unwrap();
assert_eq!(cfg.deploy_target, DeployTarget::AirGapped);
assert_eq!(cfg.listen_addr.port(), 8080);
assert_eq!(
cfg.database.url,
"postgres://clawmates:pw@db:5432/clawmates"
);
assert_eq!(cfg.llm.provider, LlmProviderKind::OpenAiCompat);
assert_eq!(
cfg.llm.base_url.as_deref(),
Some("http://local-llm:8000/v1")
);
assert_eq!(cfg.auth.mode, AuthMode::Local);
Ok(())
});
}
#[test]
fn loads_cloud_config_with_oidc() {
figment::Jail::expect_with(|jail| {
jail.create_file("clawmates.toml", CLOUD_TOML)?;
let cfg = AppConfig::load_from(&jail.directory().join("clawmates.toml")).unwrap();
assert_eq!(cfg.deploy_target, DeployTarget::Cloud);
assert_eq!(cfg.database.max_connections, 32);
assert_eq!(cfg.llm.provider, LlmProviderKind::Anthropic);
assert_eq!(cfg.auth.mode, AuthMode::Oidc);
assert_eq!(
cfg.auth.issuer_url.as_deref(),
Some("https://idp.example.com")
);
Ok(())
});
}
#[test]
fn env_overrides_toml_values() {
figment::Jail::expect_with(|jail| {
jail.create_file("clawmates.toml", AIR_GAPPED_TOML)?;
jail.set_env("CLAWMATES_DATABASE__URL", "postgres://other:pw@x:5432/y");
jail.set_env("CLAWMATES_LLM__MODEL", "llama-3.1-8b-instruct");
let cfg = AppConfig::load_from(&jail.directory().join("clawmates.toml")).unwrap();
assert_eq!(cfg.database.url, "postgres://other:pw@x:5432/y");
assert_eq!(cfg.llm.model, "llama-3.1-8b-instruct");
Ok(())
});
}
#[test]
fn database_max_connections_defaults_to_ten() {
figment::Jail::expect_with(|jail| {
jail.create_file("clawmates.toml", AIR_GAPPED_TOML)?;
let cfg = AppConfig::load_from(&jail.directory().join("clawmates.toml")).unwrap();
assert_eq!(cfg.database.max_connections, 10);
Ok(())
});
}
#[test]
fn openai_compat_without_base_url_is_rejected() {
figment::Jail::expect_with(|jail| {
let toml = AIR_GAPPED_TOML.replace("base_url = \"http://local-llm:8000/v1\"\n", "");
jail.create_file("clawmates.toml", &toml)?;
let err = AppConfig::load_from(&jail.directory().join("clawmates.toml")).unwrap_err();
assert!(matches!(err, ConfigError::Invalid(msg) if msg.contains("base_url")));
Ok(())
});
}
#[test]
fn scripted_provider_requires_scenario_path() {
figment::Jail::expect_with(|jail| {
let toml = AIR_GAPPED_TOML
.replace("provider = \"openai_compat\"", "provider = \"scripted\"")
.replace("base_url = \"http://local-llm:8000/v1\"\n", "");
jail.create_file("clawmates.toml", &toml)?;
let err = AppConfig::load_from(&jail.directory().join("clawmates.toml")).unwrap_err();
assert!(matches!(err, ConfigError::Invalid(msg) if msg.contains("scenario_path")));
Ok(())
});
}
#[test]
fn oidc_mode_requires_issuer_and_client_id() {
figment::Jail::expect_with(|jail| {
let toml = CLOUD_TOML.replace("issuer_url = \"https://idp.example.com\"\n", "");
jail.create_file("clawmates.toml", &toml)?;
let err = AppConfig::load_from(&jail.directory().join("clawmates.toml")).unwrap_err();
assert!(matches!(err, ConfigError::Invalid(msg) if msg.contains("issuer_url")));
Ok(())
});
}
#[test]
fn missing_file_is_a_clear_error() {
figment::Jail::expect_with(|jail| {
let err = AppConfig::load_from(&jail.directory().join("absent.toml")).unwrap_err();
assert!(matches!(err, ConfigError::Load(_)));
Ok(())
});
}
#[test]
fn s3_backend_requires_endpoint_and_bucket() {
figment::Jail::expect_with(|jail| {
let toml =
format!("{AIR_GAPPED_TOML}\n[storage]\ndata_dir = \"./data\"\nbackend = \"s3\"\n");
jail.create_file("clawmates.toml", &toml)?;
let err = AppConfig::load_from(&jail.directory().join("clawmates.toml")).unwrap_err();
assert!(matches!(err, ConfigError::Invalid(msg) if msg.contains("s3_endpoint")));
Ok(())
});
}
#[test]
fn clerk_mode_requires_the_instance_issuer() {
figment::Jail::expect_with(|jail| {
let toml = AIR_GAPPED_TOML.replace("mode = \"local\"", "mode = \"clerk\"");
jail.create_file("clawmates.toml", &toml)?;
let err = AppConfig::load_from(&jail.directory().join("clawmates.toml")).unwrap_err();
assert!(matches!(err, ConfigError::Invalid(msg) if msg.contains("clerk")));
let toml = toml.replace(
"mode = \"clerk\"",
"mode = \"clerk\"\nissuer_url = \"https://acme.clerk.accounts.dev\"",
);
jail.create_file("clawmates.toml", &toml)?;
let config = AppConfig::load_from(&jail.directory().join("clawmates.toml")).unwrap();
assert_eq!(config.auth.mode, AuthMode::Clerk);
Ok(())
});
}