# API CRUD tests: dashboard content, project/jobset/evaluation/build/channel/builder CRUD, admin endpoints, pagination, search { pkgs, fc-packages, nixosModule, }: pkgs.testers.nixosTest { name = "fc-api-crud"; nodes.machine = import ./common.nix {inherit pkgs fc-packages nixosModule;}; testScript = '' import hashlib import json machine.start() machine.wait_for_unit("postgresql.service") # Ensure PostgreSQL is actually ready to accept connections before fc-server starts machine.wait_until_succeeds("sudo -u fc psql -U fc -d fc -c 'SELECT 1'", timeout=30) machine.wait_for_unit("fc-server.service") # Wait for the server to start listening machine.wait_until_succeeds("curl -sf http://127.0.0.1:3000/health", timeout=30) # ---- Seed an API key for write operations ---- # Token: fc_testkey123 -> SHA-256 hash inserted into api_keys table api_token = "fc_testkey123" api_hash = hashlib.sha256(api_token.encode()).hexdigest() machine.succeed( f"sudo -u fc psql -U fc -d fc -c \"INSERT INTO api_keys (name, key_hash, role) VALUES ('test', '{api_hash}', 'admin')\"" ) auth_header = f"-H 'Authorization: Bearer {api_token}'" # Seed a read-only key ro_token = "fc_readonly_key" ro_hash = hashlib.sha256(ro_token.encode()).hexdigest() machine.succeed( f"sudo -u fc psql -U fc -d fc -c \"INSERT INTO api_keys (name, key_hash, role) VALUES ('readonly', '{ro_hash}', 'read-only')\"" ) ro_header = f"-H 'Authorization: Bearer {ro_token}'" # Create initial project for tests result = machine.succeed( "curl -sf -X POST http://127.0.0.1:3000/api/v1/projects " f"{auth_header} " "-H 'Content-Type: application/json' " "-d '{\"name\": \"test-project\", \"repository_url\": \"https://github.com/test/repo\"}' " "| jq -r .id" ) project_id = result.strip() # ======================================================================== # Phase 4: Dashboard Content & Deep Functional Tests # ======================================================================== # ---- 4A: Dashboard content verification ---- with subtest("Home page contains Dashboard heading"): body = machine.succeed("curl -sf http://127.0.0.1:3000/") assert "Dashboard" in body, "Home page missing 'Dashboard' heading" with subtest("Home page contains stats grid"): body = machine.succeed("curl -sf http://127.0.0.1:3000/") assert "stat-card" in body, "Home page missing stats grid" assert "Completed" in body, "Home page missing 'Completed' stat" with subtest("Home page shows project overview table"): body = machine.succeed("curl -sf http://127.0.0.1:3000/") # We created projects earlier, they should appear assert "test-project" in body, "Home page should list test-project in overview" with subtest("Projects page contains created projects"): body = machine.succeed("curl -sf http://127.0.0.1:3000/projects") assert "test-project" in body, "Projects page should list test-project" with subtest("Projects page returns HTML content type"): ct = machine.succeed( "curl -s -D - -o /dev/null http://127.0.0.1:3000/projects | grep -i content-type" ) assert "text/html" in ct.lower(), f"Expected text/html, got: {ct}" with subtest("Admin page shows system status"): body = machine.succeed("curl -sf http://127.0.0.1:3000/admin") assert "Administration" in body, "Admin page missing heading" assert "System Status" in body, "Admin page missing system status section" assert "Remote Builders" in body, "Admin page missing remote builders section" with subtest("Queue page renders"): body = machine.succeed("curl -sf http://127.0.0.1:3000/queue") assert "Queue" in body or "Pending" in body or "Running" in body, \ "Queue page missing expected content" with subtest("Channels page renders"): body = machine.succeed("curl -sf http://127.0.0.1:3000/channels") # Page should render even if empty assert "Channel" in body or "channel" in body, "Channels page missing expected content" with subtest("Builds page renders with filter params"): body = machine.succeed( "curl -sf 'http://127.0.0.1:3000/builds?status=pending&system=x86_64-linux'" ) assert "Build" in body or "build" in body, "Builds page missing expected content" with subtest("Evaluations page renders"): body = machine.succeed("curl -sf http://127.0.0.1:3000/evaluations") assert "Evaluation" in body or "evaluation" in body, "Evaluations page missing expected content" with subtest("Login page contains form"): body = machine.succeed("curl -sf http://127.0.0.1:3000/login") assert "api_key" in body or "API" in body, "Login page missing API key input" assert "