use super::*;
use ito_config::types::BackendProjectConfig;
fn enabled_config() -> BackendApiConfig {
BackendApiConfig {
enabled: true,
token: Some("test-token-123".to_string()),
project: BackendProjectConfig {
org: Some("acme".to_string()),
repo: Some("widgets".to_string()),
},
..BackendApiConfig::default()
}
}
#[test]
fn disabled_backend_returns_none() {
let config = BackendApiConfig::default();
assert!(!config.enabled);
let result = resolve_backend_runtime(&config).unwrap();
assert!(result.is_none());
}
#[test]
fn enabled_backend_with_explicit_token_resolves() {
let config = enabled_config();
let runtime = resolve_backend_runtime(&config).unwrap().unwrap();
assert_eq!(runtime.token, "test-token-123");
assert_eq!(runtime.base_url, "http://127.0.0.1:9010");
assert_eq!(runtime.max_retries, 3);
assert_eq!(runtime.timeout, Duration::from_millis(30_000));
assert_eq!(runtime.org, "acme");
assert_eq!(runtime.repo, "widgets");
}
#[test]
fn enabled_backend_with_env_var_token_resolves() {
let env_var = "ITO_TEST_BACKEND_TOKEN_1";
unsafe { std::env::set_var(env_var, "env-token-456") };
let config = BackendApiConfig {
token: None,
token_env_var: env_var.to_string(),
..enabled_config()
};
let runtime = resolve_backend_runtime(&config).unwrap().unwrap();
assert_eq!(runtime.token, "env-token-456");
unsafe { std::env::remove_var(env_var) };
}
#[test]
fn env_var_token_takes_precedence_over_config_token() {
let env_var = "ITO_TEST_BACKEND_TOKEN_PREC";
unsafe { std::env::set_var(env_var, "env-token-override") };
let config = BackendApiConfig {
token: Some("config-token".to_string()),
token_env_var: env_var.to_string(),
..enabled_config()
};
let runtime = resolve_backend_runtime(&config).unwrap().unwrap();
assert_eq!(runtime.token, "env-token-override");
unsafe { std::env::remove_var(env_var) };
}
#[test]
fn enabled_backend_missing_token_fails() {
let env_var = "ITO_TEST_NONEXISTENT_TOKEN_VAR";
unsafe { std::env::remove_var(env_var) };
let config = BackendApiConfig {
token: None,
token_env_var: env_var.to_string(),
..enabled_config()
};
let err = resolve_backend_runtime(&config).unwrap_err();
let msg = err.to_string();
assert!(msg.contains(env_var), "error should mention env var: {msg}");
assert!(
msg.contains("not set"),
"error should mention 'not set': {msg}"
);
}
#[test]
fn enabled_backend_empty_token_fails() {
let env_var = "ITO_TEST_EMPTY_TOKEN_VAR";
unsafe { std::env::set_var(env_var, "") };
let config = BackendApiConfig {
token: None,
token_env_var: env_var.to_string(),
..enabled_config()
};
let err = resolve_backend_runtime(&config).unwrap_err();
let msg = err.to_string();
assert!(msg.contains("empty"), "error should mention 'empty': {msg}");
unsafe { std::env::remove_var(env_var) };
}
#[test]
fn custom_backup_dir_is_used() {
let config = BackendApiConfig {
backup_dir: Some("/custom/backups".to_string()),
..enabled_config()
};
let runtime = resolve_backend_runtime(&config).unwrap().unwrap();
assert_eq!(runtime.backup_dir, PathBuf::from("/custom/backups"));
}
#[test]
fn default_backup_dir_uses_home() {
let config = BackendApiConfig {
backup_dir: None,
..enabled_config()
};
let runtime = resolve_backend_runtime(&config).unwrap().unwrap();
let path_str = runtime.backup_dir.to_string_lossy();
assert!(
path_str.ends_with(".ito/backups"),
"unexpected backup dir: {path_str}"
);
}
#[test]
fn project_namespace_from_config() {
let config = enabled_config();
let runtime = resolve_backend_runtime(&config).unwrap().unwrap();
assert_eq!(runtime.org, "acme");
assert_eq!(runtime.repo, "widgets");
}
#[test]
fn project_namespace_from_env_vars() {
let org_var = "ITO_TEST_NS_FROM_ENV_ORG";
let repo_var = "ITO_TEST_NS_FROM_ENV_REPO";
unsafe {
std::env::set_var(org_var, "env-org");
std::env::set_var(repo_var, "env-repo");
}
let config = BackendApiConfig {
project: BackendProjectConfig {
org: None,
repo: None,
},
..enabled_config()
};
let (org, repo) = resolve_project_namespace_with_env(&config, org_var, repo_var).unwrap();
assert_eq!(org, "env-org");
assert_eq!(repo, "env-repo");
unsafe {
std::env::remove_var(org_var);
std::env::remove_var(repo_var);
}
}
#[test]
fn project_namespace_env_takes_precedence_over_config() {
let org_var = "ITO_TEST_NS_PREC_ORG";
let repo_var = "ITO_TEST_NS_PREC_REPO";
unsafe {
std::env::set_var(org_var, "env-org");
std::env::set_var(repo_var, "env-repo");
}
let config = enabled_config(); let (org, repo) = resolve_project_namespace_with_env(&config, org_var, repo_var).unwrap();
assert_eq!(org, "env-org");
assert_eq!(repo, "env-repo");
unsafe {
std::env::remove_var(org_var);
std::env::remove_var(repo_var);
}
}
#[test]
fn project_namespace_missing_org_fails() {
let org_var = "ITO_TEST_NS_MISS_ORG";
let repo_var = "ITO_TEST_NS_MISS_ORG_REPO";
unsafe {
std::env::remove_var(org_var);
std::env::remove_var(repo_var);
}
let config = BackendApiConfig {
project: BackendProjectConfig {
org: None,
repo: Some("widgets".to_string()),
},
..enabled_config()
};
let err = resolve_project_namespace_with_env(&config, org_var, repo_var).unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("project.org"),
"error should mention project.org: {msg}"
);
}
#[test]
fn project_namespace_missing_repo_fails() {
let org_var = "ITO_TEST_NS_MISS_REPO_ORG";
let repo_var = "ITO_TEST_NS_MISS_REPO";
unsafe {
std::env::remove_var(org_var);
std::env::remove_var(repo_var);
}
let config = BackendApiConfig {
project: BackendProjectConfig {
org: Some("acme".to_string()),
repo: None,
},
..enabled_config()
};
let err = resolve_project_namespace_with_env(&config, org_var, repo_var).unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("project.repo"),
"error should mention project.repo: {msg}"
);
}
#[test]
fn project_namespace_empty_string_falls_through_to_env() {
let org_var = "ITO_TEST_NS_EMPTY_ORG";
let repo_var = "ITO_TEST_NS_EMPTY_REPO";
unsafe {
std::env::set_var(org_var, "env-org");
std::env::set_var(repo_var, "env-repo");
}
let config = BackendApiConfig {
project: BackendProjectConfig {
org: Some("".to_string()),
repo: Some("".to_string()),
},
..enabled_config()
};
let (org, repo) = resolve_project_namespace_with_env(&config, org_var, repo_var).unwrap();
assert_eq!(org, "env-org");
assert_eq!(repo, "env-repo");
unsafe {
std::env::remove_var(org_var);
std::env::remove_var(repo_var);
}
}
#[test]
fn project_api_prefix_formats_correctly() {
let config = enabled_config();
let runtime = resolve_backend_runtime(&config).unwrap().unwrap();
assert_eq!(
runtime.project_api_prefix(),
"http://127.0.0.1:9010/api/v1/projects/acme/widgets"
);
}
#[test]
fn is_retriable_status_checks() {
assert!(is_retriable_status(429));
assert!(is_retriable_status(500));
assert!(is_retriable_status(502));
assert!(is_retriable_status(503));
assert!(!is_retriable_status(400));
assert!(!is_retriable_status(401));
assert!(!is_retriable_status(404));
assert!(!is_retriable_status(200));
}
#[test]
fn idempotency_key_includes_operation() {
let key = idempotency_key("push");
assert!(key.ends_with("-push"));
assert!(key.len() > 5); }