circus/nix/tests/gc-pinning.nix
NotAShelf 9dde82d46f
nix: add tests for channel tarballs and gc pinning
Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Ifb9d95d5206b7b1cf23fa3d5aaf9d0db6a6a6964
2026-02-28 12:18:10 +03:00

165 lines
6.4 KiB
Nix

{
self,
pkgs,
lib,
}: let
inherit (lib.modules) mkForce;
in
pkgs.testers.nixosTest {
name = "fc-gc-pinning";
nodes.machine = {
imports = [
self.nixosModules.fc-ci
../vm-common.nix
];
_module.args.self = self;
services.fc-ci.settings.gc = {
enabled = mkForce true;
gc_roots_dir = "/var/lib/fc/gc-roots";
cleanup_interval = 9999;
max_age_days = 1;
};
};
testScript = ''
import hashlib
import json
machine.start()
machine.wait_for_unit("postgresql.service")
machine.wait_until_succeeds("sudo -u fc psql -U fc -d fc -c 'SELECT 1'", timeout=30)
machine.wait_for_unit("fc-server.service")
machine.wait_until_succeeds("curl -sf http://127.0.0.1:3000/health", timeout=30)
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}'"
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 project
project_id = 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\": \"gc-pin-test\", \"repository_url\": \"https://github.com/test/gc\"}' "
"| jq -r .id"
).strip()
with subtest("Jobset has default keep_nr of 3"):
result = machine.succeed(
f"curl -sf -X POST 'http://127.0.0.1:3000/api/v1/projects/{project_id}/jobsets' "
f"{auth_header} "
"-H 'Content-Type: application/json' "
"-d '{\"name\": \"default\", \"nix_expression\": \"packages\"}' "
"| jq -r .keep_nr"
)
assert result.strip() == "3", f"Expected default keep_nr=3, got {result.strip()}"
with subtest("keep_nr persists in database"):
machine.succeed(
"sudo -u fc psql -U fc -d fc -c "
"\"UPDATE jobsets SET keep_nr = 7 WHERE name = 'default'\""
)
result = machine.succeed(
"sudo -u fc psql -U fc -d fc -tA -c "
"\"SELECT keep_nr FROM jobsets WHERE name = 'default'\""
)
assert result.strip() == "7", f"Expected keep_nr=7, got {result.strip()}"
with subtest("keep_nr visible in active_jobsets view"):
result = machine.succeed(
"sudo -u fc psql -U fc -d fc -tA -c "
"\"SELECT keep_nr FROM active_jobsets WHERE name = 'default' LIMIT 1\""
)
assert result.strip() == "7", f"Expected keep_nr=7 in view, got {result.strip()}"
# Create evaluation + build for keep flag tests
jobset_id = machine.succeed(
"sudo -u fc psql -U fc -d fc -tA -c "
f"\"SELECT id FROM jobsets WHERE project_id = '{project_id}' AND name = 'default'\""
).strip()
eval_id = machine.succeed(
"sudo -u fc psql -U fc -d fc -tA -c "
f"\"INSERT INTO evaluations (jobset_id, commit_hash, status) VALUES ('{jobset_id}', 'abc123', 'completed') RETURNING id\" | head -1"
).strip()
build_id = machine.succeed(
"sudo -u fc psql -U fc -d fc -tA -c "
f"\"INSERT INTO builds (evaluation_id, job_name, drv_path, status, system) "
f"VALUES ('{eval_id}', 'hello', '/nix/store/fake.drv', 'succeeded', 'x86_64-linux') RETURNING id\" | head -1"
).strip()
with subtest("Build starts with keep=false"):
result = machine.succeed(
f"curl -sf http://127.0.0.1:3000/api/v1/builds/{build_id} | jq -r .keep"
)
assert result.strip() == "false", f"Expected keep=false, got {result.strip()}"
with subtest("PUT /builds/id/keep/true sets keep flag"):
code = machine.succeed(
"curl -s -o /dev/null -w '%{http_code}' "
f"-X PUT http://127.0.0.1:3000/api/v1/builds/{build_id}/keep/true "
f"{auth_header}"
)
assert code.strip() == "200", f"Expected 200, got {code.strip()}"
result = machine.succeed(
f"curl -sf http://127.0.0.1:3000/api/v1/builds/{build_id} | jq -r .keep"
)
assert result.strip() == "true", f"Expected keep=true, got {result.strip()}"
with subtest("PUT /builds/id/keep/false clears keep flag"):
machine.succeed(
f"curl -sf -X PUT http://127.0.0.1:3000/api/v1/builds/{build_id}/keep/false "
f"{auth_header}"
)
result = machine.succeed(
f"curl -sf http://127.0.0.1:3000/api/v1/builds/{build_id} | jq -r .keep"
)
assert result.strip() == "false", f"Expected keep=false, got {result.strip()}"
with subtest("Read-only key cannot set keep flag"):
code = machine.succeed(
"curl -s -o /dev/null -w '%{http_code}' "
f"-X PUT http://127.0.0.1:3000/api/v1/builds/{build_id}/keep/true "
f"{ro_header}"
)
assert code.strip() == "403", f"Expected 403, got {code.strip()}"
with subtest("keep=true visible in API response"):
machine.succeed(
f"curl -sf -X PUT http://127.0.0.1:3000/api/v1/builds/{build_id}/keep/true "
f"{auth_header}"
)
result = machine.succeed(
f"curl -sf http://127.0.0.1:3000/api/v1/builds/{build_id}"
)
build_json = json.loads(result)
assert build_json["keep"] is True, f"Expected keep=true in JSON, got {build_json.get('keep')}"
with subtest("Nonexistent build returns 404 for keep"):
code = machine.succeed(
"curl -s -o /dev/null -w '%{http_code}' "
"-X PUT http://127.0.0.1:3000/api/v1/builds/00000000-0000-0000-0000-000000000000/keep/true "
f"{auth_header}"
)
assert code.strip() == "404", f"Expected 404, got {code.strip()}"
# Cleanup
machine.succeed(
f"curl -sf -X DELETE http://127.0.0.1:3000/api/v1/projects/{project_id} {auth_header}"
)
'';
}