Skip to content

Testing

Tests in Marchyo are fast evaluation-based checks that verify NixOS configurations evaluate without errors. They don’t build any derivations, so they complete in under a minute.

Terminal window
# Run all tests
nix flake check
# List available tests
nix eval .#checks.x86_64-linux --apply builtins.attrNames

There is no way to run a single test in isolation — nix flake check runs them all.

tests/
default.nix # Entry point — auto-discovers tests/eval/ and merges the results
lib.nix # Shared helpers (assertTest, testNixOS, withTestUser, …)
lib-tests.nix # Library function unit tests
eval/ # Per-feature module tests, auto-discovered (one file per feature)

Verify that NixOS configurations evaluate without errors for various feature combinations:

Test Description
eval-minimal Minimal NixOS modules import
eval-desktop Desktop feature flag
eval-development Development feature flag
eval-all-features All features enabled together
eval-themes Theme configurations
eval-keyboard Keyboard layouts and IME
eval-graphics-* GPU configurations (Intel, AMD, NVIDIA, PRIME)

Unit tests for lib functions using the assertTest helper.

Create (or extend) a file under tests/eval/ — e.g. tests/eval/my-feature.nix. Each file is a function that receives the shared helpers and returns an attrset of named tests; it is auto-discovered, so no registration is needed:

{ helpers, ... }:
let
inherit (helpers) testNixOS withTestUser;
in
{
eval-my-feature = testNixOS "my-feature" (withTestUser {
marchyo.myFeature.enable = true;
});
}

The testNixOS helper evaluates the NixOS config without building derivations. The withTestUser helper merges your config with a minimal bootable config (grub disabled, root filesystem, stateVersion).

Add to tests/lib-tests.nix, using the assertTest helper from tests/lib.nix (assertTest name assertion message — fails at evaluation time when the assertion is false):

test-my-function =
let
result = lib.myFunction "input";
in
assertTest "my-function" (result == "expected") "Expected myFunction to transform input";

The GitHub Actions workflow (.github/workflows/validate.yml) runs four jobs:

  1. Lintnix fmt -- --ci plus a flake.lock / devenv.lock rev-parity check on a single Linux runner. Runs on every push and PR.
  2. Checknix flake check --accept-flake-config on x86_64-linux. Runs on every push and PR.
  3. Check (aarch64-linux) — the same checks on an ARM runner. Runs only on pushes to main and manual dispatch.
  4. Buildnix build .#nixosConfigurations.x86_64.config.system.build.toplevel. Runs only on pushes to main and manual dispatch, after lint and check succeed.

Lint and check run in parallel. Concurrency groups cancel superseded PR runs automatically. Builds use Cachix (jylhis cache).

A separate .github/workflows/site.yml workflow is a build-only gate for the website: changes touching site/** run bun run check and bun run build. Deployment happens outside GitHub Actions — Cloudflare Workers Builds deploys the site to marchyo.org on push to main.