use crate::manifest::tool_config::*;
use serde_json;
use std::collections::HashMap;
use std::path::PathBuf;
use toml;
mod well_known_tool {
use super::*;
#[test]
fn test_from_name_valid_tools() {
assert_eq!(WellKnownTool::from_name("claude-code"), WellKnownTool::ClaudeCode);
assert_eq!(WellKnownTool::from_name("opencode"), WellKnownTool::OpenCode);
assert_eq!(WellKnownTool::from_name("agpm"), WellKnownTool::Agpm);
}
#[test]
fn test_from_name_custom_tools() {
assert_eq!(WellKnownTool::from_name("custom-tool"), WellKnownTool::Generic);
assert_eq!(WellKnownTool::from_name("my-tool"), WellKnownTool::Generic);
assert_eq!(WellKnownTool::from_name("unknown"), WellKnownTool::Generic);
}
#[test]
fn test_from_name_edge_cases() {
assert_eq!(WellKnownTool::from_name(""), WellKnownTool::Generic);
assert_eq!(WellKnownTool::from_name("CLAUDE-CODE"), WellKnownTool::Generic); assert_eq!(WellKnownTool::from_name("claude-code "), WellKnownTool::Generic); assert_eq!(WellKnownTool::from_name(" claude-code"), WellKnownTool::Generic); }
#[test]
fn test_default_enabled_values() {
assert!(WellKnownTool::ClaudeCode.default_enabled());
assert!(WellKnownTool::OpenCode.default_enabled());
assert!(WellKnownTool::Agpm.default_enabled());
assert!(WellKnownTool::Generic.default_enabled());
}
#[test]
fn test_well_known_tool_properties() {
let tool1 = WellKnownTool::ClaudeCode;
let tool2 = tool1; assert_eq!(tool1, tool2); assert_eq!(tool1, WellKnownTool::ClaudeCode); }
}
mod resource_config {
use super::*;
#[test]
fn test_resource_config_serialization() {
let config = ResourceConfig {
path: Some("agents".to_string()),
merge_target: None,
flatten: Some(true),
};
let json = serde_json::to_string(&config).unwrap();
let deserialized: ResourceConfig = serde_json::from_str(&json).unwrap();
assert_eq!(config, deserialized);
}
#[test]
fn test_resource_config_with_merge_target() {
let config = ResourceConfig {
path: None,
merge_target: Some(".claude/settings.local.json".to_string()),
flatten: None,
};
let json = serde_json::to_string(&config).unwrap();
let deserialized: ResourceConfig = serde_json::from_str(&json).unwrap();
assert_eq!(config, deserialized);
}
#[test]
fn test_resource_config_minimal() {
let config = ResourceConfig {
path: None,
merge_target: None,
flatten: None,
};
let json = serde_json::to_string(&config).unwrap();
let deserialized: ResourceConfig = serde_json::from_str(&json).unwrap();
assert_eq!(config, deserialized);
}
#[test]
fn test_resource_config_skip_serializing_if_none() {
let config = ResourceConfig {
path: Some("agents".to_string()),
merge_target: None,
flatten: None,
};
let json = serde_json::to_string(&config).unwrap();
assert!(!json.contains("merge_target"));
assert!(!json.contains("flatten"));
assert!(json.contains("path"));
}
}
mod tools_config_deserialization {
use super::*;
#[test]
fn test_deserialize_complete_tools_config() {
let toml = r#"
claude-code = { path = ".claude", enabled = true, resources = { agents = { path = "agents", flatten = true }, hooks = { merge-target = ".claude/settings.local.json" } } }
opencode = { path = ".opencode", enabled = false, resources = { agents = { path = "agent" } } }
"#;
let config: ToolsConfig = toml::from_str(toml).unwrap();
let claude_config = config.types.get("claude-code").unwrap();
assert_eq!(claude_config.path, PathBuf::from(".claude"));
assert!(claude_config.enabled);
let claude_agents = claude_config.resources.get("agents").unwrap();
assert_eq!(claude_agents.path, Some("agents".to_string()));
assert_eq!(claude_agents.flatten, Some(true));
let opencode_config = config.types.get("opencode").unwrap();
assert_eq!(opencode_config.path, PathBuf::from(".opencode"));
assert!(!opencode_config.enabled);
}
#[test]
fn test_deserialize_with_default_enabled() {
let toml = r#"
claude-code = { path = ".claude", resources = { agents = { path = "agents" } } }
opencode = { path = ".opencode", resources = { agents = { path = "agent" } } }
"#;
let config: ToolsConfig = toml::from_str(toml).unwrap();
let claude_config = config.types.get("claude-code").unwrap();
assert!(claude_config.enabled);
let opencode_config = config.types.get("opencode").unwrap();
assert!(opencode_config.enabled);
}
#[test]
fn test_deserialize_explicit_enabled_override() {
let toml = r#"
claude-code = { path = ".claude", enabled = false, resources = { agents = { path = "agents" } } }
opencode = { path = ".opencode", enabled = true, resources = { agents = { path = "agent" } } }
"#;
let config: ToolsConfig = toml::from_str(toml).unwrap();
let claude_config = config.types.get("claude-code").unwrap();
assert!(!claude_config.enabled);
let opencode_config = config.types.get("opencode").unwrap();
assert!(opencode_config.enabled);
}
#[test]
fn test_deserialize_custom_tool() {
let toml = r#"
my-custom-tool = { path = ".my-tool", enabled = true, resources = { agents = { path = "agents" } } }
"#;
let config: ToolsConfig = toml::from_str(toml).unwrap();
let custom_config = config.types.get("my-custom-tool").unwrap();
assert_eq!(custom_config.path, PathBuf::from(".my-tool"));
assert!(custom_config.enabled);
}
#[test]
fn test_deserialize_empty_tools_config() {
let toml = "";
let config: ToolsConfig = toml::from_str(toml).unwrap();
assert!(config.types.is_empty());
}
#[test]
fn test_deserialize_invalid_toml() {
let toml = r#"
[tools.claude-code
path = ".claude" # Missing closing bracket
"#;
let result: Result<ToolsConfig, _> = toml::from_str(toml);
assert!(result.is_err());
}
#[test]
fn test_deserialize_missing_required_path() {
let toml = r#"
[tools.claude-code]
# path field missing
[tools.claude-code.resources.agents]
path = "agents"
"#;
let result: Result<ToolsConfig, _> = toml::from_str(toml);
assert!(result.is_err());
}
#[test]
fn test_deserialize_roundtrip() {
let original = ToolsConfig::default();
let toml = toml::to_string_pretty(&original).unwrap();
let deserialized: ToolsConfig = toml::from_str(&toml).unwrap();
assert_eq!(original.types.len(), deserialized.types.len());
for tool_name in ["claude-code", "opencode", "agpm"] {
assert!(deserialized.types.contains_key(tool_name));
}
}
}
mod artifact_type_config {
use super::*;
#[test]
fn test_artifact_type_config_creation() {
let mut resources = HashMap::new();
resources.insert(
"agents".to_string(),
ResourceConfig {
path: Some("agents".to_string()),
merge_target: None,
flatten: Some(true),
},
);
let config = ArtifactTypeConfig {
path: PathBuf::from(".claude"),
resources,
enabled: true,
};
assert_eq!(config.path, PathBuf::from(".claude"));
assert!(config.enabled);
assert_eq!(config.resources.len(), 1);
}
#[test]
fn test_artifact_type_config_serialization() {
let mut resources = HashMap::new();
resources.insert(
"agents".to_string(),
ResourceConfig {
path: Some("agents".to_string()),
merge_target: None,
flatten: Some(true),
},
);
let config = ArtifactTypeConfig {
path: PathBuf::from(".claude"),
resources,
enabled: true,
};
let json = serde_json::to_string(&config).unwrap();
assert!(!json.is_empty());
}
}
mod tools_config_default {
use super::*;
#[test]
fn test_default_tools_config_structure() {
let config = ToolsConfig::default();
assert_eq!(config.types.len(), 3);
assert!(config.types.contains_key("claude-code"));
assert!(config.types.contains_key("opencode"));
assert!(config.types.contains_key("agpm"));
}
#[test]
fn test_claude_code_defaults() {
let config = ToolsConfig::default();
let claude = config.types.get("claude-code").unwrap();
assert_eq!(claude.path, PathBuf::from(".claude"));
assert!(claude.enabled);
assert!(claude.resources.contains_key("agents"));
assert!(claude.resources.contains_key("snippets"));
assert!(claude.resources.contains_key("commands"));
assert!(claude.resources.contains_key("scripts"));
assert!(claude.resources.contains_key("hooks"));
assert!(claude.resources.contains_key("mcp-servers"));
let agents = claude.resources.get("agents").unwrap();
assert_eq!(agents.path, Some("agents/agpm".to_string()));
assert_eq!(agents.flatten, Some(true));
let hooks = claude.resources.get("hooks").unwrap();
assert_eq!(hooks.path, None);
assert_eq!(hooks.merge_target, Some(".claude/settings.local.json".to_string()));
}
#[test]
fn test_opencode_defaults() {
let config = ToolsConfig::default();
let opencode = config.types.get("opencode").unwrap();
assert_eq!(opencode.path, PathBuf::from(".opencode"));
assert!(opencode.enabled);
assert!(opencode.resources.contains_key("agents"));
assert!(opencode.resources.contains_key("commands"));
assert!(opencode.resources.contains_key("snippets"));
assert!(opencode.resources.contains_key("mcp-servers"));
assert!(!opencode.resources.contains_key("scripts"));
assert!(!opencode.resources.contains_key("hooks"));
let agents = opencode.resources.get("agents").unwrap();
assert_eq!(agents.path, Some("agent/agpm".to_string()));
let snippets = opencode.resources.get("snippets").unwrap();
assert_eq!(snippets.path, Some("snippet/agpm".to_string())); }
#[test]
fn test_agpm_defaults() {
let config = ToolsConfig::default();
let agpm = config.types.get("agpm").unwrap();
assert_eq!(agpm.path, PathBuf::from(".agpm"));
assert!(agpm.enabled);
assert!(agpm.resources.contains_key("snippets"));
assert_eq!(agpm.resources.len(), 1);
let snippets = agpm.resources.get("snippets").unwrap();
assert_eq!(snippets.path, Some("snippets".to_string()));
assert_eq!(snippets.flatten, Some(false)); }
#[test]
fn test_default_resource_config_consistency() {
let config = ToolsConfig::default();
for (tool_name, tool_config) in &config.types {
for (resource_name, resource_config) in &tool_config.resources {
assert!(
resource_config.path.is_some() || resource_config.merge_target.is_some(),
"Resource {} in tool {} has neither path nor merge_target",
resource_name,
tool_name
);
}
}
}
}
mod integration_tests {
use super::*;
#[test]
fn test_complete_manifest_parsing() {
let toml = r#"
[sources]
community = "https://github.com/example/community.git"
[tools.claude-code]
path = ".claude"
[tools.claude-code.resources.agents]
path = "agents"
flatten = true
[agents]
my-agent = { source = "community", path = "agents/helper.md", version = "v1.0.0" }
"#;
let manifest: crate::manifest::Manifest = toml::from_str(toml).unwrap();
assert!(manifest.tools.is_some());
let tools = manifest.tools.unwrap();
assert!(tools.types.contains_key("claude-code"));
let claude = tools.types.get("claude-code").unwrap();
assert!(claude.enabled);
}
#[test]
fn test_tool_config_with_all_resource_types() {
let toml = r#"
test-tool = { path = ".test", enabled = true, resources = { agents = { path = "agents", flatten = true }, snippets = { path = "snippets", flatten = false }, commands = { path = "commands", flatten = true }, scripts = { path = "scripts", flatten = false }, hooks = { merge-target = ".test/settings.json" }, mcp-servers = { merge-target = ".test/mcp.json" } } }
"#;
let config: ToolsConfig = toml::from_str(toml).unwrap();
let tool = config.types.get("test-tool").unwrap();
assert_eq!(tool.resources.len(), 6);
for resource_type in ["agents", "snippets", "commands", "scripts"] {
let resource = tool.resources.get(resource_type).unwrap();
assert!(resource.path.is_some());
assert!(resource.merge_target.is_none());
}
for resource_type in ["hooks", "mcp-servers"] {
let resource = tool.resources.get(resource_type).unwrap();
assert!(resource.path.is_none());
assert!(resource.merge_target.is_some());
}
}
}