nixosTests.postgresql: move all postgresql related nixosTests into one folder

This makes it possible to run all those tests at once by building
nixosTests.postgresql and allow a simple entry to ci/OWNERS for all
tests.
This commit is contained in:
Wolfgang Walther 2024-11-02 18:58:52 +01:00
parent db2d6a00ab
commit 9035573855
No known key found for this signature in database
GPG key ID: B39893FA5F65CAE1
23 changed files with 166 additions and 154 deletions

View file

@ -0,0 +1,110 @@
{ pkgs
, makeTest
}:
let
inherit (pkgs) lib;
makeAnonymizerTest = postgresqlPackage:
makeTest {
name = "postgresql_anonymizer-${postgresqlPackage.name}";
meta.maintainers = lib.teams.flyingcircus.members;
nodes.machine = { pkgs, ... }: {
environment.systemPackages = [ pkgs.pg-dump-anon ];
services.postgresql = {
enable = true;
package = postgresqlPackage;
extraPlugins = ps: [ ps.anonymizer ];
settings.shared_preload_libraries = [ "anon" ];
};
};
testScript = ''
start_all()
machine.wait_for_unit("multi-user.target")
machine.wait_for_unit("postgresql.service")
with subtest("Setup"):
machine.succeed("sudo -u postgres psql --command 'create database demo'")
machine.succeed(
"sudo -u postgres psql -d demo -f ${pkgs.writeText "init.sql" ''
create extension anon cascade;
select anon.init();
create table player(id serial, name text, points int);
insert into player(id,name,points) values (1,'Foo', 23);
insert into player(id,name,points) values (2,'Bar',42);
security label for anon on column player.name is 'MASKED WITH FUNCTION anon.fake_last_name();';
security label for anon on column player.points is 'MASKED WITH VALUE NULL';
''}"
)
def get_player_table_contents():
return [
x.split(',') for x in machine.succeed("sudo -u postgres psql -d demo --csv --command 'select * from player'").splitlines()[1:]
]
def check_anonymized_row(row, id, original_name):
assert row[0] == id, f"Expected first row to have ID {id}, but got {row[0]}"
assert row[1] != original_name, f"Expected first row to have a name other than {original_name}"
assert not bool(row[2]), "Expected points to be NULL in first row"
def find_xsv_in_dump(dump, sep=','):
"""
Expecting to find a CSV (for pg_dump_anon) or TSV (for pg_dump) structure, looking like
COPY public.player ...
1,Shields,
2,Salazar,
\.
in the given dump (the commas are tabs in case of pg_dump).
Extract the CSV lines and split by `sep`.
"""
try:
from itertools import dropwhile, takewhile
return [x.split(sep) for x in list(takewhile(
lambda x: x != "\\.",
dropwhile(
lambda x: not x.startswith("COPY public.player"),
dump.splitlines()
)
))[1:]]
except:
print(f"Dump to process: {dump}")
raise
def check_original_data(output):
assert output[0] == ['1','Foo','23'], f"Expected first row from player table to be 1,Foo,23; got {output[0]}"
assert output[1] == ['2','Bar','42'], f"Expected first row from player table to be 2,Bar,42; got {output[1]}"
def check_anonymized_rows(output):
check_anonymized_row(output[0], '1', 'Foo')
check_anonymized_row(output[1], '2', 'Bar')
with subtest("Check initial state"):
check_original_data(get_player_table_contents())
with subtest("Anonymous dumps"):
check_original_data(find_xsv_in_dump(
machine.succeed("sudo -u postgres pg_dump demo"),
sep='\t'
))
check_anonymized_rows(find_xsv_in_dump(
machine.succeed("sudo -u postgres pg_dump_anon -U postgres -h /run/postgresql -d demo"),
sep=','
))
with subtest("Anonymize"):
machine.succeed("sudo -u postgres psql -d demo --command 'select anon.anonymize_database();'")
check_anonymized_rows(get_player_table_contents())
'';
};
in
pkgs.lib.recurseIntoAttrs (
pkgs.lib.concatMapAttrs (n: p: { ${n} = makeAnonymizerTest p; }) pkgs.postgresqlVersions
// {
passthru.override = p: makeAnonymizerTest p;
}
)

View file

@ -0,0 +1,26 @@
{
system ? builtins.currentSystem,
config ? { },
pkgs ? import ../../.. { inherit system config; },
}:
with import ../../lib/testing-python.nix { inherit system pkgs; };
let
importWithArgs = path: import path { inherit pkgs makeTest; };
in
{
# postgresql
postgresql = importWithArgs ./postgresql.nix;
postgresql-jit = importWithArgs ./postgresql-jit.nix;
postgresql-wal-receiver = importWithArgs ./postgresql-wal-receiver.nix;
postgresql-tls-client-cert = importWithArgs ./postgresql-tls-client-cert.nix;
# extensions
anonymizer = importWithArgs ./anonymizer.nix;
pgjwt = importWithArgs ./pgjwt.nix;
pgvecto-rs = importWithArgs ./pgvecto-rs.nix;
timescaledb = importWithArgs ./timescaledb.nix;
tsja = importWithArgs ./tsja.nix;
wal2json = importWithArgs ./wal2json.nix;
}

View file

@ -0,0 +1,46 @@
{ pkgs
, makeTest
}:
let
inherit (pkgs) lib;
makePgjwtTest = postgresqlPackage:
makeTest {
name = "pgjwt-${postgresqlPackage.name}";
meta = with lib.maintainers; {
maintainers = [ spinus willibutz ];
};
nodes = {
master = { ... }:
{
services.postgresql = {
enable = true;
package = postgresqlPackage;
extraPlugins = ps: with ps; [ pgjwt pgtap ];
};
};
};
testScript = { nodes, ... }:
let
sqlSU = "${nodes.master.services.postgresql.superUser}";
pgProve = "${pkgs.perlPackages.TAPParserSourceHandlerpgTAP}";
inherit (nodes.master.services.postgresql.package.pkgs) pgjwt;
in
''
start_all()
master.wait_for_unit("postgresql")
master.succeed(
"${pkgs.sudo}/bin/sudo -u ${sqlSU} ${pgProve}/bin/pg_prove -d postgres -v -f ${pgjwt.src}/test.sql"
)
'';
};
in
lib.recurseIntoAttrs (
lib.concatMapAttrs (n: p: { ${n} = makePgjwtTest p; }) pkgs.postgresqlVersions
// {
passthru.override = p: makePgjwtTest p;
}
)

View file

@ -0,0 +1,74 @@
{ pkgs
, makeTest
}:
with pkgs.lib;
let
# Test cases from https://docs.pgvecto.rs/use-cases/hybrid-search.html
test-sql = pkgs.writeText "postgresql-test" ''
CREATE EXTENSION vectors;
CREATE TABLE items (
id bigserial PRIMARY KEY,
content text NOT NULL,
embedding vectors.vector(3) NOT NULL -- 3 dimensions
);
INSERT INTO items (content, embedding) VALUES
('a fat cat sat on a mat and ate a fat rat', '[1, 2, 3]'),
('a fat dog sat on a mat and ate a fat rat', '[4, 5, 6]'),
('a thin cat sat on a mat and ate a thin rat', '[7, 8, 9]'),
('a thin dog sat on a mat and ate a thin rat', '[10, 11, 12]');
'';
makePgVectorsTest = postgresqlPackage: makeTest {
name = "pgvecto-rs-${postgresqlPackage.name}";
meta = with pkgs.lib.maintainers; {
maintainers = [ diogotcorreia ];
};
nodes.machine = { ... }:
{
services.postgresql = {
enable = true;
package = postgresqlPackage;
extraPlugins = ps: with ps; [
pgvecto-rs
];
settings.shared_preload_libraries = "vectors";
};
};
testScript = { nodes, ... }:
let
inherit (nodes.machine.services.postgresql.package.pkgs) pgvecto-rs;
in
''
def check_count(statement, lines):
return 'test $(sudo -u postgres psql postgres -tAc "{}"|wc -l) -eq {}'.format(
statement, lines
)
machine.start()
machine.wait_for_unit("postgresql")
with subtest("Postgresql with extension vectors is available just after unit start"):
machine.succeed(check_count("SELECT * FROM pg_available_extensions WHERE name = 'vectors' AND default_version = '${pgvecto-rs.version}';", 1))
machine.succeed("sudo -u postgres psql -f ${test-sql}")
machine.succeed(check_count("SELECT content, embedding FROM items WHERE to_tsvector('english', content) @@ 'cat & rat'::tsquery;", 2))
machine.shutdown()
'';
};
in
recurseIntoAttrs (
concatMapAttrs (n: p: { ${n} = makePgVectorsTest p; }) (filterAttrs (n: p: !p.pkgs.pgvecto-rs.meta.broken) pkgs.postgresqlVersions)
// {
passthru.override = p: makePgVectorsTest p;
}
)

View file

@ -0,0 +1,49 @@
{ pkgs
, makeTest
}:
let
inherit (pkgs) lib;
mkJitTest = package: makeTest {
name = "postgresql-jit-${package.name}";
meta.maintainers = with lib.maintainers; [ ma27 ];
nodes.machine = { pkgs, lib, ... }: {
services.postgresql = {
inherit package;
enable = true;
enableJIT = true;
initialScript = pkgs.writeText "init.sql" ''
create table demo (id int);
insert into demo (id) select generate_series(1, 5);
'';
};
};
testScript = ''
machine.start()
machine.wait_for_unit("postgresql.service")
with subtest("JIT is enabled"):
machine.succeed("sudo -u postgres psql <<<'show jit;' | grep 'on'")
with subtest("Test JIT works fine"):
output = machine.succeed(
"cat ${pkgs.writeText "test.sql" ''
set jit_above_cost = 1;
EXPLAIN ANALYZE SELECT CONCAT('jit result = ', SUM(id)) FROM demo;
SELECT CONCAT('jit result = ', SUM(id)) from demo;
''} | sudo -u postgres psql"
)
assert "JIT:" in output
assert "jit result = 15" in output
machine.shutdown()
'';
};
in
lib.recurseIntoAttrs (
lib.concatMapAttrs (n: p: { ${n} = mkJitTest p; }) (lib.filterAttrs (n: _: lib.hasSuffix "_jit" n) pkgs.postgresqlVersions)
// {
passthru.override = p: mkJitTest p;
}
)

View file

@ -0,0 +1,130 @@
{ pkgs
, makeTest
}:
let
lib = pkgs.lib;
makePostgresqlTlsClientCertTest = pkg:
let
runWithOpenSSL = file: cmd: pkgs.runCommand file
{
buildInputs = [ pkgs.openssl ];
}
cmd;
caKey = runWithOpenSSL "ca.key" "openssl ecparam -name prime256v1 -genkey -noout -out $out";
caCert = runWithOpenSSL
"ca.crt"
''
openssl req -new -x509 -sha256 -key ${caKey} -out $out -subj "/CN=test.example" -days 36500
'';
serverKey =
runWithOpenSSL "server.key" "openssl ecparam -name prime256v1 -genkey -noout -out $out";
serverKeyPath = "/var/lib/postgresql";
serverCert =
runWithOpenSSL "server.crt" ''
openssl req -new -sha256 -key ${serverKey} -out server.csr -subj "/CN=db.test.example"
openssl x509 -req -in server.csr -CA ${caCert} -CAkey ${caKey} \
-CAcreateserial -out $out -days 36500 -sha256
'';
clientKey =
runWithOpenSSL "client.key" "openssl ecparam -name prime256v1 -genkey -noout -out $out";
clientCert =
runWithOpenSSL "client.crt" ''
openssl req -new -sha256 -key ${clientKey} -out client.csr -subj "/CN=test"
openssl x509 -req -in client.csr -CA ${caCert} -CAkey ${caKey} \
-CAcreateserial -out $out -days 36500 -sha256
'';
clientKeyPath = "/root";
in
makeTest {
name = "postgresql-tls-client-cert-${pkg.name}";
meta.maintainers = with lib.maintainers; [ erictapen ];
nodes.server = { ... }: {
system.activationScripts = {
keyPlacement.text = ''
mkdir -p '${serverKeyPath}'
cp '${serverKey}' '${serverKeyPath}/server.key'
chown postgres:postgres '${serverKeyPath}/server.key'
chmod 600 '${serverKeyPath}/server.key'
'';
};
services.postgresql = {
package = pkg;
enable = true;
enableTCPIP = true;
ensureUsers = [
{
name = "test";
ensureDBOwnership = true;
}
];
ensureDatabases = [ "test" ];
settings = {
ssl = "on";
ssl_ca_file = toString caCert;
ssl_cert_file = toString serverCert;
ssl_key_file = "${serverKeyPath}/server.key";
};
authentication = ''
hostssl test test ::/0 cert clientcert=verify-full
'';
};
networking = {
interfaces.eth1 = {
ipv6.addresses = [
{ address = "fc00::1"; prefixLength = 120; }
];
};
firewall.allowedTCPPorts = [ 5432 ];
};
};
nodes.client = { ... }: {
system.activationScripts = {
keyPlacement.text = ''
mkdir -p '${clientKeyPath}'
cp '${clientKey}' '${clientKeyPath}/client.key'
chown root:root '${clientKeyPath}/client.key'
chmod 600 '${clientKeyPath}/client.key'
'';
};
environment = {
variables = {
PGHOST = "db.test.example";
PGPORT = "5432";
PGDATABASE = "test";
PGUSER = "test";
PGSSLMODE = "verify-full";
PGSSLCERT = clientCert;
PGSSLKEY = "${clientKeyPath}/client.key";
PGSSLROOTCERT = caCert;
};
systemPackages = [ pkg ];
};
networking = {
interfaces.eth1 = {
ipv6.addresses = [
{ address = "fc00::2"; prefixLength = 120; }
];
};
hosts = { "fc00::1" = [ "db.test.example" ]; };
};
};
testScript = ''
server.wait_for_unit("multi-user.target")
client.wait_for_unit("multi-user.target")
client.succeed("psql -c \"SELECT 1;\"")
'';
};
in
lib.recurseIntoAttrs (
lib.concatMapAttrs (n: p: { ${n} = makePostgresqlTlsClientCertTest p; }) pkgs.postgresqlVersions
// {
passthru.override = p: makePostgresqlTlsClientCertTest p;
}
)

View file

@ -0,0 +1,112 @@
{ pkgs
, makeTest
}:
let
lib = pkgs.lib;
makePostgresqlWalReceiverTest = pkg:
let
postgresqlDataDir = "/var/lib/postgresql/${pkg.psqlSchema}";
replicationUser = "wal_receiver_user";
replicationSlot = "wal_receiver_slot";
replicationConn = "postgresql://${replicationUser}@localhost";
baseBackupDir = "/var/cache/wals/pg_basebackup";
walBackupDir = "/var/cache/wals/pg_wal";
recoveryFile = pkgs.writeTextDir "recovery.signal" "";
in makeTest {
name = "postgresql-wal-receiver-${pkg.name}";
meta.maintainers = with lib.maintainers; [ pacien ];
nodes.machine = { ... }: {
systemd.tmpfiles.rules = [
"d /var/cache/wals 0750 postgres postgres - -"
];
services.postgresql = {
package = pkg;
enable = true;
settings = {
max_replication_slots = 10;
max_wal_senders = 10;
recovery_end_command = "touch recovery.done";
restore_command = "cp ${walBackupDir}/%f %p";
wal_level = "archive"; # alias for replica on pg >= 9.6
};
authentication = ''
host replication ${replicationUser} all trust
'';
initialScript = pkgs.writeText "init.sql" ''
create user ${replicationUser} replication;
select * from pg_create_physical_replication_slot('${replicationSlot}');
'';
};
services.postgresqlWalReceiver.receivers.main = {
postgresqlPackage = pkg;
connection = replicationConn;
slot = replicationSlot;
directory = walBackupDir;
};
# This is only to speedup test, it isn't time racing. Service is set to autorestart always,
# default 60sec is fine for real system, but is too much for a test
systemd.services.postgresql-wal-receiver-main.serviceConfig.RestartSec = lib.mkForce 5;
systemd.services.postgresql.serviceConfig.ReadWritePaths = [ "/var/cache/wals" ];
};
testScript = ''
# make an initial base backup
machine.wait_for_unit("postgresql")
machine.wait_for_unit("postgresql-wal-receiver-main")
# WAL receiver healthchecks PG every 5 seconds, so let's be sure they have connected each other
# required only for 9.4
machine.sleep(5)
machine.succeed(
"${pkg}/bin/pg_basebackup --dbname=${replicationConn} --pgdata=${baseBackupDir}"
)
# create a dummy table with 100 records
machine.succeed(
"sudo -u postgres psql --command='create table dummy as select * from generate_series(1, 100) as val;'"
)
# stop postgres and destroy data
machine.systemctl("stop postgresql")
machine.systemctl("stop postgresql-wal-receiver-main")
machine.succeed("rm -r ${postgresqlDataDir}/{base,global,pg_*}")
# restore the base backup
machine.succeed(
"cp -r ${baseBackupDir}/* ${postgresqlDataDir} && chown postgres:postgres -R ${postgresqlDataDir}"
)
# prepare WAL and recovery
machine.succeed("chmod a+rX -R ${walBackupDir}")
machine.execute(
"for part in ${walBackupDir}/*.partial; do mv $part ''${part%%.*}; done"
) # make use of partial segments too
machine.succeed(
"cp ${recoveryFile}/* ${postgresqlDataDir}/ && chmod 666 ${postgresqlDataDir}/recovery*"
)
# replay WAL
machine.systemctl("start postgresql")
machine.wait_for_file("${postgresqlDataDir}/recovery.done")
machine.systemctl("restart postgresql")
machine.wait_for_unit("postgresql")
# check that our records have been restored
machine.succeed(
"test $(sudo -u postgres psql --pset='pager=off' --tuples-only --command='select count(distinct val) from dummy;') -eq 100"
)
'';
};
in
lib.recurseIntoAttrs (
lib.concatMapAttrs (n: p: { ${n} = makePostgresqlWalReceiverTest p; }) pkgs.postgresqlVersions
// {
passthru.override = p: makePostgresqlWalReceiverTest p;
}
)

View file

@ -0,0 +1,232 @@
{ pkgs
, makeTest
}:
with pkgs.lib;
let
testsForPackage = package:
recurseIntoAttrs {
postgresql = make-postgresql-test package false;
postgresql-backup-all = make-postgresql-test package true;
postgresql-clauses = mk-ensure-clauses-test package;
};
test-sql = pkgs.writeText "postgresql-test" ''
CREATE EXTENSION pgcrypto; -- just to check if lib loading works
CREATE TABLE sth (
id int
);
INSERT INTO sth (id) VALUES (1);
INSERT INTO sth (id) VALUES (1);
INSERT INTO sth (id) VALUES (1);
INSERT INTO sth (id) VALUES (1);
INSERT INTO sth (id) VALUES (1);
CREATE TABLE xmltest ( doc xml );
INSERT INTO xmltest (doc) VALUES ('<test>ok</test>'); -- check if libxml2 enabled
'';
make-postgresql-test = postgresql-package: backup-all: makeTest {
name = "postgresql${optionalString backup-all "-backup-all"}-${postgresql-package.name}";
meta = with pkgs.lib.maintainers; {
maintainers = [ zagy ];
};
nodes.machine = {...}:
{
services.postgresql = {
enable = true;
package = postgresql-package;
};
services.postgresqlBackup = {
enable = true;
databases = optional (!backup-all) "postgres";
};
};
testScript = let
backupName = if backup-all then "all" else "postgres";
backupService = if backup-all then "postgresqlBackup" else "postgresqlBackup-postgres";
backupFileBase = "/var/backup/postgresql/${backupName}";
in ''
def check_count(statement, lines):
return 'test $(sudo -u postgres psql postgres -tAc "{}"|wc -l) -eq {}'.format(
statement, lines
)
machine.start()
machine.wait_for_unit("postgresql")
with subtest("Postgresql is available just after unit start"):
machine.succeed(
"cat ${test-sql} | sudo -u postgres psql"
)
with subtest("Postgresql survives restart (bug #1735)"):
machine.shutdown()
import time
time.sleep(2)
machine.start()
machine.wait_for_unit("postgresql")
machine.fail(check_count("SELECT * FROM sth;", 3))
machine.succeed(check_count("SELECT * FROM sth;", 5))
machine.fail(check_count("SELECT * FROM sth;", 4))
machine.succeed(check_count("SELECT xpath('/test/text()', doc) FROM xmltest;", 1))
with subtest("Backup service works"):
machine.succeed(
"systemctl start ${backupService}.service",
"zcat ${backupFileBase}.sql.gz | grep '<test>ok</test>'",
"ls -hal /var/backup/postgresql/ >/dev/console",
"stat -c '%a' ${backupFileBase}.sql.gz | grep 600",
)
with subtest("Backup service removes prev files"):
machine.succeed(
# Create dummy prev files.
"touch ${backupFileBase}.prev.sql{,.gz,.zstd}",
"chown postgres:postgres ${backupFileBase}.prev.sql{,.gz,.zstd}",
# Run backup.
"systemctl start ${backupService}.service",
"ls -hal /var/backup/postgresql/ >/dev/console",
# Since nothing has changed in the database, the cur and prev files
# should match.
"zcat ${backupFileBase}.sql.gz | grep '<test>ok</test>'",
"cmp ${backupFileBase}.sql.gz ${backupFileBase}.prev.sql.gz",
# The prev files with unused suffix should be removed.
"[ ! -f '${backupFileBase}.prev.sql' ]",
"[ ! -f '${backupFileBase}.prev.sql.zstd' ]",
# Both cur and prev file should only be accessible by the postgres user.
"stat -c '%a' ${backupFileBase}.sql.gz | grep 600",
"stat -c '%a' '${backupFileBase}.prev.sql.gz' | grep 600",
)
with subtest("Backup service fails gracefully"):
# Sabotage the backup process
machine.succeed("rm /run/postgresql/.s.PGSQL.5432")
machine.fail(
"systemctl start ${backupService}.service",
)
machine.succeed(
"ls -hal /var/backup/postgresql/ >/dev/console",
"zcat ${backupFileBase}.prev.sql.gz | grep '<test>ok</test>'",
"stat ${backupFileBase}.in-progress.sql.gz",
)
# In a previous version, the second run would overwrite prev.sql.gz,
# so we test a second run as well.
machine.fail(
"systemctl start ${backupService}.service",
)
machine.succeed(
"stat ${backupFileBase}.in-progress.sql.gz",
"zcat ${backupFileBase}.prev.sql.gz | grep '<test>ok</test>'",
)
with subtest("Initdb works"):
machine.succeed("sudo -u postgres initdb -D /tmp/testpostgres2")
machine.log(machine.execute("systemd-analyze security postgresql.service | grep -v ")[1])
machine.shutdown()
'';
};
mk-ensure-clauses-test = postgresql-package: makeTest {
name = "postgresql-clauses-${postgresql-package.name}";
meta = with pkgs.lib.maintainers; {
maintainers = [ zagy ];
};
nodes.machine = {...}:
{
services.postgresql = {
enable = true;
package = postgresql-package;
ensureUsers = [
{
name = "all-clauses";
ensureClauses = {
superuser = true;
createdb = true;
createrole = true;
"inherit" = true;
login = true;
replication = true;
bypassrls = true;
};
}
{
name = "default-clauses";
}
];
};
};
testScript = let
getClausesQuery = user: pkgs.lib.concatStringsSep " "
[
"SELECT row_to_json(row)"
"FROM ("
"SELECT"
"rolsuper,"
"rolinherit,"
"rolcreaterole,"
"rolcreatedb,"
"rolcanlogin,"
"rolreplication,"
"rolbypassrls"
"FROM pg_roles"
"WHERE rolname = '${user}'"
") row;"
];
in ''
import json
machine.start()
machine.wait_for_unit("postgresql")
with subtest("All user permissions are set according to the ensureClauses attr"):
clauses = json.loads(
machine.succeed(
"sudo -u postgres psql -tc \"${getClausesQuery "all-clauses"}\""
)
)
print(clauses)
assert clauses['rolsuper'], 'expected user with clauses to have superuser clause'
assert clauses['rolinherit'], 'expected user with clauses to have inherit clause'
assert clauses['rolcreaterole'], 'expected user with clauses to have create role clause'
assert clauses['rolcreatedb'], 'expected user with clauses to have create db clause'
assert clauses['rolcanlogin'], 'expected user with clauses to have login clause'
assert clauses['rolreplication'], 'expected user with clauses to have replication clause'
assert clauses['rolbypassrls'], 'expected user with clauses to have bypassrls clause'
with subtest("All user permissions default when ensureClauses is not provided"):
clauses = json.loads(
machine.succeed(
"sudo -u postgres psql -tc \"${getClausesQuery "default-clauses"}\""
)
)
assert not clauses['rolsuper'], 'expected user with no clauses set to have default superuser clause'
assert clauses['rolinherit'], 'expected user with no clauses set to have default inherit clause'
assert not clauses['rolcreaterole'], 'expected user with no clauses set to have default create role clause'
assert not clauses['rolcreatedb'], 'expected user with no clauses set to have default create db clause'
assert clauses['rolcanlogin'], 'expected user with no clauses set to have default login clause'
assert not clauses['rolreplication'], 'expected user with no clauses set to have default replication clause'
assert not clauses['rolbypassrls'], 'expected user with no clauses set to have default bypassrls clause'
machine.shutdown()
'';
};
in
recurseIntoAttrs (
concatMapAttrs (n: p: { ${n} = testsForPackage p; }) pkgs.postgresqlVersions
// {
passthru.override = p: testsForPackage p;
}
)

View file

@ -0,0 +1,92 @@
{ pkgs
, makeTest
}:
with pkgs.lib;
let
test-sql = pkgs.writeText "postgresql-test" ''
CREATE EXTENSION timescaledb;
CREATE EXTENSION timescaledb_toolkit;
CREATE TABLE sth (
time TIMESTAMPTZ NOT NULL,
value DOUBLE PRECISION
);
SELECT create_hypertable('sth', 'time');
INSERT INTO sth (time, value) VALUES
('2003-04-12 04:05:06 America/New_York', 1.0),
('2003-04-12 04:05:07 America/New_York', 2.0),
('2003-04-12 04:05:08 America/New_York', 3.0),
('2003-04-12 04:05:09 America/New_York', 4.0),
('2003-04-12 04:05:10 America/New_York', 5.0)
;
WITH t AS (
SELECT
time_bucket('1 day'::interval, time) AS dt,
stats_agg(value) AS stats
FROM sth
GROUP BY time_bucket('1 day'::interval, time)
)
SELECT
average(stats)
FROM t;
SELECT * FROM sth;
'';
makeTimescaleDbTest = postgresqlPackage: makeTest {
name = "timescaledb-${postgresqlPackage.name}";
meta = with pkgs.lib.maintainers; {
maintainers = [ typetetris ];
};
nodes.machine = { ... }:
{
services.postgresql = {
enable = true;
package = postgresqlPackage;
extraPlugins = ps: with ps; [
timescaledb
timescaledb_toolkit
];
settings = { shared_preload_libraries = "timescaledb, timescaledb_toolkit"; };
};
};
testScript = ''
def check_count(statement, lines):
return 'test $(sudo -u postgres psql postgres -tAc "{}"|wc -l) -eq {}'.format(
statement, lines
)
machine.start()
machine.wait_for_unit("postgresql")
with subtest("Postgresql with extensions timescaledb and timescaledb_toolkit is available just after unit start"):
machine.succeed(
"sudo -u postgres psql -f ${test-sql}"
)
machine.fail(check_count("SELECT * FROM sth;", 3))
machine.succeed(check_count("SELECT * FROM sth;", 5))
machine.fail(check_count("SELECT * FROM sth;", 4))
machine.shutdown()
'';
};
in
# Not run by default, because this requires allowUnfree.
# To run these tests:
# NIXPKGS_ALLOW_UNFREE=1 nix-build -A nixosTests.postgresql.timescaledb
dontRecurseIntoAttrs (
pkgs.lib.concatMapAttrs (n: p: { ${n} = makeTimescaleDbTest p; }) (filterAttrs (n: p: !p.pkgs.timescaledb.meta.broken) pkgs.postgresqlVersions)
// {
passthru.override = p: makeTimescaleDbTest p;
}
)

View file

@ -0,0 +1,46 @@
{ pkgs
, makeTest
}:
let
makeTsjaTest = postgresqlPackage:
makeTest {
name = "tsja-${postgresqlPackage.name}";
meta = {
maintainers = with lib.maintainers; [ chayleaf ];
};
nodes = {
master =
{ config, ... }:
{
services.postgresql = {
enable = true;
package = postgresqlPackage;
extraPlugins = ps: with ps; [
tsja
];
};
};
};
testScript = ''
start_all()
master.wait_for_unit("postgresql")
master.succeed("sudo -u postgres psql -f /run/current-system/sw/share/postgresql/extension/libtsja_dbinit.sql")
# make sure "日本語" is parsed as a separate lexeme
master.succeed("""
sudo -u postgres \\
psql -c "SELECT * FROM ts_debug('japanese', 'PostgreSQL')" \\
| grep "{}"
""")
'';
};
in
pkgs.lib.recurseIntoAttrs (
pkgs.lib.concatMapAttrs (n: p: { ${n} = makeTsjaTest p; }) pkgs.postgresqlVersions
// {
passthru.override = p: makeTsjaTest p;
}
)

View file

@ -0,0 +1,50 @@
{ pkgs
, makeTest
}:
let
inherit (pkgs) lib;
makePostgresqlWal2jsonTest =
postgresqlPackage:
makeTest {
name = "wal2json-${postgresqlPackage.name}";
meta.maintainers = with pkgs.lib.maintainers; [ euank ];
nodes.machine = {
services.postgresql = {
package = postgresqlPackage;
enable = true;
extraPlugins = with postgresqlPackage.pkgs; [ wal2json ];
settings = {
wal_level = "logical";
max_replication_slots = "10";
max_wal_senders = "10";
};
};
};
testScript = ''
machine.wait_for_unit("postgresql")
machine.succeed(
"sudo -u postgres psql -qAt -f ${./wal2json/example2.sql} postgres > /tmp/example2.out"
)
machine.succeed(
"diff ${./wal2json/example2.out} /tmp/example2.out"
)
machine.succeed(
"sudo -u postgres psql -qAt -f ${./wal2json/example3.sql} postgres > /tmp/example3.out"
)
machine.succeed(
"diff ${./wal2json/example3.out} /tmp/example3.out"
)
'';
};
in
lib.recurseIntoAttrs (
lib.concatMapAttrs (n: p: { ${n} = makePostgresqlWal2jsonTest p; }) pkgs.postgresqlVersions
// {
passthru.override = p: makePostgresqlWal2jsonTest p;
}
)