Ctrl K
ComunidadGuías…

install.bats

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env bats

load install.helpers

setup() {
    setup_install_env

    stub_bin id 'echo 0'
    skip_enrollment_wait

    export SERVER_ADDRESS="https://cloud.example.test"
    export AGENT_VERSION="v0.0.0-test"
    export AGENT_IMAGE="shellhubio/agent:v0.0.0-test"
    export INSTALL_DIR="$BATS_TEST_TMPDIR/usr-local-bin"
    export TMP_DIR="$BATS_TEST_TMPDIR/tmp"
    export AGENT_KEY="$BATS_TEST_TMPDIR/shellhub.key"
    export PRIVATE_KEY="/host$AGENT_KEY"
    export PROC_VERSION="$BATS_TEST_TMPDIR/proc-version"
    export OS_RELEASE="$BATS_TEST_TMPDIR/os-release"

    mkdir -p "$INSTALL_DIR" "$TMP_DIR"
    echo "Linux version 6.8.0-generic" > "$PROC_VERSION"
    echo 'NAME="Ubuntu"' > "$OS_RELEASE"

    RUNTIME=docker
}

skip_enrollment_wait() {
    touch "$BATS_TEST_TMPDIR/shellhub.key"
    stub_bin sleep 'exit 0'
}

stub_agent_log() {
    cat > "$BATS_TEST_TMPDIR/agent.log"
    stub_bin agent-log "cat '$BATS_TEST_TMPDIR/agent.log'"
}

stub_runtime() {
    stub_bin "$1" "echo \"$1 \$*\" >> \"\$CALLS\"; ${2:+$2; }[ \"\$1\" = ps ] && echo shellhub; exit 0"
}

stub_container_logs() {
    stub_runtime "$1" "[ \"\$1\" = logs ] && cat '$BATS_TEST_TMPDIR/agent.log'"
}

stub_journal() {
    stub_bin journalctl "cat '$BATS_TEST_TMPDIR/agent.log'"
}

with_tenant() {
    export TENANT_ID="00000000-0000-4000-a000-000000000000"
}

as_non_root() {
    stub_bin id 'echo 1000'
    stub_sudo
}

stub_sudo() {
    stub_bin sudo 'echo "sudo $*" >> "$CALLS"; export RAN_UNDER_SUDO=1; exec "$@"'
}

use_podman() {
    RUNTIME=podman
    stub_bin systemctl 'exit 0'
}

container_install() {
    [ -x "$STUB_DIR/$RUNTIME" ] || stub_runtime "$RUNTIME"
    call_install "${RUNTIME}_install" "$@"
}

fake_agent_binary() {
    export AGENT_BINARY="$BATS_TEST_TMPDIR/fake-agent"
    printf '#!/bin/sh\n[ "$*" = "install --help" ] && exit 0\necho "agent $*" >> "$CALLS"\nexit %s\n' "${1:-0}" > "$AGENT_BINARY"
    chmod +x "$AGENT_BINARY"
}

enter_wsl() {
    echo "Linux version ${1:-5.15.167.4-microsoft-standard-WSL2} (Microsoft@Microsoft.com) #1 SMP" > "$PROC_VERSION"
}

serve_release() {
    export BINARY_ARCH=amd64
    RELEASE_DIR="$BATS_TEST_TMPDIR/release"
    mkdir -p "$RELEASE_DIR"
    printf '#!/bin/sh\necho "release binary ran $*" >> "$CALLS"\n' | gzip > "$RELEASE_DIR/shellhub-agent-linux-amd64.gz"
    (cd "$RELEASE_DIR" && sha256sum shellhub-agent-linux-amd64.gz > checksums.txt)

    local tool="${1:-curl}"
    stub_bin "$tool" "echo \"$tool \$*\" >> \"\$CALLS\"
for a; do
    case \"\$prev\" in --output|-o|-O) out=\$a ;; esac
    case \"\$a\" in https://*) url=\$a ;; esac
    prev=\$a
done
cp '$RELEASE_DIR'/\"\${url##*/}\" \"\$out\" 2>/dev/null || exit 1"

    stub_bin id 'echo 1000'
    stub_bin sudo "echo \"sudo \$*\" >> \"\$CALLS\"; [ \"\${1%.new}\" = '$INSTALL_DIR/shellhub-agent' ] && exit 0; exec \"\$@\""
}

assert_installed_release_binary() {
    gzip -dc "$RELEASE_DIR/shellhub-agent-linux-amd64.gz" | cmp -s - "$INSTALL_DIR/shellhub-agent" || {
        echo "$INSTALL_DIR/shellhub-agent is not the binary the release served"
        return 1
    }
    assert_called "sudo $INSTALL_DIR/shellhub-agent install --server-address=$SERVER_ADDRESS"
    refute_called "release binary ran"
}

discover_version() {
    unset AGENT_VERSION
    printf '{"version":"%s","endpoints":{"ssh":"localhost:22"}}\n' "$1" > "$BATS_TEST_TMPDIR/info"
    stub_bin curl "echo \"curl \$*\" >> \"\$CALLS\"; case \"\$*\" in */info*) cat '$BATS_TEST_TMPDIR/info' ;; esac"
}

@test "check_podman_boot_restart aborts when systemctl is missing" {
    call_install check_podman_boot_restart

    [ "$status" -eq 1 ]
    assert_output_contains "systemctl not found"
    assert_output_contains "SKIP_BOOT_RESTART_CHECK=1"
}

@test "check_podman_boot_restart aborts when podman-restart.service is disabled" {
    stub_bin systemctl 'echo "systemctl $*" >> "$CALLS"; exit 1'

    call_install check_podman_boot_restart

    [ "$status" -eq 1 ]
    assert_called "systemctl is-enabled podman-restart.service"
    assert_output_contains "podman-restart.service is not enabled"
    assert_output_contains "sudo systemctl enable podman-restart.service"
}

@test "check_podman_boot_restart passes silently when podman-restart.service is enabled" {
    stub_bin systemctl 'echo "systemctl $*" >> "$CALLS"; exit 0'

    call_install check_podman_boot_restart

    [ "$status" -eq 0 ]
    [ -z "$output" ]
    assert_called "systemctl is-enabled podman-restart.service"
}

@test "SKIP_BOOT_RESTART_CHECK installs anyway when systemctl is missing" {
    export SKIP_BOOT_RESTART_CHECK=1

    call_install check_podman_boot_restart

    [ "$status" -eq 0 ]
    assert_output_contains "skipping podman-restart.service check"
    assert_output_contains "will NOT auto-start on boot"
}

@test "SKIP_BOOT_RESTART_CHECK installs anyway when podman-restart.service is disabled" {
    export SKIP_BOOT_RESTART_CHECK=1
    stub_bin systemctl 'echo "systemctl $*" >> "$CALLS"; exit 1'

    call_install check_podman_boot_restart

    [ "$status" -eq 0 ]
    refute_called "systemctl is-enabled"
    assert_output_contains "skipping podman-restart.service check"
}

@test "enrollment_summary reports a provisioning key ahead of a tenant" {
    with_tenant
    export PROVISIONING_KEY=key-1

    call_install enrollment_summary

    [ "$output" = "provisioning key" ]
}

@test "enrollment_summary reports a tenant as landing pending" {
    with_tenant

    call_install enrollment_summary

    [ "$output" = "tenant $TENANT_ID" ]
}

@test "enrollment_summary points at the login flow when no credential is given" {
    call_install enrollment_summary

    [ "$output" = "none — enroll with 'shellhub-agent login'" ]
}

@test "enrollment_summary reports a tenant a previous enrollment left behind" {
    export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
    export PERSISTED_TENANT=00000000-0000-4000-0000-000000000000

    call_install enrollment_summary

    [ "$output" = "tenant 00000000-0000-4000-0000-000000000000 (persisted by a previous enrollment)" ]
}

@test "enrollment_summary reports a persisted tenant ahead of a provisioning key" {
    export PROVISIONING_KEY=key-1
    export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
    export PERSISTED_TENANT=00000000-0000-4000-0000-000000000000

    call_install enrollment_summary

    [ "$output" = "tenant 00000000-0000-4000-0000-000000000000 (persisted by a previous enrollment)" ]
}

@test "persisted_tenant escalates to read a tenant file only root can read" {
    as_non_root
    export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
    echo "00000000-0000-4000-0000-000000000000" > "$PRIVATE_KEY.tenant"
    chmod 000 "$PRIVATE_KEY.tenant"

    call_install persisted_tenant

    assert_called "sudo head -n 1 $PRIVATE_KEY.tenant"
}

@test "persisted_tenant reads the tenant file a container install names under /host" {
    export PRIVATE_KEY="/host$BATS_TEST_TMPDIR/shellhub.key"
    echo "00000000-0000-4000-0000-000000000000" > "$BATS_TEST_TMPDIR/shellhub.key.tenant"

    call_install persisted_tenant

    [ "$output" = "00000000-0000-4000-0000-000000000000" ]
}

@test "enroll_agent_interactively runs the login flow when no credential names a namespace" {
    stub_bin shellhub-agent

    call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"

    [ "$status" -eq 0 ]
    assert_called "shellhub-agent login"
}

@test "enroll_agent_interactively waits for the agent key before starting the login flow" {
    rm -f "$AGENT_KEY"
    stub_bin sleep 'echo "sleep $*" >> "$CALLS"'
    stub_bin shellhub-agent

    call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"

    [ "$status" -eq 0 ]
    [ "$(grep -c '^sleep 1$' "$CALLS")" -eq 30 ]
    assert_called "shellhub-agent login"
}

@test "enroll_agent_interactively does not wait when the agent key is already on disk" {
    stub_bin sleep 'echo "sleep $*" >> "$CALLS"'
    stub_bin shellhub-agent

    call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"

    [ "$status" -eq 0 ]
    refute_called "sleep"
}

@test "enroll_agent_interactively survives a login the user abandons" {
    stub_bin shellhub-agent 'echo "shellhub-agent $*" >> "$CALLS"; kill -INT "$PPID" "$$"'

    call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"

    [ "$status" -eq 0 ]
}

@test "enroll_agent_interactively lets Ctrl-C stop the installer again after the login" {
    stub_bin shellhub-agent 'echo "shellhub-agent $*" >> "$CALLS"; kill -INT "$PPID" "$$"'

    call_install eval "enroll_agent_interactively shellhub-agent '$AGENT_KEY'; kill -INT \$\$; echo survived"

    [ "$status" -eq 130 ]
    refute_output_contains "survived"
}

@test "enroll_agent_interactively skips the login flow for a provisioning key" {
    export PROVISIONING_KEY=key-1
    stub_bin shellhub-agent

    call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"

    [ "$status" -eq 0 ]
    refute_called "shellhub-agent"
}

@test "enroll_agent_interactively skips the login flow for a persisted tenant" {
    export PERSISTED_TENANT=00000000-0000-4000-0000-000000000000
    export PRIVATE_KEY="$AGENT_KEY"
    stub_bin shellhub-agent

    call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"

    [ "$status" -eq 0 ]
    refute_called "shellhub-agent"
    assert_output_contains "remembered at $AGENT_KEY.tenant"
}

@test "enroll_agent_interactively reports a persisted tenant ahead of a provisioning key" {
    export PROVISIONING_KEY=key-1
    export PRIVATE_KEY="$AGENT_KEY"
    export PERSISTED_TENANT=00000000-0000-4000-0000-000000000000

    call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"

    [ "$status" -eq 0 ]
    assert_output_contains "remembered at $AGENT_KEY.tenant"
}

@test "enroll_agent_interactively skips the login flow for a tenant" {
    with_tenant
    stub_bin shellhub-agent

    call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"

    [ "$status" -eq 0 ]
    refute_called "shellhub-agent"
}

@test "observe_enrollment reports a device the server accepted" {
    stub_bin agent-log 'echo "time=now level=info msg=\"Listening for connections\" status=accepted tenant_id=t"'

    call_install observe_enrollment agent-log

    [ "$status" -eq 0 ]
    assert_output_contains "The device is accepted"
    refute_output_contains "pending"
}

@test "observe_enrollment reports a device the server left pending" {
    stub_bin agent-log 'echo "time=now level=info msg=\"Listening for connections\" status=pending tenant_id=t"'

    call_install observe_enrollment agent-log

    [ "$status" -eq 0 ]
    assert_output_contains "The device is pending: accept it in the console"
}

@test "observe_enrollment reports a device rejected in the console" {
    stub_bin agent-log 'echo "time=now level=info msg=\"Listening for connections\" status=rejected tenant_id=t"'

    call_install observe_enrollment agent-log

    [ "$status" -eq 1 ]
    assert_output_contains "the device is rejected"
    assert_output_contains "A member rejected it in the console. Accept it there to enroll it."
    refute_output_contains "provisioning key"
    refute_output_contains "pending"
}

@test "observe_enrollment names the provisioning key's policy for a rejected device" {
    export PROVISIONING_KEY=key-1
    stub_bin agent-log 'echo "time=now level=info msg=\"Listening for connections\" status=rejected tenant_id=t"'

    call_install observe_enrollment agent-log

    [ "$status" -eq 1 ]
    assert_output_contains "provisioning key's allowlist or webhook refused it"
}

@test "enroll_agent_interactively names a persisted tenant ahead of the observed outcome" {
    export PERSISTED_TENANT=00000000-0000-4000-0000-000000000000
    export PRIVATE_KEY="$AGENT_KEY"
    stub_bin agent-log 'echo "time=now level=info msg=\"Listening for connections\" status=accepted tenant_id=t"'

    call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY" agent-log

    assert_output_contains "remembered at $AGENT_KEY.tenant"
    assert_output_contains "The device is accepted"
}

@test "observe_enrollment does not claim a status the agent did not log" {
    stub_bin agent-log 'echo "time=now level=info msg=\"Listening for connections\" tenant_id=t"'

    call_install observe_enrollment agent-log

    [ "$status" -eq 0 ]
    assert_output_contains "is listening for connections"
    assert_output_contains "accepted or still pending"
}

@test "enroll_agent_interactively does not predict a pending device before a refusal" {
    with_tenant
    stub_bin agent-log 'echo "level=fatal msg=\"Failed to authorize the device\" error=\"the server answered 404 Not Found\""'

    call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY" agent-log

    assert_output_contains "404 Not Found"
    refute_output_contains "pending"
}

@test "refusal_reason prints only the reason a log line carries" {
    call_install refusal_reason 'time="now" level=warning msg="Cannot authorize the device" attempt=1 error="the server answered 404 Not Found: {\"message\":\"namespace not found\"}" server_address="http://localhost:80"'

    [ "$status" -eq 0 ]
    [ "$output" = 'the server answered 404 Not Found: {"message":"namespace not found"}' ]
}

@test "refusal_reason falls back to the line when it carries no reason" {
    call_install refusal_reason 'level=warning msg="Cannot authorize the device"'

    [ "$status" -eq 0 ]
    [ "$output" = 'level=warning msg="Cannot authorize the device"' ]
}

@test "observe_enrollment reports the server's refusal" {
    stub_agent_log <<'LOG'
time="now" level=fatal msg="Failed to authorize the device" error="the server answered 404 Not Found: {\"message\":\"namespace not found\"}" server_address="http://localhost:80"
LOG

    call_install observe_enrollment agent-log

    [ "$status" -eq 1 ]
    assert_output_contains "The server refused this device"
    assert_output_contains 'the server answered 404 Not Found: {"message":"namespace not found"}'
}

@test "observe_enrollment keeps the agent's own bookkeeping out of what it reports" {
    stub_agent_log <<'LOG'
time="now" level=fatal msg="Failed to authorize the device" error="the server answered 404 Not Found" server_address="http://localhost:80"
LOG

    call_install observe_enrollment agent-log

    assert_output_contains "the server answered 404 Not Found"
    refute_output_contains "level=fatal"
    refute_output_contains "server_address"
}

@test "observe_enrollment reports the settings an agent refused its own configuration over" {
    stub_agent_log <<'LOG'
time="now" level=error msg="SHELLHUB_TENANT_ID must be a UUID"
time="now" level=fatal msg="Failed to load the configuration from the environment variables" error="validation failed"
LOG

    call_install observe_enrollment agent-log

    [ "$status" -eq 1 ]
    assert_output_contains "refused its own configuration"
    assert_output_contains "SHELLHUB_TENANT_ID must be a UUID"
    refute_output_contains "has not enrolled yet"
}

@test "observe_enrollment names the host path of a persisted tenant the agent refused" {
    stub_agent_log <<'LOG'
time="now" level=error msg="/host/etc/shellhub.key.tenant must be a UUID"
time="now" level=fatal msg="Failed to load the configuration from the environment variables" error="invalid structure"
LOG

    call_install observe_enrollment agent-log

    [ "$status" -eq 1 ]
    assert_output_contains "   /etc/shellhub.key.tenant must be a UUID"
    refute_output_contains "invalid structure"
}

@test "observe_enrollment names each refused setting once across agent restarts" {
    stub_agent_log <<'LOG'
time="now" level=error msg="SHELLHUB_TENANT_ID must be a UUID"
time="now" level=fatal msg="Failed to load the configuration from the environment variables" error="invalid structure"
time="now" level=error msg="SHELLHUB_TENANT_ID must be a UUID"
time="now" level=fatal msg="Failed to load the configuration from the environment variables" error="invalid structure"
LOG

    call_install observe_enrollment agent-log

    [ "$status" -eq 1 ]
    [ "$(echo "$output" | grep -c 'SHELLHUB_TENANT_ID must be a UUID')" -eq 1 ]
}

@test "observe_enrollment falls back to the error when a refused configuration names no setting" {
    stub_agent_log <<'LOG'
time="now" level=fatal msg="Failed to load the configuration from the environment variables" error="missing required value: SERVER_ADDRESS"
LOG

    call_install observe_enrollment agent-log

    [ "$status" -eq 1 ]
    assert_output_contains "missing required value: SERVER_ADDRESS"
    refute_output_contains "has not enrolled yet"
}

@test "observe_enrollment reports an agent still retrying when the window closes" {
    stub_agent_log <<'LOG'
time="now" level=warning msg="Cannot authorize the device, retrying until the server accepts it" attempt=1 error="the server answered 404 Not Found: {\"message\":\"namespace not found\"}"
LOG

    call_install observe_enrollment agent-log

    [ "$status" -eq 1 ]
    assert_output_contains "The server is still refusing this device"
    assert_output_contains 'the server answered 404 Not Found: {"message":"namespace not found"}'
    refute_output_contains "level=warning"
}

@test "observe_enrollment names the command to inspect an agent that says nothing" {
    stub_bin agent-log

    call_install observe_enrollment agent-log

    [ "$status" -eq 0 ]
    assert_output_contains "agent-log"
}

@test "observe_enrollment does not claim an outcome for a runtime it cannot read" {
    call_install observe_enrollment ""

    [ "$status" -eq 0 ]
    assert_output_contains "does not expose"
}

@test "enroll_agent_interactively observes the outcome of a provisioning key enrollment" {
    export PROVISIONING_KEY=key-1
    stub_bin agent-log 'echo "level=fatal msg=\"Failed to authorize the device\" error=\"the server answered 404 Not Found\""'

    call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY" agent-log

    assert_output_contains "404 Not Found"
}

@test "enroll_agent_interactively does not claim an outcome it cannot observe" {
    export PROVISIONING_KEY=key-1

    call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"

    assert_output_contains "does not expose"
}

@test "enroll_agent_interactively observes the outcome of a persisted tenant enrollment" {
    export PERSISTED_TENANT=00000000-0000-4000-0000-000000000000
    export PRIVATE_KEY="$AGENT_KEY"
    stub_bin agent-log 'echo "level=fatal msg=\"Failed to authorize the device\" error=\"the server answered 404 Not Found\""'

    call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY" agent-log

    assert_output_contains "remembered at $AGENT_KEY.tenant"
    assert_output_contains "404 Not Found"
}

@test "enroll_agent_interactively observes the outcome of a tenant enrollment" {
    with_tenant
    stub_bin agent-log 'echo "level=fatal msg=\"Failed to authorize the device\" error=\"the server answered 404 Not Found\""'

    call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY" agent-log

    assert_output_contains "404 Not Found"
}

@test "docker_install runs the container with unless-stopped so it survives a reboot" {
    container_install

    [ "$status" -eq 0 ]
    assert_called "docker run -d --name=shellhub --restart=unless-stopped"
}

@test "podman_install runs the container with unless-stopped so podman-restart.service picks it up" {
    use_podman

    container_install

    [ "$status" -eq 0 ]
    assert_called "podman run -d --name=shellhub --replace --restart=unless-stopped"
}

@test "docker_install grants the agent the host access it needs" {
    container_install

    [ "$status" -eq 0 ]
    assert_called "--privileged --net=host --pid=host"
    assert_called "-v /:/host"
    assert_called "-v /var/run/docker.sock:/var/run/docker.sock"
    assert_called "-v /etc/resolv.conf:/etc/resolv.conf"
}

@test "podman_install grants the agent the host access it needs" {
    use_podman

    container_install

    [ "$status" -eq 0 ]
    assert_called "--privileged --pid=host --security-opt label=disable --network host"
    assert_called "-v /:/host"
}

@test "podman_install presents the podman socket to the agent as the docker socket" {
    use_podman

    container_install

    [ "$status" -eq 0 ]
    assert_called "-v /var/run/podman/podman.sock:/var/run/docker.sock"
}

@test "docker_install labels the agent container so the wrapper can find it" {
    container_install

    [ "$status" -eq 0 ]
    assert_called "--label shellhub.role=agent"
    assert_output_contains "Starting ShellHub container in Agent mode"
}

@test "podman_install labels the agent container so the wrapper can find it" {
    use_podman

    container_install

    [ "$status" -eq 0 ]
    assert_called "--label shellhub.role=agent"
}

@test "docker_install treats an explicit agent mode as the default mode" {
    container_install agent

    [ "$status" -eq 0 ]
    assert_called "--label shellhub.role=agent"
    assert_output_contains "Starting ShellHub container in Agent mode"
}

@test "podman_install treats an explicit agent mode as the default mode" {
    use_podman

    container_install agent

    [ "$status" -eq 0 ]
    assert_called "--label shellhub.role=agent"
}

@test "docker_install omits the agent label in connector mode" {
    with_tenant

    container_install connector

    [ "$status" -eq 0 ]
    refute_called "--label shellhub.role=agent"
    assert_called "--name=shellhub-connector"
    assert_called "-e SHELLHUB_PRIVATE_KEYS=/host/etc/shellhub/connector/keys"
}

@test "podman_install omits the agent label in connector mode" {
    use_podman
    with_tenant

    container_install connector

    [ "$status" -eq 0 ]
    refute_called "--label shellhub.role=agent"
    assert_called "--name=shellhub-connector"
    assert_called "-e SHELLHUB_PRIVATE_KEYS=/host/etc/shellhub/connector/keys"
}

@test "docker_install refuses connector mode without a tenant or a provisioning key" {
    container_install connector

    [ "$status" -eq 1 ]
    assert_output_contains "TENANT_ID or PROVISIONING_KEY is required"
    refute_called "docker run"
}

@test "podman_install refuses connector mode without a tenant or a provisioning key" {
    use_podman

    container_install connector

    [ "$status" -eq 1 ]
    assert_output_contains "TENANT_ID or PROVISIONING_KEY is required"
    refute_called "podman run"
}

@test "docker_install starts the connector with a provisioning key alone" {
    export PROVISIONING_KEY=key-1

    container_install connector

    [ "$status" -eq 0 ]
    assert_called "-e SHELLHUB_PROVISIONING_KEY=key-1"
    refute_called "SHELLHUB_TENANT_ID"
}

@test "podman_install starts the connector with a provisioning key alone" {
    use_podman
    export PROVISIONING_KEY=key-1

    container_install connector

    [ "$status" -eq 0 ]
    assert_called "-e SHELLHUB_PROVISIONING_KEY=key-1"
    refute_called "SHELLHUB_TENANT_ID"
}

@test "docker_install rejects an unknown mode" {
    container_install bogus

    [ "$status" -eq 1 ]
    assert_output_contains "Invalid mode: bogus"
    refute_called "docker run"
}

@test "podman_install rejects an unknown mode" {
    use_podman

    container_install bogus

    [ "$status" -eq 1 ]
    assert_output_contains "Invalid mode: bogus"
    refute_called "podman run"
}

@test "docker_install maps credentials and preferences onto the agent environment" {
    with_tenant
    export PROVISIONING_KEY=key-1 KEEPALIVE_INTERVAL=45
    export PREFERRED_HOSTNAME=box PREFERRED_IDENTITY=eth0

    container_install

    [ "$status" -eq 0 ]
    assert_called "-e SHELLHUB_PROVISIONING_KEY=key-1"
    assert_called "-e SHELLHUB_KEEPALIVE_INTERVAL=45"
    assert_called "-e SHELLHUB_PREFERRED_HOSTNAME=box"
    assert_called "-e SHELLHUB_PREFERRED_IDENTITY=eth0"
    assert_called "-e SHELLHUB_TENANT_ID=$TENANT_ID"
    assert_called "-e SHELLHUB_SERVER_ADDRESS=$SERVER_ADDRESS"
}

@test "podman_install maps credentials and preferences onto the agent environment" {
    use_podman
    with_tenant
    export PROVISIONING_KEY=key-1 KEEPALIVE_INTERVAL=45
    export PREFERRED_HOSTNAME=box PREFERRED_IDENTITY=eth0

    container_install

    [ "$status" -eq 0 ]
    assert_called "-e SHELLHUB_PROVISIONING_KEY=key-1"
    assert_called "-e SHELLHUB_KEEPALIVE_INTERVAL=45"
    assert_called "-e SHELLHUB_PREFERRED_HOSTNAME=box"
    assert_called "-e SHELLHUB_PREFERRED_IDENTITY=eth0"
    assert_called "-e SHELLHUB_TENANT_ID=$TENANT_ID"
    assert_called "-e SHELLHUB_SERVER_ADDRESS=$SERVER_ADDRESS"
}

@test "docker_install omits environment variables for options left unset" {
    container_install

    [ "$status" -eq 0 ]
    refute_called "SHELLHUB_TENANT_ID"
    refute_called "SHELLHUB_PROVISIONING_KEY"
    refute_called "SHELLHUB_KEEPALIVE_INTERVAL"
    refute_called "SHELLHUB_PREFERRED_"
    assert_called "-e SHELLHUB_PRIVATE_KEY=$PRIVATE_KEY"
}

@test "podman_install omits environment variables for options left unset" {
    use_podman

    container_install

    [ "$status" -eq 0 ]
    refute_called "SHELLHUB_TENANT_ID"
    refute_called "SHELLHUB_PROVISIONING_KEY"
    refute_called "SHELLHUB_KEEPALIVE_INTERVAL"
    refute_called "SHELLHUB_PREFERRED_"
    assert_called "-e SHELLHUB_PRIVATE_KEY=$PRIVATE_KEY"
}

@test "docker_install pulls the agent image by default" {
    container_install

    [ "$status" -eq 0 ]
    assert_called "docker pull -q $AGENT_IMAGE"
    assert_output_contains "Downloading ShellHub container image"
}

@test "podman_install pulls the agent image by default" {
    use_podman

    container_install

    [ "$status" -eq 0 ]
    assert_called "podman pull -q $AGENT_IMAGE"
    assert_output_contains "Downloading ShellHub container image"
}

@test "docker_install skips the pull when the image was overridden" {
    export AGENT_IMAGE_OVERRIDDEN=1

    container_install

    [ "$status" -eq 0 ]
    refute_called "docker pull"
    assert_output_contains "skipping pull"
}

@test "podman_install skips the pull when the image was overridden" {
    use_podman
    export AGENT_IMAGE_OVERRIDDEN=1

    container_install

    [ "$status" -eq 0 ]
    refute_called "podman pull"
    assert_output_contains "skipping pull"
}

@test "docker_install aborts when the image pull fails" {
    stub_bin docker 'echo "docker $*" >> "$CALLS"; [ "$1" = pull ] && exit 1; exit 0'

    container_install

    [ "$status" -eq 1 ]
    assert_output_contains "Failed to download shellhub container image"
    refute_called "docker run"
}

@test "podman_install aborts when the image pull fails" {
    use_podman
    stub_bin podman 'echo "podman $*" >> "$CALLS"; [ "$1" = pull ] && exit 1; exit 0'

    container_install

    [ "$status" -eq 1 ]
    assert_output_contains "Failed to download shellhub container image"
    refute_called "podman run"
}

@test "docker_install honours CONTAINER_NAME" {
    export CONTAINER_NAME=my-agent

    container_install

    [ "$status" -eq 0 ]
    assert_called "--name=my-agent"
    assert_called "docker rm -f my-agent"
}

@test "podman_install honours CONTAINER_NAME" {
    use_podman
    export CONTAINER_NAME=my-agent

    container_install

    [ "$status" -eq 0 ]
    assert_called "--name=my-agent"
}

@test "docker_install removes an existing container so the new one carries the role label" {
    container_install

    [ "$status" -eq 0 ]
    assert_called "docker rm -f shellhub"
}

@test "podman_install checks the boot restart unit before touching podman" {
    RUNTIME=podman

    container_install

    [ "$status" -eq 1 ]
    assert_output_contains "systemctl not found"
    refute_called "podman"
}

@test "install_agent_wrapper writes an executable wrapper bound to podman" {
    call_install install_agent_wrapper podman

    [ "$status" -eq 0 ]
    [ -x "$INSTALL_DIR/shellhub-agent" ]
    assert_output_contains "Installed shellhub-agent wrapper at $INSTALL_DIR/shellhub-agent"
    assert_file_contains "$INSTALL_DIR/shellhub-agent" "podman ps --filter label=shellhub.role=agent"
    assert_file_contains "$INSTALL_DIR/shellhub-agent" "podman exec"
    refute_file_contains "$INSTALL_DIR/shellhub-agent" "docker exec"
}

@test "install_agent_wrapper writes an executable wrapper bound to docker" {
    call_install install_agent_wrapper docker

    [ "$status" -eq 0 ]
    [ -x "$INSTALL_DIR/shellhub-agent" ]
    assert_file_contains "$INSTALL_DIR/shellhub-agent" "docker ps --filter label=shellhub.role=agent"
    assert_file_contains "$INSTALL_DIR/shellhub-agent" "docker exec"
    refute_file_contains "$INSTALL_DIR/shellhub-agent" "podman exec"
}

@test "install_agent_wrapper escalates to write into a root-owned directory" {
    as_non_root

    call_install install_agent_wrapper docker

    [ "$status" -eq 0 ]
    assert_called "sudo tee $INSTALL_DIR/shellhub-agent"
    assert_called "sudo chmod +x $INSTALL_DIR/shellhub-agent"
}

@test "install_agent_wrapper aborts when the install directory does not exist" {
    export INSTALL_DIR="$BATS_TEST_TMPDIR/absent"

    call_install install_agent_wrapper docker

    [ "$status" -eq 1 ]
    assert_output_contains "Failed to install shellhub-agent wrapper"
}

@test "snap_install requires a tenant" {
    stub_bin snap

    call_install snap_install

    [ "$status" -eq 1 ]
    assert_output_contains "TENANT_ID is required"
    refute_called "snap install"
}

@test "snap_install refuses a provisioning key it cannot honour" {
    with_tenant
    export PROVISIONING_KEY=key-1
    stub_bin snap

    call_install snap_install

    [ "$status" -eq 1 ]
    assert_output_contains "PROVISIONING_KEY is not supported by the snap install method"
    refute_called "snap install"
}

@test "snap_install aborts when snap is not available" {
    with_tenant

    call_install snap_install

    [ "$status" -eq 1 ]
    assert_output_contains "Snap is not installed or not supported on this system"
}

@test "snap_install configures the service from the environment" {
    with_tenant
    export PREFERRED_HOSTNAME=box KEEPALIVE_INTERVAL=45
    stub_bin snap
    stub_sudo

    call_install snap_install

    [ "$status" -eq 0 ]
    assert_called "snap install --classic --channel=latest/stable shellhub"
    assert_called "snap set shellhub server-address=$SERVER_ADDRESS"
    assert_called "snap set shellhub tenant-id=$TENANT_ID"
    assert_called "snap set shellhub preferred-hostname=box"
    assert_called "snap set shellhub keepalive-interval=45"
    assert_called "snap start shellhub"
}

@test "standalone_install passes the credentials on to the agent service" {
    with_tenant
    export PROVISIONING_KEY=key-1 KEEPALIVE_INTERVAL=45
    fake_agent_binary

    call_install standalone_install

    [ "$status" -eq 0 ]
    assert_called "agent install --server-address=$SERVER_ADDRESS --tenant-id=$TENANT_ID --provisioning-key=key-1 --keepalive-interval=45"
    [ -x "$INSTALL_DIR/shellhub-agent" ]
}

@test "standalone_install reads only the journal of the service it started" {
    with_tenant
    fake_agent_binary
    stub_bin date 'echo 1700000000'
    stub_bin journalctl

    call_install standalone_install

    [ "$status" -eq 0 ]
    assert_called "journalctl -u shellhub-agent --no-pager -n 50 --since @1700000000"
}

@test "standalone_install escalates when it is not already root" {
    with_tenant
    as_non_root
    fake_agent_binary

    call_install standalone_install

    [ "$status" -eq 0 ]
    assert_output_contains "This install method requires root privileges"
    assert_called "sudo $INSTALL_DIR/shellhub-agent install"
}

@test "standalone_install removes the binary when the service install fails" {
    with_tenant
    fake_agent_binary 1

    call_install standalone_install

    [ "$status" -eq 1 ]
    assert_output_contains "Failed to install ShellHub agent service"
    [ ! -e "$INSTALL_DIR/shellhub-agent" ]
    [ ! -e "$TMP_DIR" ]
}

@test "standalone_install starts the agent it stopped again when the service install fails" {
    with_tenant
    fake_agent_binary 1
    local stopped="$BATS_TEST_TMPDIR/old-agent-stopped"
    stub_bin systemctl "echo \"systemctl \$*\" >> \"\$CALLS\"
case \"\$1\" in
stop) : > '$stopped' ;;
is-active) [ ! -e '$stopped' ] ;;
esac"

    call_install standalone_install

    [ "$status" -eq 1 ]
    assert_called "systemctl enable --now shellhub-agent"
    [ -x "$INSTALL_DIR/shellhub-agent" ]
}

@test "standalone_install leaves the running agent alone when the new binary cannot run its installer" {
    with_tenant
    export AGENT_BINARY="$BATS_TEST_TMPDIR/foreign-agent"
    printf '#!/bin/sh\n[ "$1" = --help ] && exit 0\nexit 1\n' > "$AGENT_BINARY"
    chmod +x "$AGENT_BINARY"
    mkdir -p "$INSTALL_DIR"
    echo "old agent" > "$INSTALL_DIR/shellhub-agent"
    stub_bin systemctl 'echo "systemctl $*" >> "$CALLS"; exit 0'

    call_install standalone_install

    [ "$status" -eq 1 ]
    assert_output_contains "cannot run its installer"
    refute_called "systemctl stop"
    [ "$(cat "$INSTALL_DIR/shellhub-agent")" = "old agent" ]
    [ ! -e "$INSTALL_DIR/shellhub-agent.new" ]
}

@test "standalone_install reports a local binary it cannot copy" {
    with_tenant
    fake_agent_binary
    mkdir -p "$INSTALL_DIR"
    cp "$AGENT_BINARY" "$INSTALL_DIR/shellhub-agent.new"
    export AGENT_BINARY="$BATS_TEST_TMPDIR/missing-agent"

    call_install standalone_install

    [ "$status" -eq 1 ]
    assert_output_contains "Could not copy $AGENT_BINARY"
    [ ! -e "$INSTALL_DIR/shellhub-agent" ]
    [ ! -e "$INSTALL_DIR/shellhub-agent.new" ]
    [ ! -e "$TMP_DIR" ]
}

@test "standalone_install reports a release binary it cannot extract" {
    with_tenant
    serve_release
    echo "not gzip" > "$RELEASE_DIR/shellhub-agent-linux-amd64.gz"
    (cd "$RELEASE_DIR" && sha256sum shellhub-agent-linux-amd64.gz > checksums.txt)

    call_install standalone_install

    [ "$status" -eq 1 ]
    assert_output_contains "Failed to extract the agent binary"
    refute_output_contains "Could not replace"
    [ ! -e "$INSTALL_DIR/shellhub-agent" ]
    [ ! -e "$TMP_DIR" ]
}

@test "standalone_install installs the agent binary that matches the release checksum" {
    with_tenant
    serve_release

    call_install standalone_install

    [ "$status" -eq 0 ]
    assert_called "https://github.com/shellhub-io/shellhub/releases/download/v0.0.0-test/shellhub-agent-linux-amd64.gz"
    assert_called "https://github.com/shellhub-io/shellhub/releases/download/v0.0.0-test/checksums.txt"
    assert_installed_release_binary
}

@test "standalone_install installs the agent binary that matches the release checksum with wget" {
    with_tenant
    serve_release wget

    call_install standalone_install

    [ "$status" -eq 0 ]
    assert_called "wget"
    assert_called "https://github.com/shellhub-io/shellhub/releases/download/v0.0.0-test/checksums.txt"
    assert_installed_release_binary
}

@test "standalone_install refuses an agent binary that does not match the release checksum" {
    with_tenant

    for tool in curl wget; do
        echo "downloading with $tool"
        rm -f "$STUB_DIR/curl" "$STUB_DIR/wget"
        mkdir -p "$TMP_DIR"
        serve_release "$tool"
        printf '#!/bin/sh\necho "tampered $*" >> "$CALLS"\n' | gzip > "$RELEASE_DIR/shellhub-agent-linux-amd64.gz"

        call_install standalone_install

        [ "$status" -eq 1 ]
        assert_called "$tool"
        assert_output_contains "does not match the checksum published with release v0.0.0-test"
        refute_called "tampered"
        [ ! -e "$INSTALL_DIR/shellhub-agent" ]
    done
}

@test "standalone_install refuses a release whose checksums do not list the agent binary" {
    with_tenant
    serve_release
    sed -i 's/linux-amd64/linux-arm64/' "$RELEASE_DIR/checksums.txt"

    call_install standalone_install

    [ "$status" -eq 1 ]
    assert_output_contains "does not match the checksum published with release v0.0.0-test"
    refute_called "agent install"
    [ ! -e "$INSTALL_DIR/shellhub-agent" ]
}

@test "standalone_install refuses a release whose checksums it cannot download" {
    with_tenant
    serve_release
    rm "$RELEASE_DIR/checksums.txt"

    call_install standalone_install

    [ "$status" -eq 1 ]
    assert_output_contains "Could not download the checksums published with release v0.0.0-test"
    refute_called "agent install"
    [ ! -e "$INSTALL_DIR/shellhub-agent" ]
}

@test "docker_uninstall removes the container and the wrapper" {
    stub_bin docker 'echo "docker $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; exit 0'
    call_install install_agent_wrapper docker

    call_install docker_uninstall

    [ "$status" -eq 0 ]
    assert_called "docker rm -f shellhub"
    [ ! -e "$INSTALL_DIR/shellhub-agent" ]
}

@test "docker_uninstall escalates when it is not already root" {
    stub_bin docker 'echo "docker $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; exit 0'
    call_install install_agent_wrapper docker
    as_non_root

    call_install docker_uninstall

    [ "$status" -eq 0 ]
    assert_called "sudo docker rm -f shellhub"
    assert_called "sudo rm -f $INSTALL_DIR/shellhub-agent"
}

@test "podman_uninstall removes the container and the wrapper" {
    stub_bin podman 'echo "podman $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; exit 0'
    call_install install_agent_wrapper podman

    call_install podman_uninstall

    [ "$status" -eq 0 ]
    assert_called "podman rm -f shellhub"
    [ ! -e "$INSTALL_DIR/shellhub-agent" ]
}

@test "podman_uninstall escalates when it is not already root" {
    stub_bin podman 'echo "podman $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; exit 0'
    call_install install_agent_wrapper podman
    as_non_root

    call_install podman_uninstall

    [ "$status" -eq 0 ]
    assert_called "sudo podman rm -f shellhub"
    assert_called "sudo rm -f $INSTALL_DIR/shellhub-agent"
}

@test "docker_uninstall reports a container that was already gone" {
    stub_bin docker

    call_install docker_uninstall

    [ "$status" -eq 0 ]
    assert_output_contains "not found (may already be removed)"
    refute_called "docker rm -f"
}

@test "docker_uninstall aborts when the container cannot be removed" {
    stub_bin docker 'echo "docker $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; [ "$1" != rm ]'

    call_install docker_uninstall

    [ "$status" -eq 1 ]
    assert_output_contains "Failed to remove container 'shellhub'"
    refute_output_contains "uninstalled"
}

@test "podman_uninstall aborts when the container cannot be removed" {
    stub_bin podman 'echo "podman $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; [ "$1" != rm ]'

    call_install podman_uninstall

    [ "$status" -eq 1 ]
    assert_output_contains "Failed to remove container 'shellhub'"
    refute_output_contains "uninstalled"
}

@test "docker_uninstall aborts when it cannot list containers" {
    stub_bin docker 'echo "docker $*" >> "$CALLS"; [ "$1" != ps ]'

    call_install docker_uninstall

    [ "$status" -eq 1 ]
    assert_output_contains "Failed to look up container 'shellhub'"
    refute_output_contains "not found"
    refute_output_contains "uninstalled"
}

@test "podman_uninstall aborts when it cannot list containers" {
    stub_bin podman 'echo "podman $*" >> "$CALLS"; [ "$1" != ps ]'

    call_install podman_uninstall

    [ "$status" -eq 1 ]
    assert_output_contains "Failed to look up container 'shellhub'"
    refute_output_contains "not found"
    refute_output_contains "uninstalled"
}

@test "podman_uninstall reports a container that was already gone" {
    stub_bin podman

    call_install podman_uninstall

    [ "$status" -eq 0 ]
    assert_output_contains "not found (may already be removed)"
    refute_called "podman rm -f"
}

@test "uninstall names the tenant file it leaves behind" {
    export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
    echo "00000000-0000-4000-0000-000000000000" > "$PRIVATE_KEY.tenant"
    stub_bin docker

    call_install docker_uninstall

    assert_output_contains "$PRIVATE_KEY.tenant"
}

@test "uninstall names the tenant file a container install left behind under /host" {
    export PRIVATE_KEY="/host$BATS_TEST_TMPDIR/shellhub.key"
    echo "00000000-0000-4000-0000-000000000000" > "$BATS_TEST_TMPDIR/shellhub.key.tenant"
    stub_bin docker

    call_install docker_uninstall

    assert_output_contains "$BATS_TEST_TMPDIR/shellhub.key.tenant"
    [[ "$output" != *"/host$BATS_TEST_TMPDIR"* ]]
}

@test "podman_uninstall names the tenant file it leaves behind" {
    export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
    echo "00000000-0000-4000-0000-000000000000" > "$PRIVATE_KEY.tenant"
    stub_bin podman

    call_install podman_uninstall

    assert_output_contains "$PRIVATE_KEY.tenant"
}

@test "standalone_uninstall names the tenant file it leaves behind" {
    export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
    echo "00000000-0000-4000-0000-000000000000" > "$PRIVATE_KEY.tenant"
    fake_agent_binary
    cp "$AGENT_BINARY" "$INSTALL_DIR/shellhub-agent"

    call_install standalone_uninstall

    assert_output_contains "$PRIVATE_KEY.tenant"
}

@test "uninstall names a tenant file it cannot read a tenant from" {
    export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
    : > "$PRIVATE_KEY.tenant"
    stub_bin docker

    call_install docker_uninstall

    [ "$status" -eq 0 ]
    assert_output_contains "remembered in $PRIVATE_KEY.tenant"
}

@test "uninstall stays quiet about a tenant file that is not there" {
    export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
    stub_bin docker

    call_install docker_uninstall

    [ "$status" -eq 0 ]
    assert_output_contains "ShellHub agent uninstalled."
    refute_output_contains ".tenant"
}

@test "standalone_uninstall reports a missing binary" {
    call_install standalone_uninstall

    [ "$status" -eq 1 ]
    assert_output_contains "ShellHub agent binary not found"
}

@test "standalone_uninstall looks in the default install dir when the environment names none" {
    unset INSTALL_DIR

    call_install standalone_uninstall

    [ "$status" -eq 1 ]
    assert_output_contains "/usr/local/bin/shellhub-agent"
}

@test "standalone_uninstall stops the service and removes the binary" {
    with_tenant
    fake_agent_binary
    call_install standalone_install

    call_install standalone_uninstall

    [ "$status" -eq 0 ]
    assert_called "agent uninstall"
    [ ! -e "$INSTALL_DIR/shellhub-agent" ]
}

@test "wsl_install aborts when systemd is not enabled" {
    stub_bin systemctl 'exit 1'

    call_install wsl_install

    [ "$status" -eq 1 ]
    assert_output_contains "requires systemd to be enabled"
}

@test "wsl_install aborts when WSL networking is not mirrored" {
    stub_bin systemctl 'exit 0'
    stub_bin wslinfo 'echo nat'

    call_install wsl_install

    [ "$status" -eq 1 ]
    assert_output_contains "networking mode must be set to mirrored"
}

@test "wsl_install falls through to the standalone install once both preconditions hold" {
    with_tenant
    stub_bin systemctl 'exit 0'
    stub_bin wslinfo 'echo mirrored'
    fake_agent_binary

    call_install wsl_install

    [ "$status" -eq 0 ]
    assert_called "agent install --server-address=$SERVER_ADDRESS"
}

@test "the installer aborts on FreeBSD" {
    stub_bin uname 'echo FreeBSD'

    run_install

    [ "$status" -eq 1 ]
    assert_output_contains "Automatic installation is not supported on FreeBSD"
}

@test "detection prefers docker when it is accessible" {
    stub_runtime docker
    stub_runtime podman

    run_install

    [ "$status" -eq 0 ]
    assert_output_contains "Installing ShellHub using docker method"
    assert_called "docker run -d"
    refute_called "podman run"
}

@test "detection falls back to podman when docker is absent" {
    stub_runtime podman
    stub_bin systemctl 'exit 0'

    run_install

    [ "$status" -eq 0 ]
    assert_output_contains "Installing ShellHub using podman method"
    assert_called "podman run -d"
}

@test "detection retries docker with sudo when it is only reachable as root" {
    unset AGENT_IMAGE
    stub_runtime docker '[ -z "$RAN_UNDER_SUDO" ] && exit 1'
    stub_sudo

    run_install

    [ "$status" -eq 0 ]
    assert_called "sudo docker info"
    assert_called "sudo docker pull -q docker.io/shellhubio/agent:$AGENT_VERSION"
    assert_called "sudo docker run -d"
    assert_called "sudo $INSTALL_DIR/shellhub-agent login"
}

@test "detection retries podman with sudo when it is only reachable as root" {
    unset AGENT_IMAGE
    stub_runtime podman '[ -z "$RAN_UNDER_SUDO" ] && exit 1'
    stub_bin systemctl 'exit 0'
    stub_sudo

    run_install

    [ "$status" -eq 0 ]
    assert_called "sudo podman info"
    assert_called "sudo podman pull -q docker.io/shellhubio/agent:$AGENT_VERSION"
    assert_called "sudo podman run -d"
    assert_called "sudo $INSTALL_DIR/shellhub-agent login"
}

@test "detection falls back to snap when no container runtime is available" {
    with_tenant
    stub_bin snap
    stub_sudo

    run_install

    [ "$status" -eq 0 ]
    assert_output_contains "Installing ShellHub using snap method"
    assert_called "snap install --classic --channel=latest/stable shellhub"
}

@test "detection falls back to the standalone install when nothing else is available" {
    fake_agent_binary

    run_install

    [ "$status" -eq 0 ]
    assert_output_contains "Installing ShellHub using standalone method"
    assert_called "agent install --server-address=$SERVER_ADDRESS"
}

@test "an explicit INSTALL_METHOD skips detection entirely" {
    export INSTALL_METHOD=standalone
    stub_bin docker
    fake_agent_binary

    run_install

    [ "$status" -eq 0 ]
    assert_output_contains "Installing ShellHub using standalone method"
    refute_called "docker info"
}

@test "an unsupported INSTALL_METHOD is rejected" {
    export INSTALL_METHOD=bogus

    run_install

    [ "$status" -eq 1 ]
    assert_output_contains "Install method not supported"
}

@test "detection reports WSL 2 on Ubuntu" {
    enter_wsl
    stub_bin systemctl 'exit 0'
    stub_bin wslinfo 'echo mirrored'
    fake_agent_binary

    run_install

    [ "$status" -eq 0 ]
    assert_output_contains "Detected WSL environment"
    assert_output_contains "Installing ShellHub using WSL method"
    assert_called "agent install --server-address=$SERVER_ADDRESS"
}

@test "detection aborts on WSL 1" {
    enter_wsl 4.4.0-19041-Microsoft

    run_install

    [ "$status" -eq 1 ]
    assert_output_contains "WSL version 2 is required"
}

@test "detection aborts on the 2016 WSL 1 kernel release" {
    enter_wsl 3.4.0-Microsoft

    run_install

    [ "$status" -eq 1 ]
    assert_output_contains "WSL version 2 is required"
}

@test "detection lets a non-WSL-1 kernel release through once /proc/version names Microsoft" {
    enter_wsl 6.1.0-custom-WSL2
    stub_bin systemctl 'exit 0'
    stub_bin wslinfo 'echo mirrored'
    fake_agent_binary

    run_install

    [ "$status" -eq 0 ]
    assert_output_contains "Installing ShellHub using WSL method"
}

@test "detection aborts on WSL with a distro other than Ubuntu" {
    enter_wsl
    echo 'NAME="Debian GNU/Linux"' > "$OS_RELEASE"

    run_install

    [ "$status" -eq 1 ]
    assert_output_contains "Only Ubuntu is supported in WSL"
}

@test "WSL detection overrides an explicit INSTALL_METHOD, so a chosen method does not survive WSL" {
    export INSTALL_METHOD=standalone
    enter_wsl
    stub_bin systemctl 'exit 0'
    stub_bin wslinfo 'echo mirrored'
    fake_agent_binary

    run_install

    [ "$status" -eq 0 ]
    assert_output_contains "Installing ShellHub using WSL method"
}

@test "the installer reports the enrollment credential before installing anything" {
    with_tenant
    stub_bin docker

    run_install

    [ "$status" -eq 0 ]
    assert_output_contains "Enrollment: tenant $TENANT_ID"
}

@test "the installer discovers the agent version from the server" {
    discover_version v0.27.0-rc.17
    unset AGENT_IMAGE
    stub_runtime docker

    run_install

    [ "$status" -eq 0 ]
    assert_called "docker pull -q docker.io/shellhubio/agent:v0.27.0-rc.17"
}

@test "the installer refuses a server whose certificate it cannot verify" {
    discover_version v0.27.0-rc.17
    stub_bin curl "echo \"curl \$*\" >> \"\$CALLS\"; for a; do case \"\$a\" in --insecure|-k*|-[!-]*k*) cat '$BATS_TEST_TMPDIR/info'; exit 0 ;; esac; done; exit 60"
    unset AGENT_IMAGE
    stub_bin docker

    run_install

    [ "$status" -eq 1 ]
    assert_output_contains "did not report an agent version"
    assert_output_contains "trust the certificate"
    refute_called "docker pull"
}

@test "the installer refuses a server version that points the download at another repository" {
    discover_version "../../../../audit-owner/audit-repo/releases/download/v1"
    export INSTALL_METHOD=standalone

    run_install

    [ "$status" -eq 1 ]
    assert_output_contains "not a ShellHub release tag"
    refute_called "releases/download"
}

@test "the installer refuses a server version that is not a valid image tag" {
    discover_version 'v0.27.0$(id)'
    unset AGENT_IMAGE
    stub_bin docker

    run_install

    [ "$status" -eq 1 ]
    assert_output_contains "not a ShellHub release tag"
    refute_called "docker pull"
}

@test "the installer refuses an agent version that is not a release tag" {
    export INSTALL_METHOD=standalone
    stub_bin curl

    for version in "" "v1.2" "v0.27.0/../../x" $'v0.27.0\nv0.27.1' 'v0.27.0$(id)' "v0.27.0 x"; do
        echo "AGENT_VERSION=$version"
        AGENT_VERSION="$version" run_install

        [ "$status" -eq 1 ]
        if [ -z "$version" ]; then
            assert_output_contains "did not report an agent version"
        else
            assert_output_contains "not a ShellHub release tag"
            refute_output_contains "certificate"
        fi
    done

    refute_called "releases/download"
}

@test "the installer accepts latest as the agent version" {
    export AGENT_VERSION=latest
    unset AGENT_IMAGE
    stub_runtime docker

    run_install

    [ "$status" -eq 0 ]
    assert_called "docker pull -q docker.io/shellhubio/agent:latest"
}

@test "the installer needs no server version when the agent image is given" {
    discover_version ""
    stub_runtime docker

    run_install

    [ "$status" -eq 0 ]
    assert_called "docker run -d"
}

@test "the installer needs no server version when the agent binary is given" {
    discover_version ""
    export INSTALL_METHOD=standalone
    fake_agent_binary

    run_install

    [ "$status" -eq 0 ]
    assert_called "agent install --server-address=$SERVER_ADDRESS"
}

@test "uninstall does not depend on the server reporting a release version" {
    discover_version "not a version"
    stub_bin docker 'echo "docker $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; exit 0'

    run_install uninstall

    [ "$status" -eq 0 ]
    assert_called "docker rm -f shellhub"
}

@test "the installer reports a tenant a previous enrollment left behind" {
    echo "00000000-0000-4000-0000-000000000000" > "$AGENT_KEY.tenant"
    stub_bin docker

    run_install

    [ "$status" -eq 0 ]
    assert_output_contains "Enrollment: tenant 00000000-0000-4000-0000-000000000000 (persisted by a previous enrollment)"
}

@test "the installer prefers a tenant given to this run over the persisted one" {
    with_tenant
    echo "00000000-0000-4000-0000-000000000000" > "$AGENT_KEY.tenant"
    stub_bin docker

    run_install

    [ "$status" -eq 0 ]
    assert_output_contains "Enrollment: tenant $TENANT_ID"
    refute_output_contains "remembered at"
}

@test "the installer fails when the server refuses a device installed with docker" {
    with_tenant
    stub_agent_log <<'LOG'
time="now" level=fatal msg="Failed to authorize the device" error="the server answered 404 Not Found"
LOG
    stub_container_logs docker

    run_install

    [ "$status" -eq 1 ]
    assert_output_contains "The server refused this device"
    assert_called "docker run -d"
}

@test "the installer succeeds when docker leaves the device pending" {
    with_tenant
    stub_agent_log <<'LOG'
time="now" level=info msg="Listening for connections" status=pending tenant_id=t
LOG
    stub_container_logs docker

    run_install

    [ "$status" -eq 0 ]
    assert_output_contains "The device is pending"
}

@test "the installer fails when the login flow fails" {
    stub_runtime docker '[ "$1" = exec ] && exit 2'

    run_install

    [ "$status" -eq 1 ]
    assert_called "docker exec shellhub agent login"
}

@test "the installer fails when docker cannot start the agent container" {
    with_tenant
    stub_runtime docker '[ "$1" = run ] && exit 1'

    run_install

    [ "$status" -eq 1 ]
    assert_output_contains "Failed to start the ShellHub container"
}

@test "the installer fails when podman cannot start the agent container" {
    with_tenant
    use_podman
    stub_runtime podman '[ "$1" = run ] && exit 1'

    run_install

    [ "$status" -eq 1 ]
    assert_output_contains "Failed to start the ShellHub container"
}

@test "the installer fails when the server refuses a device installed with podman" {
    with_tenant
    use_podman
    stub_agent_log <<'LOG'
time="now" level=fatal msg="Failed to authorize the device" error="the server answered 404 Not Found"
LOG
    stub_container_logs podman

    run_install

    [ "$status" -eq 1 ]
    assert_output_contains "The server refused this device"
    assert_called "podman run -d"
}

@test "the installer fails and still cleans up when the server refuses a standalone device" {
    with_tenant
    export INSTALL_METHOD=standalone
    fake_agent_binary
    stub_agent_log <<'LOG'
time="now" level=fatal msg="Failed to authorize the device" error="the server answered 404 Not Found"
LOG
    stub_journal

    run_install

    [ "$status" -eq 1 ]
    assert_output_contains "The server refused this device"
    [ -x "$INSTALL_DIR/shellhub-agent" ]
    [ ! -e "$TMP_DIR" ]
}

@test "the installer ignores what the agent it replaced logged before a standalone reinstall" {
    with_tenant
    export INSTALL_METHOD=standalone
    fake_agent_binary
    local stopped="$BATS_TEST_TMPDIR/old-agent-stopped" polls="$BATS_TEST_TMPDIR/journal-polls"
    stub_bin systemctl "[ \"\$1\" = stop ] && : > '$stopped'; exit 0"
    stub_bin sleep "[ -e '$stopped' ] && : > '$stopped.slept'; exit 0"
    stub_bin date "[ -e '$stopped.slept' ] && echo 1700000001 || echo 1700000000"
    stub_bin journalctl "echo >> '$polls'
case \"\$*\" in
*'--since @1700000000'*) echo 'level=fatal msg=\"Failed to authorize the device\" error=\"the server answered 404 Not Found\"' ;;
esac
[ \$(wc -l < '$polls') -gt 1 ] && echo 'level=info msg=\"Listening for connections\" status=accepted tenant_id=t'
exit 0"

    run_install

    [ "$status" -eq 0 ]
    assert_output_contains "The device is accepted"
    refute_output_contains "The server refused this device"
}

@test "the installer succeeds when a standalone device is accepted" {
    with_tenant
    export INSTALL_METHOD=standalone
    fake_agent_binary
    stub_agent_log <<'LOG'
time="now" level=info msg="Listening for connections" status=accepted tenant_id=t
LOG
    stub_journal

    run_install

    [ "$status" -eq 0 ]
    assert_output_contains "The device is accepted"
}

@test "uninstall dispatches to the detected method" {
    stub_bin docker 'echo "docker $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; exit 0'

    run_install uninstall

    [ "$status" -eq 0 ]
    assert_output_contains "Uninstalling ShellHub using docker method"
    assert_called "docker rm -f shellhub"
}

@test "uninstall does no installer work before removing the agent" {
    stub_bin docker
    stub_bin curl 'echo "curl $*" >> "$CALLS"'
    stub_bin wget 'echo "wget $*" >> "$CALLS"'

    run_install uninstall

    [ "$status" -eq 0 ]
    refute_called "curl"
    refute_called "wget"
    [[ "$output" != *"Detected settings"* ]]
    [[ "$output" != *"ShellHub Agent Installer"* ]]
}

@test "uninstall is refused for install methods that do not support it" {
    export INSTALL_METHOD=snap

    run_install uninstall

    [ "$status" -eq 1 ]
    assert_output_contains "Uninstall is not yet supported for 'snap' install method"
}