use serde_json::json;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
fn mock_stub_binary() -> std::path::PathBuf {
super::workspace_bin("mock-stub")
}
pub struct MockConfig {
name: String,
version: Option<String>,
commands: HashMap<String, MockResponse>,
}
pub struct MockResponse {
file: Option<String>,
output: Option<String>,
stderr: Option<String>,
exit_code: i32,
}
impl MockResponse {
pub fn file(path: &str) -> Self {
Self {
file: Some(path.to_string()),
output: None,
stderr: None,
exit_code: 0,
}
}
pub fn output(text: &str) -> Self {
Self {
file: None,
output: Some(text.to_string()),
stderr: None,
exit_code: 0,
}
}
pub fn stderr(text: &str) -> Self {
Self {
file: None,
output: None,
stderr: Some(text.to_string()),
exit_code: 0,
}
}
pub fn exit(code: i32) -> Self {
Self {
file: None,
output: None,
stderr: None,
exit_code: code,
}
}
pub fn with_exit_code(mut self, code: i32) -> Self {
self.exit_code = code;
self
}
pub fn with_stderr(mut self, text: &str) -> Self {
self.stderr = Some(text.to_string());
self
}
fn to_json(&self) -> serde_json::Value {
let mut obj = serde_json::Map::new();
if let Some(f) = &self.file {
obj.insert("file".to_string(), json!(f));
}
if let Some(o) = &self.output {
obj.insert("output".to_string(), json!(o));
}
if let Some(e) = &self.stderr {
obj.insert("stderr".to_string(), json!(e));
}
if self.exit_code != 0
|| (self.file.is_none() && self.output.is_none() && self.stderr.is_none())
{
obj.insert("exit_code".to_string(), json!(self.exit_code));
}
serde_json::Value::Object(obj)
}
}
impl MockConfig {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
version: None,
commands: HashMap::new(),
}
}
pub fn version(mut self, v: &str) -> Self {
self.version = Some(v.to_string());
self
}
pub fn command(mut self, cmd: &str, response: MockResponse) -> Self {
self.commands.insert(cmd.to_string(), response);
self
}
pub fn write(self, bin_dir: &Path) {
let mut config = serde_json::Map::new();
if let Some(v) = &self.version {
config.insert("version".to_string(), json!(v));
}
let commands: serde_json::Map<String, serde_json::Value> = self
.commands
.iter()
.map(|(k, v)| (k.clone(), v.to_json()))
.collect();
config.insert("commands".to_string(), serde_json::Value::Object(commands));
let json = serde_json::to_string_pretty(&serde_json::Value::Object(config)).unwrap();
let config_path = bin_dir.join(format!("{}.json", self.name));
fs::write(&config_path, json).unwrap();
copy_mock_binary(bin_dir, &self.name);
}
}
pub fn copy_mock_binary(bin_dir: &Path, name: &str) {
let stub = mock_stub_binary();
#[cfg(unix)]
{
let dest = bin_dir.join(name);
let _ = fs::remove_file(&dest);
std::os::unix::fs::symlink(&stub, &dest).expect("failed to symlink mock-stub binary");
}
#[cfg(windows)]
{
let dest = bin_dir.join(format!("{}.exe", name));
let _ = fs::remove_file(&dest);
fs::copy(&stub, &dest).expect("failed to copy mock-stub.exe");
}
}
pub fn create_mock_cargo(bin_dir: &Path) {
MockConfig::new("cargo")
.command(
"test",
MockResponse::output(
" Finished test [unoptimized + debuginfo] target(s) in 0.12s
Running unittests src/lib.rs (target/debug/deps/worktrunk-abc123)
running 18 tests
test auth::tests::test_jwt_decode ... ok
test auth::tests::test_jwt_encode ... ok
test auth::tests::test_token_refresh ... ok
test auth::tests::test_token_validation ... ok
test result: ok. 18 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.08s
",
),
)
.command(
"clippy",
MockResponse::output(
" Checking worktrunk v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 1.23s
",
),
)
.command(
"install",
MockResponse::output(
" Installing worktrunk v0.1.0
Compiling worktrunk v0.1.0
Finished release [optimized] target(s) in 2.34s
Installing ~/.cargo/bin/wt
Installed package `worktrunk v0.1.0` (executable `wt`)
",
),
)
.write(bin_dir);
}
pub fn create_mock_llm_auth(bin_dir: &Path) {
MockConfig::new("llm")
.command(
"_default",
MockResponse::output(
"feat(auth): Implement JWT authentication system
Add comprehensive JWT token handling including validation, refresh logic,
and authentication tests. This establishes the foundation for secure
API authentication.
- Implement token refresh mechanism with expiry handling
- Add JWT encoding/decoding with signature verification
- Create test suite covering all authentication flows",
),
)
.write(bin_dir);
}
pub fn create_mock_llm_api(bin_dir: &Path) {
MockConfig::new("llm")
.command(
"_default",
MockResponse::output(
"feat(api): Add user authentication endpoints
Implement login and token refresh endpoints with JWT validation.
Includes comprehensive test coverage and input validation.",
),
)
.write(bin_dir);
}
pub fn create_mock_llm_quickstart(bin_dir: &Path) {
MockConfig::new("llm")
.command(
"_default",
MockResponse::output("Add authentication module"),
)
.write(bin_dir);
}
pub fn create_mock_uv_sync(bin_dir: &Path) {
MockConfig::new("uv")
.command(
"sync",
MockResponse::output(
"
Resolved 24 packages in 145ms
Installed 24 packages in 1.2s
",
),
)
.command(
"run",
MockResponse::output(
"
Starting dev server on http://localhost:3000...
",
),
)
.write(bin_dir);
}
pub fn create_mock_uv_pytest_ruff(bin_dir: &Path) {
MockConfig::new("uv")
.command(
"run",
MockResponse::output(
"
============================= test session starts ==============================
collected 3 items
tests/test_auth.py::test_login_success PASSED [ 33%]
tests/test_auth.py::test_login_invalid_password PASSED [ 66%]
tests/test_auth.py::test_token_validation PASSED [100%]
============================== 3 passed in 0.8s ===============================
",
),
)
.write(bin_dir);
}
pub fn create_mock_pytest(bin_dir: &Path) {
MockConfig::new("pytest")
.command(
"_default",
MockResponse::output(
"
============================= test session starts ==============================
collected 3 items
tests/test_auth.py::test_login_success PASSED [ 33%]
tests/test_auth.py::test_login_invalid_password PASSED [ 66%]
tests/test_auth.py::test_token_validation PASSED [100%]
============================== 3 passed in 0.8s ===============================
",
),
)
.write(bin_dir);
}
pub fn create_mock_ruff(bin_dir: &Path) {
MockConfig::new("ruff")
.command("check", MockResponse::output("\nAll checks passed!\n\n"))
.write(bin_dir);
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_mock_config_write() {
let temp = TempDir::new().unwrap();
let bin_dir = temp.path();
MockConfig::new("test-cmd")
.version("test-cmd version 1.0")
.command("foo", MockResponse::output("hello"))
.command("bar", MockResponse::exit(42))
.write(bin_dir);
let config_path = bin_dir.join("test-cmd.json");
assert!(config_path.exists());
let content = fs::read_to_string(&config_path).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
assert_eq!(parsed["version"], "test-cmd version 1.0");
#[cfg(unix)]
assert!(bin_dir.join("test-cmd").exists());
#[cfg(windows)]
assert!(bin_dir.join("test-cmd.exe").exists());
}
}