proofmode 0.9.0

Capture, share, and preserve verifiable photos and videos
Documentation
use std::fs;
use std::path::PathBuf;
use std::process::Command;
use tempfile::TempDir;

#[cfg(test)]
mod cli_tests {
    use super::*;

    fn get_binary_path() -> PathBuf {
        let mut path = std::env::current_exe().unwrap();
        path.pop(); // Remove test executable name
        if path.ends_with("deps") {
            path.pop(); // Remove deps directory
        }
        path.join("proofmode")
    }

    fn create_test_file(dir: &std::path::Path, name: &str, content: &[u8]) -> PathBuf {
        let file_path = dir.join(name);
        fs::write(&file_path, content).expect("Failed to write test file");
        file_path
    }

    #[test]
    fn test_cli_version() {
        let output = Command::new(get_binary_path())
            .arg("--version")
            .output()
            .expect("Failed to execute CLI");

        assert!(output.status.success());
        let stdout = String::from_utf8(output.stdout).unwrap();
        assert!(stdout.contains("proofmode"));
        let version = env!("CARGO_PKG_VERSION");
        assert!(stdout.contains(version));
    }

    #[test]
    fn test_cli_help() {
        let output = Command::new(get_binary_path())
            .arg("--help")
            .output()
            .expect("Failed to execute CLI");

        assert!(output.status.success());
        let stdout = String::from_utf8(output.stdout).unwrap();
        assert!(stdout.contains("Capture, share, and preserve verifiable photos and videos"));
        assert!(stdout.contains("check"));
        assert!(stdout.contains("generate"));
    }

    #[test]
    fn test_cli_generate_help() {
        let output = Command::new(get_binary_path())
            .args(["generate", "--help"])
            .output()
            .expect("Failed to execute CLI");

        assert!(output.status.success());
        let stdout = String::from_utf8(output.stdout).unwrap();
        assert!(stdout.contains("--file"));
        assert!(stdout.contains("--dir"));
        assert!(stdout.contains("--storage"));
        assert!(stdout.contains("--email"));
        assert!(stdout.contains("--passphrase"));
    }

    #[test]
    fn test_cli_check_help() {
        let output = Command::new(get_binary_path())
            .args(["check", "--help"])
            .output()
            .expect("Failed to execute CLI");

        assert!(output.status.success());
        let stdout = String::from_utf8(output.stdout).unwrap();
        assert!(stdout.contains("--file"));
        assert!(stdout.contains("--dir"));
        assert!(stdout.contains("--url"));
        assert!(stdout.contains("--cid"));
    }

    #[test]
    fn test_cli_generate_single_file() {
        let temp_dir = TempDir::new().expect("Failed to create temp dir");
        let storage_dir = TempDir::new().expect("Failed to create storage dir");

        let test_file = create_test_file(temp_dir.path(), "test.txt", b"CLI test content");

        let output = Command::new(get_binary_path())
            .args([
                "generate",
                "--file",
                test_file.to_str().unwrap(),
                "--storage",
                storage_dir.path().to_str().unwrap(),
                "--email",
                "cli-test@example.com",
                "--passphrase",
                "clipassword123",
            ])
            .output()
            .expect("Failed to execute CLI");

        assert!(output.status.success());
        let stdout = String::from_utf8(output.stdout).unwrap();
        assert!(stdout.contains("Generated proof for"));
        assert!(stdout.contains(test_file.file_name().unwrap().to_str().unwrap()));

        // Extract hash from output
        let lines: Vec<&str> = stdout.lines().collect();
        let proof_line = lines
            .iter()
            .find(|line| line.contains("Generated proof for"))
            .unwrap();
        let hash = proof_line.split(": ").last().unwrap().trim();
        assert_eq!(hash.len(), 64); // SHA256 length

        // Verify proof files were created
        let proof_dir = storage_dir.path().join("proofmode").join(hash);
        assert!(proof_dir.exists());

        let json_file = proof_dir.join(format!("{}.proof.json", hash));
        let csv_file = proof_dir.join(format!("{}.proof.csv", hash));
        let asc_file = proof_dir.join(format!("{}.asc", hash));

        assert!(json_file.exists());
        assert!(csv_file.exists());
        assert!(asc_file.exists());
    }

    #[test]
    fn test_cli_generate_multiple_files() {
        let temp_dir = TempDir::new().expect("Failed to create temp dir");
        let storage_dir = TempDir::new().expect("Failed to create storage dir");

        let file1 = create_test_file(temp_dir.path(), "file1.txt", b"First file content");
        let file2 = create_test_file(temp_dir.path(), "file2.txt", b"Second file content");

        let output = Command::new(get_binary_path())
            .args([
                "generate",
                "--file",
                file1.to_str().unwrap(),
                "--file",
                file2.to_str().unwrap(),
                "--storage",
                storage_dir.path().to_str().unwrap(),
                "--email",
                "multi-test@example.com",
                "--passphrase",
                "multipass123",
            ])
            .output()
            .expect("Failed to execute CLI");

        assert!(output.status.success());
        let stdout = String::from_utf8(output.stdout).unwrap();

        // Should have two "Generated proof" lines
        let proof_lines: Vec<&str> = stdout
            .lines()
            .filter(|line| line.contains("Generated proof for"))
            .collect();
        assert_eq!(proof_lines.len(), 2);

        // Extract hashes and verify they're different
        let mut hashes = Vec::new();
        for line in proof_lines {
            let hash = line.split(": ").last().unwrap().trim();
            hashes.push(hash);
        }
        assert_ne!(hashes[0], hashes[1]);

        // Verify both proof directories exist
        for hash in hashes {
            let proof_dir = storage_dir.path().join("proofmode").join(hash);
            assert!(proof_dir.exists());
        }
    }

    #[test]
    fn test_cli_generate_directory() {
        let temp_dir = TempDir::new().expect("Failed to create temp dir");
        let storage_dir = TempDir::new().expect("Failed to create storage dir");

        let test_dir = temp_dir.path().join("testdir");
        fs::create_dir(&test_dir).expect("Failed to create test directory");

        create_test_file(&test_dir, "dirfile1.txt", b"Directory file 1");
        create_test_file(&test_dir, "dirfile2.txt", b"Directory file 2");

        let output = Command::new(get_binary_path())
            .args([
                "generate",
                "--dir",
                test_dir.to_str().unwrap(),
                "--storage",
                storage_dir.path().to_str().unwrap(),
                "--email",
                "dir-test@example.com",
                "--passphrase",
                "dirpass123",
            ])
            .output()
            .expect("Failed to execute CLI");

        assert!(output.status.success());
        let stdout = String::from_utf8(output.stdout).unwrap();

        // Should have proof lines for both files
        let proof_lines: Vec<&str> = stdout
            .lines()
            .filter(|line| line.contains("Generated proof for"))
            .collect();
        assert_eq!(proof_lines.len(), 2);
    }

    #[test]
    fn test_cli_check_file() {
        let temp_dir = TempDir::new().expect("Failed to create temp dir");
        let test_file = create_test_file(temp_dir.path(), "check_test.txt", b"Check test content");

        let output = Command::new(get_binary_path())
            .args(["check", "--file", test_file.to_str().unwrap()])
            .output()
            .expect("Failed to execute CLI");

        assert!(output.status.success());
        let stdout = String::from_utf8(output.stdout).unwrap();

        // Should show verification steps
        assert!(
            stdout.contains("Starting ProofCheck")
                || stdout.contains("Extracting files")
                || stdout.contains("Checking integrity")
        );
    }

    #[test]
    fn test_cli_invalid_arguments() {
        // Test with non-existent file
        let output = Command::new(get_binary_path())
            .args([
                "generate",
                "--file",
                "/non/existent/file.txt",
                "--storage",
                "/tmp/test",
                "--email",
                "test@example.com",
                "--passphrase",
                "test123",
            ])
            .output()
            .expect("Failed to execute CLI");

        assert!(!output.status.success());
        let stderr = String::from_utf8(output.stderr).unwrap();
        // Should contain error about file not found
        assert!(!stderr.is_empty());
    }

    #[test]
    fn test_cli_missing_required_args() {
        // Test generate without required arguments
        let output = Command::new(get_binary_path())
            .args(["generate"])
            .output()
            .expect("Failed to execute CLI");

        // Should succeed but do nothing (no files/dirs specified)
        assert!(output.status.success());
    }

    #[test]
    fn test_cli_check_with_output_file() {
        let temp_dir = TempDir::new().expect("Failed to create temp dir");
        let test_file =
            create_test_file(temp_dir.path(), "output_test.txt", b"Output test content");
        let output_file = temp_dir.path().join("check_results.json");

        let output = Command::new(get_binary_path())
            .args([
                "check",
                "--file",
                test_file.to_str().unwrap(),
                "--output-file",
                output_file.to_str().unwrap(),
            ])
            .output()
            .expect("Failed to execute CLI");

        assert!(output.status.success());

        // Verify output file was created
        assert!(output_file.exists());

        // Verify it contains JSON
        let json_content = fs::read_to_string(&output_file).unwrap();
        assert!(json_content.contains("{") && json_content.contains("}"));
    }

    #[test]
    fn test_cli_generate_custom_storage_path() {
        let temp_dir = TempDir::new().expect("Failed to create temp dir");
        let storage_dir = TempDir::new().expect("Failed to create storage dir");
        let custom_storage = storage_dir
            .path()
            .join("custom")
            .join("proof")
            .join("storage");

        let test_file = create_test_file(
            temp_dir.path(),
            "custom_storage.txt",
            b"Custom storage test",
        );

        let output = Command::new(get_binary_path())
            .args([
                "generate",
                "--file",
                test_file.to_str().unwrap(),
                "--storage",
                custom_storage.to_str().unwrap(),
                "--email",
                "custom@example.com",
                "--passphrase",
                "custompass123",
            ])
            .output()
            .expect("Failed to execute CLI");

        assert!(output.status.success());

        // Verify custom storage path was created and used
        assert!(custom_storage.exists());

        let stdout = String::from_utf8(output.stdout).unwrap();
        let hash = stdout
            .lines()
            .find(|line| line.contains("Generated proof for"))
            .unwrap()
            .split(": ")
            .last()
            .unwrap()
            .trim();

        let proof_dir = custom_storage.join("proofmode").join(hash);
        assert!(proof_dir.exists());
    }
}