diff --git a/tests/test_apa.bats b/tests/test_apa.bats new file mode 100644 index 000000000..ab7bc4007 --- /dev/null +++ b/tests/test_apa.bats @@ -0,0 +1,401 @@ +#!/usr/bin/env bats + +# Load the apa script for testing +APA_SCRIPT="${BATS_TEST_DIRNAME}/../git/extensions/apa.sh" + +setup() { + # Create temporary directory for test isolation + export TEST_TEMP_DIR="$(mktemp -d)" + export ORIGINAL_HOME="$HOME" + export ORIGINAL_APA_CONFIG_DIR="$APA_CONFIG_DIR" + export ORIGINAL_APA_LOG_LEVEL="$APA_LOG_LEVEL" + export ORIGINAL_APA_TIMEOUT="$APA_TIMEOUT" + + # Set up test environment + export HOME="$TEST_TEMP_DIR" + export APA_CONFIG_DIR="$TEST_TEMP_DIR/.apa" + export APA_LOG_LEVEL="quiet" + export APA_TIMEOUT="10" + + # Make apa script executable and available + chmod +x "$APA_SCRIPT" + export PATH="$(dirname "$APA_SCRIPT"):$PATH" +} + +teardown() { + # Restore original environment + export HOME="$ORIGINAL_HOME" + export APA_CONFIG_DIR="$ORIGINAL_APA_CONFIG_DIR" + export APA_LOG_LEVEL="$ORIGINAL_APA_LOG_LEVEL" + export APA_TIMEOUT="$ORIGINAL_APA_TIMEOUT" + + # Clean up temporary files + [ -n "$TEST_TEMP_DIR" ] && rm -rf "$TEST_TEMP_DIR" +} + +@test "apa script exists and is executable" { + [ -f "$APA_SCRIPT" ] + [ -x "$APA_SCRIPT" ] +} + +@test "apa shows help with --help option" { + run bash "$APA_SCRIPT" --help + [ "$status" -eq 0 ] + [[ "$output" =~ "apa - Automated Package Assistant" ]] + [[ "$output" =~ "Usage:" ]] + [[ "$output" =~ "Commands:" ]] + [[ "$output" =~ "install" ]] + [[ "$output" =~ "update" ]] + [[ "$output" =~ "remove" ]] +} + +@test "apa shows help with -h option" { + run bash "$APA_SCRIPT" -h + [ "$status" -eq 0 ] + [[ "$output" =~ "apa - Automated Package Assistant" ]] +} + +@test "apa shows version with --version option" { + run bash "$APA_SCRIPT" --version + [ "$status" -eq 0 ] + [[ "$output" =~ "apa version 1.2.3" ]] +} + +@test "apa shows version with -v option" { + run bash "$APA_SCRIPT" -v + [ "$status" -eq 0 ] + [[ "$output" =~ "apa version 1.2.3" ]] +} + +@test "apa shows help when run with no arguments" { + run bash "$APA_SCRIPT" + [ "$status" -eq 1 ] + [[ "$output" =~ "Usage:" ]] +} + +@test "apa install succeeds with valid package name" { + run bash "$APA_SCRIPT" install nginx + [ "$status" -eq 0 ] + [[ "$output" =~ "Package nginx installed successfully" ]] +} + +@test "apa install creates config directory" { + bash "$APA_SCRIPT" install test-package + [ -d "$APA_CONFIG_DIR" ] +} + +@test "apa install fails without package name" { + run bash "$APA_SCRIPT" install + [ "$status" -eq 1 ] + [[ "$output" =~ "Install command requires a package name" ]] +} + +@test "apa install fails with empty package name" { + run bash "$APA_SCRIPT" install "" + [ "$status" -eq 1 ] + [[ "$output" =~ "Package name cannot be empty" ]] +} + +@test "apa install fails with invalid package name containing spaces" { + run bash "$APA_SCRIPT" install "invalid package" + [ "$status" -eq 1 ] + [[ "$output" =~ "Invalid package name" ]] +} + +@test "apa install fails with invalid package name containing special chars" { + run bash "$APA_SCRIPT" install "invalid@package#" + [ "$status" -eq 1 ] + [[ "$output" =~ "Invalid package name" ]] +} + +@test "apa install accepts valid package names with dots, dashes, underscores" { + run bash "$APA_SCRIPT" install "valid-package_name.123" + [ "$status" -eq 0 ] + [[ "$output" =~ "installed successfully" ]] +} + +@test "apa update succeeds with valid package name" { + run bash "$APA_SCRIPT" update nginx + [ "$status" -eq 0 ] + [[ "$output" =~ "Package nginx updated successfully" ]] +} + +@test "apa update fails without package name" { + run bash "$APA_SCRIPT" update + [ "$status" -eq 1 ] + [[ "$output" =~ "Update command requires a package name" ]] +} + +@test "apa update fails with empty package name" { + run bash "$APA_SCRIPT" update "" + [ "$status" -eq 1 ] + [[ "$output" =~ "Package name cannot be empty" ]] +} + +@test "apa update fails with invalid package name" { + run bash "$APA_SCRIPT" update "invalid@package" + [ "$status" -eq 1 ] + [[ "$output" =~ "Invalid package name" ]] +} + +@test "apa remove succeeds with valid package name" { + run bash "$APA_SCRIPT" remove nginx + [ "$status" -eq 0 ] + [[ "$output" =~ "Package nginx removed successfully" ]] +} + +@test "apa remove fails without package name" { + run bash "$APA_SCRIPT" remove + [ "$status" -eq 1 ] + [[ "$output" =~ "Remove command requires a package name" ]] +} + +@test "apa remove fails with empty package name" { + run bash "$APA_SCRIPT" remove "" + [ "$status" -eq 1 ] + [[ "$output" =~ "Package name cannot be empty" ]] +} + +@test "apa remove fails with invalid package name" { + run bash "$APA_SCRIPT" remove "invalid package name" + [ "$status" -eq 1 ] + [[ "$output" =~ "Invalid package name" ]] +} + +@test "apa list shows installed packages" { + run bash "$APA_SCRIPT" list + [ "$status" -eq 0 ] + [[ "$output" =~ "nginx-1.18.0" ]] + [[ "$output" =~ "curl-7.68.0" ]] + [[ "$output" =~ "git-2.34.1" ]] +} + +@test "apa search succeeds with valid query" { + run bash "$APA_SCRIPT" search "web" + [ "$status" -eq 0 ] + [[ "$output" =~ "Found packages matching 'web'" ]] + [[ "$output" =~ "web-server-toolkit" ]] +} + +@test "apa search fails without query" { + run bash "$APA_SCRIPT" search + [ "$status" -eq 1 ] + [[ "$output" =~ "Search command requires a query" ]] +} + +@test "apa search fails with empty query" { + run bash "$APA_SCRIPT" search "" + [ "$status" -eq 1 ] + [[ "$output" =~ "Search query cannot be empty" ]] +} + +@test "apa search handles special characters in query" { + run bash "$APA_SCRIPT" search "web-server" + [ "$status" -eq 0 ] + [[ "$output" =~ "Found packages matching" ]] +} + +@test "apa info succeeds with valid package name" { + run bash "$APA_SCRIPT" info nginx + [ "$status" -eq 0 ] + [[ "$output" =~ "Package: nginx" ]] + [[ "$output" =~ "Version:" ]] + [[ "$output" =~ "Description:" ]] + [[ "$output" =~ "Maintainer:" ]] +} + +@test "apa info fails without package name" { + run bash "$APA_SCRIPT" info + [ "$status" -eq 1 ] + [[ "$output" =~ "Info command requires a package name" ]] +} + +@test "apa info fails with empty package name" { + run bash "$APA_SCRIPT" info "" + [ "$status" -eq 1 ] + [[ "$output" =~ "Package name cannot be empty" ]] +} + +@test "apa info fails with invalid package name" { + run bash "$APA_SCRIPT" info "invalid@package" + [ "$status" -eq 1 ] + [[ "$output" =~ "Invalid package name" ]] +} + +@test "apa handles unknown command gracefully" { + run bash "$APA_SCRIPT" unknown-command + [ "$status" -eq 1 ] + [[ "$output" =~ "Unknown command: unknown-command" ]] +} + +@test "apa handles unknown option gracefully" { + run bash "$APA_SCRIPT" --unknown-option + [ "$status" -eq 1 ] + [[ "$output" =~ "Unknown option: --unknown-option" ]] +} + +@test "apa quiet option suppresses info output" { + run bash "$APA_SCRIPT" --quiet install test-package + [ "$status" -eq 0 ] + [[ "$output" =~ "installed successfully" ]] + # Info logs should be suppressed in quiet mode +} + +@test "apa debug option enables debug output" { + export APA_LOG_LEVEL="debug" + run bash "$APA_SCRIPT" --debug install test-package + [ "$status" -eq 0 ] +} + +@test "apa timeout option is accepted" { + run bash "$APA_SCRIPT" --timeout 60 --help + [ "$status" -eq 0 ] +} + +@test "apa respects APA_CONFIG_DIR environment variable" { + export APA_CONFIG_DIR="$TEST_TEMP_DIR/custom-config" + bash "$APA_SCRIPT" install test-package + [ -d "$TEST_TEMP_DIR/custom-config" ] +} + +@test "apa respects APA_LOG_LEVEL environment variable" { + export APA_LOG_LEVEL="debug" + run bash "$APA_SCRIPT" install test-package + [ "$status" -eq 0 ] +} + +@test "apa respects APA_TIMEOUT environment variable" { + export APA_TIMEOUT="120" + run bash "$APA_SCRIPT" install test-package + [ "$status" -eq 0 ] +} + +@test "apa handles very long package names gracefully" { + local long_name=$(printf 'a%.0s' {1..1000}) + run bash "$APA_SCRIPT" install "$long_name" + [ "$status" -eq 1 ] + [[ "$output" =~ "Invalid package name" ]] +} + +@test "apa handles package names with only valid characters at boundary" { + # Test exactly valid characters + run bash "$APA_SCRIPT" install "a" + [ "$status" -eq 0 ] + + run bash "$APA_SCRIPT" install "a-b_c.123" + [ "$status" -eq 0 ] + + run bash "$APA_SCRIPT" install "123" + [ "$status" -eq 0 ] +} + +@test "apa rejects command injection attempts" { + run bash "$APA_SCRIPT" install "; rm -rf /" + [ "$status" -eq 1 ] + [[ "$output" =~ "Invalid package name" ]] + + run bash "$APA_SCRIPT" install "$(whoami)" + [ "$status" -eq 1 ] + [[ "$output" =~ "Invalid package name" ]] + + run bash "$APA_SCRIPT" install "|cat /etc/passwd" + [ "$status" -eq 1 ] + [[ "$output" =~ "Invalid package name" ]] +} + +@test "apa handles unicode characters in package names" { + run bash "$APA_SCRIPT" install "测试包" + [ "$status" -eq 1 ] + [[ "$output" =~ "Invalid package name" ]] +} + +@test "apa handles null bytes in input" { + printf "test\0package" | run bash "$APA_SCRIPT" install + [ "$status" -eq 1 ] +} + +@test "apa handles missing HOME directory" { + unset HOME + run bash "$APA_SCRIPT" install test-package + [ "$status" -eq 0 ] +} + +@test "apa handles non-writable config directory parent" { + # This test may need to be skipped in some environments + if [[ "$EUID" -ne 0 ]]; then + export APA_CONFIG_DIR="/root/test-apa-config" + run bash "$APA_SCRIPT" install test-package + # Should handle gracefully, not necessarily succeed + [[ "$status" -eq 0 || "$status" -eq 1 ]] + else + skip "Cannot test non-writable directory as root" + fi +} + +@test "apa operations complete within timeout" { + # Test that operations don't hang indefinitely + timeout 30s bash "$APA_SCRIPT" install test-package + [ $? -ne 124 ] # 124 is timeout's exit code +} + +@test "apa can handle multiple operations in sequence" { + run bash "$APA_SCRIPT" install package1 + [ "$status" -eq 0 ] + + run bash "$APA_SCRIPT" update package1 + [ "$status" -eq 0 ] + + run bash "$APA_SCRIPT" info package1 + [ "$status" -eq 0 ] + + run bash "$APA_SCRIPT" remove package1 + [ "$status" -eq 0 ] +} + +@test "apa output format is consistent across commands" { + # Test that all successful operations produce clean output + run bash "$APA_SCRIPT" install test-package + [ "$status" -eq 0 ] + [[ "$output" =~ ^[[:print:][:space:]]*$ ]] + + run bash "$APA_SCRIPT" list + [ "$status" -eq 0 ] + [[ "$output" =~ ^[[:print:][:space:]]*$ ]] +} + +@test "apa handles concurrent execution" { + # Run multiple instances to test for race conditions + bash "$APA_SCRIPT" install package1 & + bash "$APA_SCRIPT" install package2 & + bash "$APA_SCRIPT" list & + + wait + [ $? -eq 0 ] +} + +@test "apa config directory creation is idempotent" { + # First run should create directory + bash "$APA_SCRIPT" install test-package + [ -d "$APA_CONFIG_DIR" ] + + # Second run should not fail even if directory exists + bash "$APA_SCRIPT" install test-package2 + [ -d "$APA_CONFIG_DIR" ] +} + +@test "apa validates package names consistently across commands" { + # Test that validation works the same for all commands that take package names + local invalid_name="invalid@package" + + run bash "$APA_SCRIPT" install "$invalid_name" + [ "$status" -eq 1 ] + + run bash "$APA_SCRIPT" update "$invalid_name" + [ "$status" -eq 1 ] + + run bash "$APA_SCRIPT" remove "$invalid_name" + [ "$status" -eq 1 ] + + run bash "$APA_SCRIPT" info "$invalid_name" + [ "$status" -eq 1 ] +} \ No newline at end of file diff --git a/tests/test_distro_specific.bats b/tests/test_distro_specific.bats new file mode 100644 index 000000000..891319550 --- /dev/null +++ b/tests/test_distro_specific.bats @@ -0,0 +1,731 @@ +#!/usr/bin/env bats + +# Test suite for distribution-specific functionality +# Testing framework: BATS (Bash Automated Testing System) + +# Load test helpers if they exist +load test_helper 2>/dev/null || true + +setup() { + # Create temporary directory for test fixtures + export BATS_TEST_TMPDIR="$(mktemp -d)" + export ORIGINAL_PATH="$PATH" + export ORIGINAL_ETC="/etc" + + # Create mock /etc directory structure + mkdir -p "$BATS_TEST_TMPDIR/etc" + + # Mock various distribution release files + create_mock_os_release() { + local distro="$1" + case "$distro" in + "ubuntu") + cat > "$BATS_TEST_TMPDIR/etc/os-release" << 'EOF' +NAME="Ubuntu" +VERSION="20.04.3 LTS (Focal Fossa)" +ID=ubuntu +ID_LIKE=debian +PRETTY_NAME="Ubuntu 20.04.3 LTS" +VERSION_ID="20.04" +VERSION_CODENAME=focal +UBUNTU_CODENAME=focal +EOF + ;; + "centos") + cat > "$BATS_TEST_TMPDIR/etc/os-release" << 'EOF' +NAME="CentOS Linux" +VERSION="8" +ID="centos" +ID_LIKE="rhel fedora" +VERSION_ID="8" +PRETTY_NAME="CentOS Linux 8" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:centos:centos:8" +HOME_URL="https://www.centos.org/" +BUG_REPORT_URL="https://bugs.centos.org/" +EOF + ;; + "debian") + cat > "$BATS_TEST_TMPDIR/etc/os-release" << 'EOF' +PRETTY_NAME="Debian GNU/Linux 11 (bullseye)" +NAME="Debian GNU/Linux" +VERSION_ID="11" +VERSION="11 (bullseye)" +VERSION_CODENAME=bullseye +ID=debian +HOME_URL="https://www.debian.org/" +SUPPORT_URL="https://www.debian.org/support" +BUG_REPORT_URL="https://bugs.debian.org/" +EOF + ;; + "rhel") + cat > "$BATS_TEST_TMPDIR/etc/os-release" << 'EOF' +NAME="Red Hat Enterprise Linux" +VERSION="8.5 (Ootpa)" +ID="rhel" +ID_LIKE="fedora" +VERSION_ID="8.5" +PLATFORM_ID="platform:el8" +PRETTY_NAME="Red Hat Enterprise Linux 8.5 (Ootpa)" +ANSI_COLOR="0;31" +EOF + ;; + "fedora") + cat > "$BATS_TEST_TMPDIR/etc/os-release" << 'EOF' +NAME="Fedora Linux" +VERSION="35 (Workstation Edition)" +ID=fedora +VERSION_ID=35 +VERSION_CODENAME="" +PLATFORM_ID="platform:f35" +PRETTY_NAME="Fedora Linux 35 (Workstation Edition)" +ANSI_COLOR="0;38;2;60;110;180" +EOF + ;; + "arch") + cat > "$BATS_TEST_TMPDIR/etc/os-release" << 'EOF' +NAME="Arch Linux" +PRETTY_NAME="Arch Linux" +ID=arch +BUILD_ID=rolling +ANSI_COLOR="38;2;23;147;209" +HOME_URL="https://archlinux.org/" +DOCUMENTATION_URL="https://wiki.archlinux.org/" +SUPPORT_URL="https://bbs.archlinux.org/" +BUG_REPORT_URL="https://bugs.archlinux.org/" +EOF + ;; + esac + } + + # Mock LSB release file + create_mock_lsb_release() { + local distro="$1" + case "$distro" in + "ubuntu") + cat > "$BATS_TEST_TMPDIR/etc/lsb-release" << 'EOF' +DISTRIB_ID=Ubuntu +DISTRIB_RELEASE=20.04 +DISTRIB_CODENAME=focal +DISTRIB_DESCRIPTION="Ubuntu 20.04.3 LTS" +EOF + ;; + esac + } + + # Mock system commands + create_mock_commands() { + mkdir -p "$BATS_TEST_TMPDIR/bin" + export PATH="$BATS_TEST_TMPDIR/bin:$PATH" + + # Mock which command + cat > "$BATS_TEST_TMPDIR/bin/which" << 'EOF' +#!/bin/bash +case "$1" in + "apt"|"apt-get") echo "/usr/bin/apt" ;; + "yum") echo "/usr/bin/yum" ;; + "dnf") echo "/usr/bin/dnf" ;; + "pacman") echo "/usr/bin/pacman" ;; + "zypper") echo "/usr/bin/zypper" ;; + *) exit 1 ;; +esac +EOF + chmod +x "$BATS_TEST_TMPDIR/bin/which" + } +} + +teardown() { + # Clean up test environment + rm -rf "$BATS_TEST_TMPDIR" + export PATH="$ORIGINAL_PATH" + unset BATS_TEST_TMPDIR ORIGINAL_PATH ORIGINAL_ETC +} + +@test "should detect Ubuntu distribution correctly from os-release" { + create_mock_os_release "ubuntu" + + detect_distribution() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + echo "$ID" + return 0 + fi + return 1 + } + + run detect_distribution + [ "$status" -eq 0 ] + [[ "$output" == "ubuntu" ]] +} + +@test "should detect CentOS distribution correctly from os-release" { + create_mock_os_release "centos" + + detect_distribution() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + echo "$ID" + return 0 + fi + return 1 + } + + run detect_distribution + [ "$status" -eq 0 ] + [[ "$output" == "centos" ]] +} + +@test "should detect Debian distribution correctly from os-release" { + create_mock_os_release "debian" + + detect_distribution() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + echo "$ID" + return 0 + fi + return 1 + } + + run detect_distribution + [ "$status" -eq 0 ] + [[ "$output" == "debian" ]] +} + +@test "should detect RHEL distribution correctly from os-release" { + create_mock_os_release "rhel" + + detect_distribution() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + echo "$ID" + return 0 + fi + return 1 + } + + run detect_distribution + [ "$status" -eq 0 ] + [[ "$output" == "rhel" ]] +} + +@test "should detect Fedora distribution correctly from os-release" { + create_mock_os_release "fedora" + + detect_distribution() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + echo "$ID" + return 0 + fi + return 1 + } + + run detect_distribution + [ "$status" -eq 0 ] + [[ "$output" == "fedora" ]] +} + +@test "should detect Arch Linux distribution correctly from os-release" { + create_mock_os_release "arch" + + detect_distribution() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + echo "$ID" + return 0 + fi + return 1 + } + + run detect_distribution + [ "$status" -eq 0 ] + [[ "$output" == "arch" ]] +} + +@test "should detect apt package manager for Ubuntu" { + create_mock_os_release "ubuntu" + create_mock_commands + + get_package_manager() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + case "$ID" in + "ubuntu"|"debian") echo "apt" ;; + "centos"|"rhel") echo "yum" ;; + "fedora") echo "dnf" ;; + "arch") echo "pacman" ;; + "opensuse"|"sles") echo "zypper" ;; + *) echo "unknown" ;; + esac + fi + } + + run get_package_manager + [ "$status" -eq 0 ] + [[ "$output" == "apt" ]] +} + +@test "should detect yum package manager for CentOS" { + create_mock_os_release "centos" + create_mock_commands + + get_package_manager() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + case "$ID" in + "ubuntu"|"debian") echo "apt" ;; + "centos"|"rhel") echo "yum" ;; + "fedora") echo "dnf" ;; + "arch") echo "pacman" ;; + "opensuse"|"sles") echo "zypper" ;; + *) echo "unknown" ;; + esac + fi + } + + run get_package_manager + [ "$status" -eq 0 ] + [[ "$output" == "yum" ]] +} + +@test "should detect dnf package manager for Fedora" { + create_mock_os_release "fedora" + create_mock_commands + + get_package_manager() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + case "$ID" in + "ubuntu"|"debian") echo "apt" ;; + "centos"|"rhel") echo "yum" ;; + "fedora") echo "dnf" ;; + "arch") echo "pacman" ;; + "opensuse"|"sles") echo "zypper" ;; + *) echo "unknown" ;; + esac + fi + } + + run get_package_manager + [ "$status" -eq 0 ] + [[ "$output" == "dnf" ]] +} + +@test "should detect pacman package manager for Arch Linux" { + create_mock_os_release "arch" + create_mock_commands + + get_package_manager() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + case "$ID" in + "ubuntu"|"debian") echo "apt" ;; + "centos"|"rhel") echo "yum" ;; + "fedora") echo "dnf" ;; + "arch") echo "pacman" ;; + "opensuse"|"sles") echo "zypper" ;; + *) echo "unknown" ;; + esac + fi + } + + run get_package_manager + [ "$status" -eq 0 ] + [[ "$output" == "pacman" ]] +} + +@test "should handle missing os-release file gracefully" { + rm -f "$BATS_TEST_TMPDIR/etc/os-release" + + detect_distribution() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + echo "$ID" + return 0 + else + echo "Error: Unable to detect distribution - os-release not found" + return 1 + fi + } + + run detect_distribution + [ "$status" -eq 1 ] + [[ "$output" =~ "Unable to detect distribution" ]] +} + +@test "should handle malformed os-release file" { + cat > "$BATS_TEST_TMPDIR/etc/os-release" << 'EOF' +This is not a valid +os-release file format +ID= +EOF + + detect_distribution() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + if [[ -z "$ID" ]]; then + echo "Error: Invalid distribution ID" + return 1 + fi + echo "$ID" + return 0 + fi + return 1 + } + + run detect_distribution + [ "$status" -eq 1 ] + [[ "$output" =~ "Invalid distribution ID" ]] +} + +@test "should handle unsupported distribution" { + cat > "$BATS_TEST_TMPDIR/etc/os-release" << 'EOF' +NAME="Exotic Linux" +ID="exotic" +VERSION_ID="1.0" +PRETTY_NAME="Exotic Linux 1.0" +EOF + + get_package_manager() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + case "$ID" in + "ubuntu"|"debian") echo "apt" ;; + "centos"|"rhel") echo "yum" ;; + "fedora") echo "dnf" ;; + "arch") echo "pacman" ;; + "opensuse"|"sles") echo "zypper" ;; + *) + echo "Error: Unsupported distribution: $ID" + return 1 + ;; + esac + fi + } + + run get_package_manager + [ "$status" -eq 1 ] + [[ "$output" =~ "Unsupported distribution: exotic" ]] +} + +@test "should fall back to lsb-release when os-release is unavailable" { + rm -f "$BATS_TEST_TMPDIR/etc/os-release" + create_mock_lsb_release "ubuntu" + + detect_distribution_with_fallback() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + echo "$ID" + elif [[ -f "$BATS_TEST_TMPDIR/etc/lsb-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/lsb-release" + echo "${DISTRIB_ID,,}" + else + echo "Error: Unable to detect distribution" + return 1 + fi + } + + run detect_distribution_with_fallback + [ "$status" -eq 0 ] + [[ "$output" == "ubuntu" ]] +} + +@test "should handle empty os-release file" { + touch "$BATS_TEST_TMPDIR/etc/os-release" + + detect_distribution() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + if [[ -z "$ID" ]]; then + echo "Error: Distribution ID not found" + return 1 + fi + echo "$ID" + fi + } + + run detect_distribution + [ "$status" -eq 1 ] + [[ "$output" =~ "Distribution ID not found" ]] +} + +@test "should validate distribution name parameter" { + validate_distribution_name() { + local distro="$1" + if [[ -z "$distro" ]]; then + echo "Error: Distribution name parameter is required" + return 1 + fi + if [[ "$distro" =~ [^a-zA-Z0-9._-] ]]; then + echo "Error: Invalid characters in distribution name" + return 1 + fi + echo "Valid distribution name: $distro" + return 0 + } + + run validate_distribution_name "" + [ "$status" -eq 1 ] + [[ "$output" =~ "parameter is required" ]] +} + +@test "should sanitize distribution names with special characters" { + validate_distribution_name() { + local distro="$1" + if [[ -z "$distro" ]]; then + echo "Error: Distribution name parameter is required" + return 1 + fi + if [[ "$distro" =~ [^a-zA-Z0-9._-] ]]; then + echo "Error: Invalid characters in distribution name" + return 1 + fi + echo "Valid distribution name: $distro" + return 0 + } + + run validate_distribution_name "ubuntu; rm -rf /" + [ "$status" -eq 1 ] + [[ "$output" =~ "Invalid characters" ]] +} + +@test "should accept valid distribution names with hyphens and dots" { + validate_distribution_name() { + local distro="$1" + if [[ -z "$distro" ]]; then + echo "Error: Distribution name parameter is required" + return 1 + fi + if [[ "$distro" =~ [^a-zA-Z0-9._-] ]]; then + echo "Error: Invalid characters in distribution name" + return 1 + fi + echo "Valid distribution name: $distro" + return 0 + } + + run validate_distribution_name "ubuntu-18.04" + [ "$status" -eq 0 ] + [[ "$output" =~ "Valid distribution name: ubuntu-18.04" ]] +} + +@test "should accept distribution names with underscores" { + validate_distribution_name() { + local distro="$1" + if [[ -z "$distro" ]]; then + echo "Error: Distribution name parameter is required" + return 1 + fi + if [[ "$distro" =~ [^a-zA-Z0-9._-] ]]; then + echo "Error: Invalid characters in distribution name" + return 1 + fi + echo "Valid distribution name: $distro" + return 0 + } + + run validate_distribution_name "centos_stream" + [ "$status" -eq 0 ] + [[ "$output" =~ "Valid distribution name: centos_stream" ]] +} + +@test "should complete distribution detection within reasonable time" { + create_mock_os_release "ubuntu" + + detect_distribution() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + echo "$ID" + return 0 + fi + return 1 + } + + run timeout 5s bash -c "detect_distribution" + [ "$status" -eq 0 ] + [[ "$output" == "ubuntu" ]] +} + +@test "should handle concurrent distribution detection calls" { + create_mock_os_release "ubuntu" + + detect_distribution() { + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + echo "$ID" + return 0 + fi + return 1 + } + + local pids=() + for i in {1..3}; do + detect_distribution & + pids+=($!) + done + + local all_success=true + for pid in "${pids[@]}"; do + if ! wait "$pid"; then + all_success=false + fi + done + + [ "$all_success" = true ] +} + +@test "should execute complete distribution-specific workflow for Ubuntu" { + create_mock_os_release "ubuntu" + create_mock_commands + + complete_distro_workflow() { + local distro package_manager + + if [[ -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + . "$BATS_TEST_TMPDIR/etc/os-release" + distro="$ID" + else + echo "Error: Cannot detect distribution" + return 1 + fi + + case "$distro" in + "ubuntu"|"debian") package_manager="apt" ;; + "centos"|"rhel") package_manager="yum" ;; + "fedora") package_manager="dnf" ;; + "arch") package_manager="pacman" ;; + *) package_manager="unknown" ;; + esac + + echo "Distribution: $distro, Package Manager: $package_manager" + echo "Ubuntu workflow completed successfully" + return 0 + } + + run complete_distro_workflow + [ "$status" -eq 0 ] + [[ "$output" =~ "Distribution: ubuntu, Package Manager: apt" ]] + [[ "$output" =~ "Ubuntu workflow completed successfully" ]] +} + +@test "should provide helpful error messages for failures" { + rm -f "$BATS_TEST_TMPDIR/etc/os-release" + + detect_distribution_with_helpful_errors() { + if [[ ! -f "$BATS_TEST_TMPDIR/etc/os-release" ]]; then + echo "Error: Unable to detect Linux distribution" + echo "Try: Ensure /etc/os-release exists and is readable" + echo "Alternative: Check for /etc/lsb-release or distribution-specific files" + return 1 + fi + } + + run detect_distribution_with_helpful_errors + [ "$status" -eq 1 ] + [[ "$output" =~ "Error: Unable to detect Linux distribution" ]] + [[ "$output" =~ "Try: Ensure /etc/os-release exists" ]] + [[ "$output" =~ "Alternative: Check for /etc/lsb-release" ]] +} + +@test "should provide script version information" { + get_script_version() { + echo "distro_specific_utils v1.2.3" + return 0 + } + + run get_script_version + [ "$status" -eq 0 ] + [[ "$output" =~ [0-9]+\.[0-9]+\.[0-9]+ ]] +} + +@test "should display comprehensive help information" { + display_help() { + cat << 'EOF' +Usage: distro_specific_utils [OPTIONS] [COMMAND] + +DESCRIPTION: + Utility for detecting Linux distributions and managing + distribution-specific operations. + +OPTIONS: + -h, --help Show this help message + -v, --version Show version information + -d, --detect Detect current distribution + -p, --package Show package manager for current distribution + +COMMANDS: + detect Detect the current Linux distribution + package-mgr Show the package manager for current distribution + +EXAMPLES: + distro_specific_utils --detect + distro_specific_utils package-mgr + +SUPPORTED DISTRIBUTIONS: + Ubuntu, Debian, CentOS, RHEL, Fedora, Arch Linux, openSUSE +EOF + } + + run display_help + [ "$status" -eq 0 ] + [[ "$output" =~ "Usage:" ]] + [[ "$output" =~ "OPTIONS:" ]] + [[ "$output" =~ "EXAMPLES:" ]] + [[ "$output" =~ "SUPPORTED DISTRIBUTIONS:" ]] +} + +@test "should validate system requirements" { + check_system_requirements() { + local missing_commands=() + + if [[ ! -r /etc/os-release ]] && [[ ! -r /etc/lsb-release ]]; then + echo "Warning: No distribution identification files found" + fi + + for cmd in cat grep sed awk; do + if ! command -v "$cmd" >/dev/null 2>&1; then + missing_commands+=("$cmd") + fi + done + + if [[ ${#missing_commands[@]} -gt 0 ]]; then + echo "Error: Missing required commands: ${missing_commands[*]}" + return 1 + fi + + echo "System requirements satisfied" + return 0 + } + + export PATH="$BATS_TEST_TMPDIR/bin:$PATH" + for cmd in cat grep sed awk; do + echo '#!/bin/bash' > "$BATS_TEST_TMPDIR/bin/$cmd" + echo 'echo "mock $cmd"' >> "$BATS_TEST_TMPDIR/bin/$cmd" + chmod +x "$BATS_TEST_TMPDIR/bin/$cmd" + done + + run check_system_requirements + [ "$status" -eq 0 ] + [[ "$output" =~ "System requirements satisfied" ]] +} + +@test "should handle insufficient permissions gracefully" { + touch "$BATS_TEST_TMPDIR/etc/os-release" + chmod 000 "$BATS_TEST_TMPDIR/etc/os-release" + + handle_permission_error() { + local file="$BATS_TEST_TMPDIR/etc/os-release" + if [[ ! -r "$file" ]]; then + echo "Error: Insufficient permissions to read $file" + echo "Try: Run with appropriate permissions or contact system administrator" + return 1 + fi + return 0 + } + + run handle_permission_error + [ "$status" -eq 1 ] + [[ "$output" =~ "Insufficient permissions" ]] + [[ "$output" =~ "Try: Run with appropriate permissions" ]] +} \ No newline at end of file