mod common;
use common::*;
use std::process::Command;
async fn bootstrap_test_environment() -> testcontainers::ContainerAsync<testcontainers::GenericImage> {
let container = setup_fedora_container().await;
let container_id = container.id();
let status = Command::new("docker")
.args(&["exec", container_id, "git", "init", "--bare", "/tmp/mock-gitlab-repo.git"])
.status()
.expect("Failed to initialize mock bare git remote");
assert!(status.success());
let status = Command::new("docker")
.args(&[
"exec",
container_id,
"mkdir",
"-p",
"/root/.config/fp-dotfiles-manager",
])
.status()
.expect("Failed to create config dir");
assert!(status.success());
let status = Command::new("docker")
.args(&[
"exec",
container_id,
"ssh-keygen",
"-t",
"ed25519",
"-N",
"",
"-f",
"/root/.config/fp-dotfiles-manager/ssh_gitlab",
])
.status()
.expect("Failed to generate ed25519 SSH keys");
assert!(status.success());
let status = Command::new("docker")
.args(&[
"exec",
container_id,
"git",
"config",
"--global",
"user.name",
"Test User",
])
.status()
.expect("Failed to configure git user.name");
assert!(status.success());
let status = Command::new("docker")
.args(&[
"exec",
container_id,
"git",
"config",
"--global",
"user.email",
"test@example.com",
])
.status()
.expect("Failed to configure git user.email");
assert!(status.success());
run_manager_success(
&container,
&[
"setup",
"--repo-url",
"/tmp/mock-gitlab-repo.git",
"--ssh-key",
"/root/.config/fp-dotfiles-manager/ssh_gitlab",
"--force",
],
).await;
container
}
#[tokio::test]
async fn test_commands_add_and_add_secret() {
let container = bootstrap_test_environment().await;
write_file_in_container(&container, "/root/.bashrc", "some content");
write_file_in_container(&container, "/root/.zshrc", "some zsh content");
write_file_in_container(&container, "/root/.ssh/config", "Host *");
run_manager_success(&container, &["add", "/root/.bashrc", "/root/.zshrc"]).await;
run_manager_success(&container, &["add-secret", "/root/.ssh/config"]).await;
let tracked = read_file_in_container(&container, "/root/.config/fp-dotfiles-manager/tracked");
assert!(tracked.contains(".bashrc"));
assert!(tracked.contains(".zshrc"));
let tracked_enc = read_file_in_container(&container, "/root/.config/fp-dotfiles-manager/tracked_encrypted");
assert!(tracked_enc.contains(".ssh/config"));
}
#[tokio::test]
async fn test_command_bootstrap() {
let container = bootstrap_test_environment().await;
write_file_in_container(
&container,
"/root/.config/fp-dotfiles-manager/bootstrap.d/01-test-hook.sh",
"#!/bin/sh\necho 'BOOTSTRAPPED' > /tmp/bootstrap_test_ran\n"
);
let status = Command::new("docker")
.args(&[
"exec",
container.id(),
"chmod",
"+x",
"/root/.config/fp-dotfiles-manager/bootstrap.d/01-test-hook.sh",
])
.status()
.expect("Failed to chmod bootstrap script");
assert!(status.success());
let stdout = run_manager_success(&container, &["bootstrap"]).await;
assert!(stdout.contains("Running bootstrap scripts"));
let bootstrap_marker = read_file_in_container(&container, "/tmp/bootstrap_test_ran");
assert_eq!(bootstrap_marker.trim(), "BOOTSTRAPPED");
}
#[tokio::test]
async fn test_command_backup() {
let container = bootstrap_test_environment().await;
write_file_in_container(
&container,
"/root/.config/fp-dotfiles-manager/tracked",
".bashrc\n"
);
write_file_in_container(&container, "/root/.bashrc", "alias ll='ls -lh'\n");
let stdout = run_manager_success(&container, &["backup", "/tmp/dotfiles_backup.tar.gz"]).await;
assert!(stdout.contains("Unencrypted backup successfully archived"));
let ls_output = Command::new("docker")
.args(&[
"exec",
container.id(),
"ls",
"-lh",
"/tmp/dotfiles_backup.tar.gz",
])
.output()
.expect("Failed to locate backup file");
assert!(ls_output.status.success());
let ls_str = String::from_utf8_lossy(&ls_output.stdout);
assert!(ls_str.contains("dotfiles_backup.tar.gz"));
}
#[tokio::test]
async fn test_command_logs() {
let container = bootstrap_test_environment().await;
write_file_in_container(
&container,
"/root/.config/fp-dotfiles-manager/tracked",
".bashrc\n"
);
write_file_in_container(&container, "/root/.bashrc", "alias ll='ls -l'\n");
run_manager_success(&container, &["sync"]).await;
let stdout = run_manager_success(&container, &["logs"]).await;
assert!(stdout.contains("chore: auto-sync dotfiles"));
}
#[tokio::test]
async fn test_command_clean_history() {
let container = bootstrap_test_environment().await;
write_file_in_container(
&container,
"/root/.config/fp-dotfiles-manager/tracked",
".bashrc\n"
);
write_file_in_container(&container, "/root/.bashrc", "alias ll='ls -l'\n");
run_manager_success(&container, &["sync"]).await;
write_file_in_container(&container, "/root/.bashrc", "alias ll='ls -lh'\n");
run_manager_success(&container, &["sync"]).await;
let count_output = Command::new("docker")
.args(&[
"exec",
container.id(),
"git",
"-C",
"/tmp/mock-gitlab-repo.git",
"rev-list",
"--count",
"HEAD",
])
.output()
.expect("Failed to count commits");
assert!(count_output.status.success());
let count_str = String::from_utf8_lossy(&count_output.stdout).trim().to_string();
let count: u32 = count_str.parse().expect("Failed to parse commit count");
assert!(count >= 2, "Expected at least 2 commits, got {count}");
let output = run_manager_with_stdin(
&container,
&["clean-history", "--repo-path", "/tmp/mock-gitlab-repo.git"],
"y\ny\n",
)
.await;
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
output.status.success(),
"clean-history failed.\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}"
);
assert!(stdout.contains("History cleaned successfully."));
let count_output_after = Command::new("docker")
.args(&[
"exec",
container.id(),
"git",
"-C",
"/tmp/mock-gitlab-repo.git",
"rev-list",
"--count",
"HEAD",
])
.output()
.expect("Failed to count commits after cleaning");
assert!(count_output_after.status.success());
let count_after_str = String::from_utf8_lossy(&count_output_after.stdout).trim().to_string();
assert_eq!(count_after_str, "1");
let log_output = Command::new("docker")
.args(&[
"exec",
container.id(),
"git",
"-C",
"/tmp/mock-gitlab-repo.git",
"log",
"-1",
"--oneline",
])
.output()
.expect("Failed to get git log");
assert!(log_output.status.success());
let log_str = String::from_utf8_lossy(&log_output.stdout);
assert!(log_str.contains("chore: reset repository history"));
}
#[tokio::test]
async fn test_commands_status_diff_forget() {
let container = bootstrap_test_environment().await;
write_file_in_container(
&container,
"/root/.config/fp-dotfiles-manager/tracked",
".bashrc\n"
);
write_file_in_container(&container, "/root/.bashrc", "alias ll='ls -lh'\n");
run_manager_success(&container, &["sync"]).await;
write_file_in_container(&container, "/root/.bashrc", "alias ll='ls -la'\n");
let status_stdout = run_manager_success(&container, &["status"]).await;
assert!(status_stdout.contains("bashrc") || status_stdout.contains("M"));
let diff_stdout = run_manager_success(&container, &["diff"]).await;
assert!(diff_stdout.contains("alias ll='ls -la'"));
run_manager_success(&container, &["forget", "/root/.bashrc"]).await;
let status_stdout_after = run_manager_success(&container, &["status"]).await;
assert!(!status_stdout_after.contains("bashrc") && !status_stdout_after.contains("M"));
}