Based on https://nix.dev/tutorials/integration-testing-using-virtual-machines, but with a flake.
BH5XPS7CPEH4MNGTZONRZYABNOUUVYNNQ5YRXMBCHXIXTAAPH3RQC
{
description = "Example for running a VM network test";
inputs.nixpkgs.url = "github:NixOS/nixpkgs";
outputs = { self, nixpkgs }:
let
# Single source of truth for all tutorial constants
database = "postgres";
schema = "api";
table = "todos";
username = "authenticator";
groupname = "authenticator";
password = "mysecretpassword";
webRole = "web_anon";
postgrestPort = 3000;
# NixOS module shared between server and client
sharedModule = {
# Since it's common for CI not to have $DISPLAY available,
# we have to explicitly tell the tests "please don't expect any screen available"
virtualisation.graphics = false;
};
# NixOS tests only support Linux for now:
pkgs = import nixpkgs { system = "x86_64-linux"; };
vm-test = pkgs.nixosTest ({
# Name of the test
name = "vm-test";
nodes = {
server = { config, pkgs, ... }: {
imports = [ sharedModule ];
networking.firewall.allowedTCPPorts = [ postgrestPort ];
services.postgresql = {
enable = true;
initialScript = pkgs.writeText "initialScript.sql" ''
create schema ${schema};
create table ${schema}.${table} (
id serial primary key,
done boolean not null default false,
task text not null,
due timestamptz
);
insert into ${schema}.${table} (task) values ('finish tutorial 0'), ('pat self on back');
create role ${webRole} nologin;
grant usage on schema ${schema} to ${webRole};
grant select on ${schema}.${table} to ${webRole};
create role ${username} inherit login password '${password}';
grant ${webRole} to ${username};
'';
};
users = {
mutableUsers = false;
users = {
# For ease of debugging the VM as the `root` user
root.password = "";
# Create a system user that matches the database user so that we
# can use peer authentication. The tutorial defines a password,
# but it's not necessary.
"${username}" = {
isSystemUser = true;
group = groupname;
};
};
groups."${groupname}" = {};
};
systemd.services.postgrest = {
wantedBy = [ "multi-user.target" ];
after = [ "postgresql.service" ];
script =
let
configuration = pkgs.writeText "tutorial.conf" ''
db-uri = "postgres://${username}:${password}@localhost:${toString config.services.postgresql.port}/${database}"
db-schema = "${schema}"
db-anon-role = "${username}"
'';
in "${pkgs.haskellPackages.postgrest}/bin/postgrest ${configuration}";
serviceConfig.User = username;
};
};
client = {
imports = [ sharedModule ];
};
};
# Disable linting for simpler debugging of the testScript
skipLint = true;
testScript = ''
import json
import sys
start_all()
server.wait_for_open_port(${toString postgrestPort})
expected = [
{"id": 1, "done": False, "task": "finish tutorial 0", "due": None},
{"id": 2, "done": False, "task": "pat self on back", "due": None},
]
actual = json.loads(
client.succeed(
"${pkgs.curl}/bin/curl http://server:${toString postgrestPort}/${table}"
)
)
assert expected == actual, "table query returns expected content"
'';
});
in
{
checks.x86_64-linux.default = vm-test;
packages.x86_64-linux.default = vm-test;
};
}
#!/usr/bin/env bash
# shellcheck disable=SC1010,SC2288
set -Eeuo pipefail
dir="$(realpath --relative-to="${PWD}" "$(dirname "${BASH_SOURCE[0]}")")"
source "${dir}/../bash/nix-container-demo-helper.sh"
h "Integration testing using virtual machines"
, "This demo is based on https://nix.dev/tutorials/integration-testing-using-virtual-machines,"
, "but we adapt it to use flakes instead."
, "One of the most powerful features in the Nix ecosystem is the ability to provide"
, "a set of declarative NixOS configurations and use a simple Python interface"
, "to interact with them using QEMU as the backend."
h "Testing a typical web application backed by PostgreSQL"
, "This tutorial follows PostgREST tutorial, a generic RESTful API for PostgreSQL."
, "If you skim over the official tutorial, you'll notice there's quite a bit of setup in order to test if all the steps work."
, "We are going to set up:"
, " * A VM named 'server' running postgreSQL and postgREST."
, " * A VM named 'client' running HTTP client queries using curl."
, " * A testScript orchestrating testing logic between client and server."
x pygmentize "${dir}/flake.nix"
h "Let's run it!"
n "One of these tests is just like any other derivation when it is built"
, "So if we've run a successful test before, the result will be in the Nix store"
, "and we will not re-run it automatically."
x nix build "${dir}" -L
n "Force a rebuild so we can see what is happening"
x nix build "${dir}" -L --rebuild
h "Flake check"
, "'Checking' a flake involves making sure that all its output packages etc. build,"
, "as well as all explicit checks."
x nix flake check "${dir}" -L