Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ifb9d95d5206b7b1cf23fa3d5aaf9d0db6a6a6964
158 lines
6.4 KiB
Nix
158 lines
6.4 KiB
Nix
{
|
|
pkgs,
|
|
self,
|
|
}:
|
|
pkgs.testers.nixosTest {
|
|
name = "fc-channel-tarball";
|
|
|
|
nodes.machine = {
|
|
imports = [
|
|
self.nixosModules.fc-ci
|
|
../vm-common.nix
|
|
];
|
|
_module.args.self = self;
|
|
};
|
|
|
|
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}'"
|
|
|
|
# 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\": \"tarball-test\", \"repository_url\": \"https://github.com/test/tarball\"}' "
|
|
"| jq -r .id"
|
|
).strip()
|
|
|
|
# Create jobset
|
|
jobset_id = 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\": \"packages\", \"nix_expression\": \"packages\"}' "
|
|
"| jq -r .id"
|
|
).strip()
|
|
|
|
# Create evaluation via SQL
|
|
eval_id = machine.succeed(
|
|
"sudo -u fc psql -U fc -d fc -tA -c "
|
|
"\"INSERT INTO evaluations (jobset_id, commit_hash, status) "
|
|
f"VALUES ('{jobset_id}', 'abc123', 'completed') RETURNING id\" | head -1"
|
|
).strip()
|
|
|
|
# Create succeeded builds with output paths
|
|
machine.succeed(
|
|
"sudo -u fc psql -U fc -d fc -c "
|
|
"\"INSERT INTO builds (evaluation_id, job_name, drv_path, status, system, build_output_path) "
|
|
f"VALUES ('{eval_id}', 'hello', '/nix/store/fake-hello.drv', 'succeeded', 'x86_64-linux', '/nix/store/aaaa-hello-1.0')\""
|
|
)
|
|
machine.succeed(
|
|
"sudo -u fc psql -U fc -d fc -c "
|
|
"\"INSERT INTO builds (evaluation_id, job_name, drv_path, status, system, build_output_path) "
|
|
f"VALUES ('{eval_id}', 'world', '/nix/store/fake-world.drv', 'succeeded', 'x86_64-linux', '/nix/store/bbbb-world-2.0')\""
|
|
)
|
|
# A failed build should not appear in the tarball
|
|
machine.succeed(
|
|
"sudo -u fc psql -U fc -d fc -c "
|
|
"\"INSERT INTO builds (evaluation_id, job_name, drv_path, status, system) "
|
|
f"VALUES ('{eval_id}', 'broken', '/nix/store/fake-broken.drv', 'failed', 'x86_64-linux')\""
|
|
)
|
|
|
|
# Create channel
|
|
channel_id = machine.succeed(
|
|
"curl -sf -X POST http://127.0.0.1:3000/api/v1/channels "
|
|
f"{auth_header} "
|
|
"-H 'Content-Type: application/json' "
|
|
f"-d '{{\"project_id\": \"{project_id}\", \"name\": \"nixos-unstable\", \"jobset_id\": \"{jobset_id}\"}}' "
|
|
"| jq -r .id"
|
|
).strip()
|
|
|
|
with subtest("Channel without evaluation returns 404 for tarball"):
|
|
# The channel auto-promotes on create if eval exists, so check if it already has one
|
|
ch = json.loads(machine.succeed(
|
|
f"curl -sf http://127.0.0.1:3000/api/v1/channels/{channel_id}"
|
|
))
|
|
if ch["current_evaluation_id"] is None:
|
|
code = machine.succeed(
|
|
f"curl -s -o /dev/null -w '%{{http_code}}' "
|
|
f"http://127.0.0.1:3000/api/v1/channels/{channel_id}/nixexprs.tar.xz"
|
|
)
|
|
assert code.strip() == "404", f"Expected 404 for no-eval channel, got {code.strip()}"
|
|
|
|
# Promote evaluation to channel
|
|
machine.succeed(
|
|
f"curl -sf -X POST http://127.0.0.1:3000/api/v1/channels/{channel_id}/promote/{eval_id} "
|
|
f"{auth_header}"
|
|
)
|
|
|
|
with subtest("Channel has current_evaluation_id after promotion"):
|
|
result = machine.succeed(
|
|
f"curl -sf http://127.0.0.1:3000/api/v1/channels/{channel_id}"
|
|
)
|
|
ch = json.loads(result)
|
|
assert ch["current_evaluation_id"] == eval_id, \
|
|
f"Expected current_evaluation_id={eval_id}, got {ch['current_evaluation_id']}"
|
|
|
|
with subtest("nixexprs.tar.xz returns 200 with correct content-type"):
|
|
headers = machine.succeed(
|
|
"curl -sf -D - -o /tmp/nixexprs.tar.xz "
|
|
f"http://127.0.0.1:3000/api/v1/channels/{channel_id}/nixexprs.tar.xz"
|
|
)
|
|
assert "application/x-xz" in headers, \
|
|
f"Expected application/x-xz content-type, got: {headers}"
|
|
|
|
with subtest("Tarball is valid xz and contains default.nix"):
|
|
listing = machine.succeed("xz -d < /tmp/nixexprs.tar.xz | tar tf -")
|
|
assert "default.nix" in listing, \
|
|
f"Expected default.nix in tarball, got: {listing}"
|
|
|
|
with subtest("default.nix contains succeeded builds"):
|
|
machine.succeed("xz -d < /tmp/nixexprs.tar.xz | tar xf - -C /tmp")
|
|
content = machine.succeed("cat /tmp/default.nix")
|
|
assert "hello" in content, "Expected 'hello' job in default.nix"
|
|
assert "world" in content, "Expected 'world' job in default.nix"
|
|
assert "/nix/store/aaaa-hello-1.0" in content, \
|
|
"Expected hello output path in default.nix"
|
|
assert "/nix/store/bbbb-world-2.0" in content, \
|
|
"Expected world output path in default.nix"
|
|
|
|
with subtest("default.nix excludes failed builds"):
|
|
content = machine.succeed("cat /tmp/default.nix")
|
|
assert "broken" not in content, \
|
|
"Failed build 'broken' should not appear in default.nix"
|
|
|
|
with subtest("default.nix has mkFakeDerivation structure"):
|
|
content = machine.succeed("cat /tmp/default.nix")
|
|
assert "mkFakeDerivation" in content, \
|
|
"Expected mkFakeDerivation helper in default.nix"
|
|
assert "builtin:fetchurl" in content, \
|
|
"Expected builtin:fetchurl in mkFakeDerivation"
|
|
|
|
with subtest("Nonexistent channel returns 404 for tarball"):
|
|
code = machine.succeed(
|
|
"curl -s -o /dev/null -w '%{http_code}' "
|
|
"http://127.0.0.1:3000/api/v1/channels/00000000-0000-0000-0000-000000000000/nixexprs.tar.xz"
|
|
)
|
|
assert code.strip() == "404", f"Expected 404 for nonexistent channel, got {code.strip()}"
|
|
|
|
# Cleanup
|
|
machine.succeed(
|
|
f"curl -sf -X DELETE http://127.0.0.1:3000/api/v1/projects/{project_id} {auth_header}"
|
|
)
|
|
'';
|
|
}
|