Skip to content

Template: CI Tests

The CI template is Den’s comprehensive test suite. It tests every feature using nix-unit. This is the best learning resource for understanding exactly how Den behaves.

Every .nix file under modules/ is auto-imported by import-tree — the flake just hands ./modules to it. To add a test, drop a new file in; there is no central list to edit.

Test files are grouped into four buckets by what surface they exercise:

flake.nix # evalModules + (import-tree ./modules)
modules/
new-test.nix # copy-me skeleton for a new test
test-support/
eval-den.nix # wires denTest + the den fixture into the flake
public-api/ # tests using ONLY the documented public surface
internal-api/ # tests poking pipeline internals (not for users)
deprecated/ # tests pinning legacy/compat behavior
deadbugs/ # one regression test per fixed bug
non-dendritic/ # plain files consumed by import-tree tests
provider/ # a separate flake exporting a namespace

When you want to learn the recommended way to do something, read public-api/. The other buckets exist to keep the suite honest: internal-api/ and deprecated/ are not patterns to copy.

The tables below point at representative files in public-api/. Each file declares one flake.tests.<suite> and holds several test-* cases.

Test FileSuiteWhat It Tests
conditional-config.nixconditional-configConditional imports using host/user attributes
default-includes.nixdefault-includesden.default applying to all hosts/users
host-options.nixhost-optionsCustom host attributes, hostName, aspect names
top-level-parametric.nixtop-level-parametricContext-aware top-level aspects
auto-parametric.nixauto-parametricParametric aspects auto-dispatched by the pipeline
Test FileSuiteWhat It Tests
user-host-mutual-config.nixuser-host-mutual-configHost→user and user→host config flow
mutual-provider.nixmutual-providerMutual provider mechanism
policy-provide.nixpolicy-providePolicy-driven provide effects
Test FileSuiteWhat It Tests
policies.nixpoliciesPolicy declaration and activation
policy-excludes.nixpolicy-excludesPolicies excluding includes
route.nixrouteRoute effects
Test FileSuiteWhat It Tests
define-user.nixdefine-userUser definition across contexts
primary-user.nixprimary-userPrimary user groups
user-shell.nixuser-shellShell configuration
unfree.nixunfreeUnfree package predicates
tty-autologin.nixtty-autologinTTY autologin service
vm-autologin.nixvm-autologinVM autologin service
import-tree.nix(import-tree)Auto-importing class dirs
flake-parts.nixflake-partsinputs' and self' providers
Test FileSuiteWhat It Tests
angle-brackets.nixangle-bracketsAll bracket resolution paths
namespaces.nixnamespacesLocal, remote, merged namespaces
forward-from-custom-class.nixforward-custom-classCustom class forwarding
homes.nixstandalone-homesStandalone Home-Manager configs
schema-base-modules.nixschema-base-modulesden.schema.{host,user,home,conf}

Every fixed bug gets a pinned regression test in deadbugs/, named after its issue. A couple of examples:

Test FileWhat It Tests
deadbugs/static-include-dup-package.nixDeduplication for packages/lists
deadbugs/external-namespace-deep-aspect.nixDeep aspect access from external flakes

The provider/ subdirectory is a separate flake that exports a namespace called provider. It is consumed by namespace tests to verify cross-flake aspect consumption. The exporting flake imports den.flakeModule and registers the namespace as an output with den.namespace "provider" true:

provider/modules/den.nix
{ inputs, ... }:
{
imports = [
inputs.den.flakeModule
(inputs.den.namespace "provider" true)
];
provider.tools.provides.dev.provides.editors = {
nixos.programs.vim.enable = true;
};
}

The second argument to den.namespace (true) marks the namespace as a flake output so other flakes can import it.

From the Den root against your local checkout:

Terminal window
just ci

You can run a single suite (or a sub-path below flake.tests) by passing its name:

Terminal window
# Any attr-path below flake.tests works, e.g. a whole suite:
just ci wsl-class
# …or a single case within it:
just ci wsl-class.test-wsl-forwards

The default just ci is fast but cannot show error diffs and skips tests that assert via nix-unit expectedError. Use just ci-deep for full nix-unit output with diffs:

Terminal window
just ci-deep wsl-class

Copy modules/new-test.nix as a starting point, rename the suite and case, then assert with expr / expected:

{ denTest, ... }:
{
flake.tests.new = {
test-something = denTest (
{ den, igloo, ... }:
{
den.hosts.x86_64-linux.igloo.users.tux = { };
den.aspects.base =
{ host, ... }:
{
nixos.networking.hostName = host.hostName;
};
den.aspects.igloo.includes = [ den.aspects.base ];
expr = igloo.networking.hostName;
expected = "igloo";
}
);
};
}

denTest provides handy fixtures as function arguments: den (the live den config), igloo (nixosConfigurations.igloo.config), tuxHm (igloo.home-manager.users.tux), plus iceberg, apple, pinguHm, and a trace helper. Drop your new file under modules/ and git add it before running nix — import-tree only sees tracked files.

Contribute Community Sponsor