use envy_secrets::db::{DbError, EnvId, ProjectId, Vault};
const DUMMY_KEY: [u8; 32] = [0u8; 32];
fn open_temp_vault() -> (Vault, tempfile::NamedTempFile) {
let tmp = tempfile::NamedTempFile::new()
.expect("NamedTempFile::new always succeeds in a writable temp dir");
let vault = Vault::open(tmp.path(), &DUMMY_KEY)
.expect("Vault::open on a fresh temp file always succeeds");
(vault, tmp)
}
fn make_project(vault: &Vault, name: &str) -> ProjectId {
vault
.create_project(name)
.expect("create_project must succeed in setup")
}
#[test]
fn test_create_environment_succeeds() {
let (vault, _tmp) = open_temp_vault();
let project_id = make_project(&vault, "my-app");
let env_id = vault
.create_environment(&project_id, "development")
.expect("create_environment must succeed with a valid lowercase name");
assert_eq!(
env_id.as_str().len(),
36,
"EnvId must be a 36-character UUID"
);
}
#[test]
fn test_create_environment_duplicate_returns_already_exists() {
let (vault, _tmp) = open_temp_vault();
let project_id = make_project(&vault, "my-app");
vault
.create_environment(&project_id, "production")
.expect("first create_environment must succeed");
let result = vault.create_environment(&project_id, "production");
assert!(
matches!(result, Err(DbError::AlreadyExists)),
"duplicate environment name must return DbError::AlreadyExists, got: {:?}",
result
);
}
#[test]
fn test_get_environment_by_name() {
let (vault, _tmp) = open_temp_vault();
let project_id = make_project(&vault, "my-app");
let env_id = vault
.create_environment(&project_id, "staging")
.expect("create_environment must succeed");
let env = vault
.get_environment_by_name(&project_id, "staging")
.expect("get_environment_by_name must find the environment");
assert_eq!(env.id, env_id);
assert_eq!(env.name, "staging");
assert_eq!(env.project_id, project_id);
assert!(env.created_at > 0, "created_at must be a non-zero epoch");
assert!(env.updated_at > 0, "updated_at must be a non-zero epoch");
}
#[test]
fn test_get_environment_by_name_not_found() {
let (vault, _tmp) = open_temp_vault();
let project_id = make_project(&vault, "my-app");
let result = vault.get_environment_by_name(&project_id, "does-not-exist");
assert!(
matches!(result, Err(DbError::NotFound)),
"get_environment_by_name with unknown name must return DbError::NotFound"
);
}
#[test]
fn test_create_environment_invalid_project_id() {
let (vault, _tmp) = open_temp_vault();
let ghost_project = ProjectId("00000000-0000-0000-0000-000000000000".into());
let result = vault.create_environment(&ghost_project, "development");
assert!(
matches!(result, Err(DbError::ConstraintViolation(_))),
"create_environment with non-existent project_id must return DbError::ConstraintViolation, got: {:?}",
result
);
}
#[test]
fn test_delete_project_cascades_to_environments() {
let (vault, _tmp) = open_temp_vault();
let project_id = make_project(&vault, "my-app");
let env_id = vault
.create_environment(&project_id, "development")
.expect("create_environment must succeed");
vault
.delete_project(&project_id)
.expect("delete_project must succeed");
let result = vault.get_environment(&env_id);
assert!(
matches!(result, Err(DbError::NotFound)),
"get_environment after project delete must return DbError::NotFound"
);
}
#[test]
fn test_uppercase_environment_name_is_rejected() {
let (vault, _tmp) = open_temp_vault();
let project_id = make_project(&vault, "my-app");
let result = vault.create_environment(&project_id, "Production");
assert!(
matches!(result, Err(DbError::ConstraintViolation(_))),
"uppercase environment name must return DbError::ConstraintViolation, got: {:?}",
result
);
}
#[test]
fn test_list_environments_order() {
let (vault, _tmp) = open_temp_vault();
let project_id = make_project(&vault, "my-app");
let empty = vault
.list_environments(&project_id)
.expect("list_environments on empty project must return Ok(vec![])");
assert!(
empty.is_empty(),
"list_environments on empty project must return []"
);
vault
.create_environment(&project_id, "staging")
.expect("create staging");
vault
.create_environment(&project_id, "development")
.expect("create development");
vault
.create_environment(&project_id, "production")
.expect("create production");
let list = vault
.list_environments(&project_id)
.expect("list_environments must succeed after inserts");
assert_eq!(list.len(), 3, "list must contain exactly 3 environments");
let names: Vec<&str> = list.iter().map(|e| e.name.as_str()).collect();
assert_eq!(
names,
vec!["development", "production", "staging"],
"environments must be ordered by name ASC"
);
}
#[test]
fn test_delete_environment_removes_record() {
let (vault, _tmp) = open_temp_vault();
let project_id = make_project(&vault, "my-app");
let env_id = vault
.create_environment(&project_id, "to-delete")
.expect("create_environment must succeed");
vault
.delete_environment(&env_id)
.expect("delete_environment must succeed for an existing environment");
let result = vault.get_environment(&env_id);
assert!(
matches!(result, Err(DbError::NotFound)),
"get_environment after delete must return DbError::NotFound"
);
}
#[test]
fn test_delete_environment_not_found() {
let (vault, _tmp) = open_temp_vault();
let result = vault.delete_environment(&EnvId("00000000-0000-0000-0000-000000000000".into()));
assert!(
matches!(result, Err(DbError::NotFound)),
"delete_environment with unknown id must return DbError::NotFound"
);
}
#[test]
fn test_same_env_name_in_different_projects() {
let (vault, _tmp) = open_temp_vault();
let project_a = make_project(&vault, "project-a");
let project_b = make_project(&vault, "project-b");
vault
.create_environment(&project_a, "production")
.expect("create production in project-a must succeed");
vault
.create_environment(&project_b, "production")
.expect("create production in project-b must succeed");
}