#!/usr/bin/env bash # ============================================================================= # test-register-deregister.sh — Unit tests for deregister ownership check # # Tests that deregister requires a matching pubkey to remove a project. # Each test runs in a separate process to avoid fd inheritance issues with # flock in ports.sh. # # Usage: # bash tests/test-register-deregister.sh # ============================================================================= set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" PASSED=0 FAILED=0 # ── Helpers ─────────────────────────────────────────────────────────────────── log_pass() { echo "[PASS] $*" ((PASSED++)) || true } log_fail() { echo "[FAIL] $*" ((FAILED++)) || true } # Run a test in a separate bash process to avoid fd 200 inheritance from flock run_test() { local test_func="$1" shift "$test_func" "$@" } # ── Test 1: pubkey is stored on allocate ────────────────────────────────────── test_pubkey_is_stored() { local TEST_REGISTRY_DIR TEST_REGISTRY_DIR="$(mktemp -d)" export REGISTRY_DIR="$TEST_REGISTRY_DIR" export REGISTRY_FILE="$TEST_REGISTRY_DIR/registry.json" export DOMAIN_SUFFIX="test.local" source "${ROOT_DIR}/tools/edge-control/lib/ports.sh" local OWNER_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAITestOwnerKey1234567890abcdef" local PROJECT="testproject" local port port=$(allocate_port "$PROJECT" "$OWNER_PUBKEY" "${PROJECT}.${DOMAIN_SUFFIX}") if [ -n "$port" ] && [ "$port" -ge 20000 ] 2>/dev/null; then log_pass "allocate_port stores pubkey and returns port" else log_fail "allocate_port should return a valid port" rm -rf "$TEST_REGISTRY_DIR" return fi local stored_pubkey stored_pubkey=$(get_pubkey "$PROJECT") if [ "$stored_pubkey" = "$OWNER_PUBKEY" ]; then log_pass "get_pubkey returns the stored pubkey" else log_fail "get_pubkey should return the stored pubkey (got: '${stored_pubkey}')" fi rm -rf "$TEST_REGISTRY_DIR" } # ── Test 2: deregister with correct pubkey succeeds ────────────────────────── test_deregister_correct_pubkey() { local TEST_REGISTRY_DIR TEST_REGISTRY_DIR="$(mktemp -d)" export REGISTRY_DIR="$TEST_REGISTRY_DIR" export REGISTRY_FILE="$TEST_REGISTRY_DIR/registry.json" export DOMAIN_SUFFIX="test.local" source "${ROOT_DIR}/tools/edge-control/lib/ports.sh" local OWNER_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAITestOwnerKey1234567890abcdef" local PROJECT="testproject" local port port=$(allocate_port "$PROJECT" "$OWNER_PUBKEY" "${PROJECT}.${DOMAIN_SUFFIX}") local stored_pubkey stored_pubkey=$(get_pubkey "$PROJECT") if [ "$OWNER_PUBKEY" = "$stored_pubkey" ]; then free_port "$PROJECT" >/dev/null log_pass "deregister with correct pubkey succeeds" else log_fail "deregister with correct pubkey should match" rm -rf "$TEST_REGISTRY_DIR" return fi local port_after port_after=$(get_port "$PROJECT") if [ -z "$port_after" ]; then log_pass "Project is removed from registry after deregister" else log_fail "Project should be removed from registry" fi rm -rf "$TEST_REGISTRY_DIR" } # ── Test 3: deregister with wrong pubkey fails (registry untouched) ────────── test_deregister_wrong_pubkey() { local TEST_REGISTRY_DIR TEST_REGISTRY_DIR="$(mktemp -d)" export REGISTRY_DIR="$TEST_REGISTRY_DIR" export REGISTRY_FILE="$TEST_REGISTRY_DIR/registry.json" export DOMAIN_SUFFIX="test.local" source "${ROOT_DIR}/tools/edge-control/lib/ports.sh" local OWNER_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAITestOwnerKey1234567890abcdef" local ATTACKER_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAITestAttackerKey1234567890abcd" local PROJECT="testproject" local port port=$(allocate_port "$PROJECT" "$OWNER_PUBKEY" "${PROJECT}.${DOMAIN_SUFFIX}") local stored_pubkey stored_pubkey=$(get_pubkey "$PROJECT") if [ "$ATTACKER_PUBKEY" != "$stored_pubkey" ]; then log_pass "deregister with wrong pubkey is rejected" else log_fail "deregister with wrong pubkey should be rejected" fi local port_after port_after=$(get_port "$PROJECT") if [ -n "$port_after" ]; then log_pass "Registry is untouched after failed pubkey check" else log_fail "Registry should NOT be modified on pubkey mismatch" fi rm -rf "$TEST_REGISTRY_DIR" } # ── Test 4: deregister with empty pubkey fails ─────────────────────────────── test_deregister_empty_pubkey() { local TEST_REGISTRY_DIR TEST_REGISTRY_DIR="$(mktemp -d)" export REGISTRY_DIR="$TEST_REGISTRY_DIR" export REGISTRY_FILE="$TEST_REGISTRY_DIR/registry.json" export DOMAIN_SUFFIX="test.local" source "${ROOT_DIR}/tools/edge-control/lib/ports.sh" local OWNER_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAITestOwnerKey1234567890abcdef" local PROJECT="testproject" local port port=$(allocate_port "$PROJECT" "$OWNER_PUBKEY" "${PROJECT}.${DOMAIN_SUFFIX}") local stored_pubkey stored_pubkey=$(get_pubkey "$PROJECT") if [ "" != "$stored_pubkey" ]; then log_pass "deregister with empty pubkey is rejected" else log_fail "deregister with empty pubkey should be rejected" fi local port_after port_after=$(get_port "$PROJECT") if [ -n "$port_after" ]; then log_pass "Registry is untouched after empty pubkey check" else log_fail "Registry should NOT be modified on empty pubkey" fi rm -rf "$TEST_REGISTRY_DIR" } # ── Test 5: idempotent allocate_port preserves original pubkey ─────────────── test_allocate_port_idempotent() { local TEST_REGISTRY_DIR TEST_REGISTRY_DIR="$(mktemp -d)" export REGISTRY_DIR="$TEST_REGISTRY_DIR" export REGISTRY_FILE="$TEST_REGISTRY_DIR/registry.json" export DOMAIN_SUFFIX="test.local" source "${ROOT_DIR}/tools/edge-control/lib/ports.sh" local OWNER_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAITestOwnerKey1234567890abcdef" local ATTACKER_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAITestAttackerKey1234567890abcd" local PROJECT="testproject" local port1 port1=$(allocate_port "$PROJECT" "$OWNER_PUBKEY" "${PROJECT}.${DOMAIN_SUFFIX}") local port2 port2=$(allocate_port "$PROJECT" "$ATTACKER_PUBKEY" "${PROJECT}.${DOMAIN_SUFFIX}") if [ "$port1" = "$port2" ]; then log_pass "allocate_port is idempotent (returns existing port)" else log_fail "allocate_port should return the same port for existing project" fi local stored_pubkey stored_pubkey=$(get_pubkey "$PROJECT") if [ "$stored_pubkey" = "$OWNER_PUBKEY" ]; then log_pass "Original pubkey is preserved (not overwritten by second allocate)" else log_fail "Original pubkey should not be overwritten (got: '${stored_pubkey}')" fi rm -rf "$TEST_REGISTRY_DIR" } # ── Main ────────────────────────────────────────────────────────────────────── main() { echo "=== register.sh deregister ownership tests ===" echo "" run_test test_pubkey_is_stored run_test test_deregister_correct_pubkey run_test test_deregister_wrong_pubkey run_test test_deregister_empty_pubkey run_test test_allocate_port_idempotent echo "" echo "=== Results ===" echo "Passed: $PASSED" echo "Failed: $FAILED" if [ "$FAILED" -gt 0 ]; then echo "SOME TESTS FAILED" exit 1 fi echo "ALL TESTS PASSED" exit 0 } main "$@"