nixos/tests/vdirsyncer: init
This commit is contained in:
parent
aaf715153c
commit
cd06d2dd2a
@ -1275,6 +1275,7 @@ in {
|
||||
vault-dev = handleTest ./vault-dev.nix {};
|
||||
vault-postgresql = handleTest ./vault-postgresql.nix {};
|
||||
vaultwarden = discoverTests (import ./vaultwarden.nix);
|
||||
vdirsyncer = handleTest ./vdirsyncer.nix {};
|
||||
vector = handleTest ./vector {};
|
||||
velocity = runTest ./velocity.nix;
|
||||
vengi-tools = handleTest ./vengi-tools.nix {};
|
||||
|
||||
303
nixos/tests/vdirsyncer.nix
Normal file
303
nixos/tests/vdirsyncer.nix
Normal file
@ -0,0 +1,303 @@
|
||||
import ./make-test-python.nix (
|
||||
{ pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
|
||||
radicale_calendars = {
|
||||
type = "caldav";
|
||||
url = "http://localhost:5232/";
|
||||
# Radicale needs username/password.
|
||||
username = "alice";
|
||||
password = "password";
|
||||
};
|
||||
|
||||
radicale_contacts = {
|
||||
type = "carddav";
|
||||
url = "http://localhost:5232/";
|
||||
# Radicale needs username/password.
|
||||
username = "alice";
|
||||
password = "password";
|
||||
};
|
||||
|
||||
xandikos_calendars = {
|
||||
type = "caldav";
|
||||
url = "http://localhost:8080/user/calendars";
|
||||
# Xandikos warns
|
||||
# > No current-user-principal returned, re-using URL http://localhost:8080/user/calendars/
|
||||
# but we do not need username/password.
|
||||
};
|
||||
|
||||
xandikos_contacts = {
|
||||
type = "carddav";
|
||||
url = "http://localhost:8080/user/contacts";
|
||||
};
|
||||
|
||||
local_calendars = {
|
||||
type = "filesystem";
|
||||
path = "~/calendars";
|
||||
fileext = ".ics";
|
||||
};
|
||||
|
||||
local_contacts = {
|
||||
type = "filesystem";
|
||||
path = "~/contacts";
|
||||
fileext = ".vcf";
|
||||
};
|
||||
|
||||
mkPairs = a: b: {
|
||||
calendars = {
|
||||
a = "${a}_calendars";
|
||||
b = "${b}_calendars";
|
||||
collections = [
|
||||
"from a"
|
||||
"from b"
|
||||
];
|
||||
};
|
||||
contacts = {
|
||||
a = "${a}_contacts";
|
||||
b = "${b}_contacts";
|
||||
collections = [
|
||||
"from a"
|
||||
"from b"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
mkRadicaleProps =
|
||||
tag:
|
||||
pkgs.writeText "Radicale.props" (
|
||||
builtins.toJSON {
|
||||
inherit tag;
|
||||
}
|
||||
);
|
||||
|
||||
writeLines =
|
||||
name: eol: lines:
|
||||
pkgs.writeText name (lib.concatMapStrings (l: "${l}${eol}") lines);
|
||||
|
||||
prodid = "-//NixOS test//EN";
|
||||
dtstamp = "20231129T194743Z";
|
||||
|
||||
writeICS =
|
||||
{
|
||||
uid,
|
||||
summary,
|
||||
dtstart,
|
||||
dtend,
|
||||
}:
|
||||
writeLines "${uid}.ics" "\r\n" [
|
||||
"BEGIN:VCALENDAR"
|
||||
"VERSION:2.0"
|
||||
"PRODID:${prodid}"
|
||||
"BEGIN:VEVENT"
|
||||
"UID:${uid}"
|
||||
"SUMMARY:${summary}"
|
||||
"DTSTART:${dtstart}"
|
||||
"DTEND:${dtend}"
|
||||
"DTSTAMP:${dtstamp}"
|
||||
"END:VEVENT"
|
||||
"END:VCALENDAR"
|
||||
];
|
||||
|
||||
foo_ics = writeICS {
|
||||
uid = "foo";
|
||||
summary = "Epochalypse";
|
||||
dtstart = "19700101T000000Z";
|
||||
dtend = "20380119T031407Z";
|
||||
};
|
||||
|
||||
bar_ics = writeICS {
|
||||
uid = "bar";
|
||||
summary = "One Billion Seconds";
|
||||
dtstart = "19700101T000000Z";
|
||||
dtend = "20010909T014640Z";
|
||||
};
|
||||
|
||||
writeVCF =
|
||||
{
|
||||
uid,
|
||||
name,
|
||||
displayName,
|
||||
email,
|
||||
}:
|
||||
writeLines "${uid}.vcf" "\r\n" [
|
||||
# One of the tools enforces this order of fields.
|
||||
"BEGIN:VCARD"
|
||||
"VERSION:4.0"
|
||||
"UID:${uid}"
|
||||
"EMAIL;TYPE=INTERNET:${email}"
|
||||
"FN:${displayName}"
|
||||
"N:${name}"
|
||||
"END:VCARD"
|
||||
];
|
||||
|
||||
foo_vcf = writeVCF {
|
||||
uid = "foo";
|
||||
name = "Doe;John;;;";
|
||||
displayName = "John Doe";
|
||||
email = "john.doe@example.org";
|
||||
};
|
||||
|
||||
bar_vcf = writeVCF {
|
||||
uid = "bar";
|
||||
name = "Doe;Jane;;;";
|
||||
displayName = "Jane Doe";
|
||||
email = "jane.doe@example.org";
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
name = "vdirsyncer";
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ schnusch ];
|
||||
|
||||
nodes = {
|
||||
machine = {
|
||||
services.radicale.enable = true;
|
||||
services.xandikos = {
|
||||
enable = true;
|
||||
extraOptions = [ "--autocreate" ];
|
||||
};
|
||||
|
||||
services.vdirsyncer = {
|
||||
enable = true;
|
||||
jobs = {
|
||||
|
||||
alice = {
|
||||
user = "alice";
|
||||
group = "users";
|
||||
config = {
|
||||
statusPath = "/home/alice/.vdirsyncer";
|
||||
storages = {
|
||||
inherit
|
||||
local_calendars
|
||||
local_contacts
|
||||
radicale_calendars
|
||||
radicale_contacts
|
||||
;
|
||||
};
|
||||
pairs = mkPairs "local" "radicale";
|
||||
};
|
||||
forceDiscover = true;
|
||||
};
|
||||
|
||||
bob = {
|
||||
user = "bob";
|
||||
group = "users";
|
||||
config = {
|
||||
statusPath = "/home/bob/.vdirsyncer";
|
||||
storages = {
|
||||
inherit
|
||||
local_calendars
|
||||
local_contacts
|
||||
xandikos_calendars
|
||||
xandikos_contacts
|
||||
;
|
||||
};
|
||||
pairs = mkPairs "local" "xandikos";
|
||||
};
|
||||
forceDiscover = true;
|
||||
};
|
||||
|
||||
remote = {
|
||||
config = {
|
||||
storages = {
|
||||
inherit
|
||||
radicale_calendars
|
||||
radicale_contacts
|
||||
xandikos_calendars
|
||||
xandikos_contacts
|
||||
;
|
||||
};
|
||||
pairs = mkPairs "radicale" "xandikos";
|
||||
};
|
||||
forceDiscover = true;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
# ProtectHome is the default, but we must access our storage
|
||||
# in ~.
|
||||
systemd.services = {
|
||||
"vdirsyncer@alice".serviceConfig.ProtectHome = lib.mkForce false;
|
||||
"vdirsyncer@bob".serviceConfig.ProtectHome = lib.mkForce false;
|
||||
};
|
||||
|
||||
users.users = {
|
||||
alice.isNormalUser = true;
|
||||
bob.isNormalUser = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
def run_unit(name):
|
||||
machine.systemctl(f"start {name}")
|
||||
# The service is Type=oneshot without RemainAfterExit=yes. Once it
|
||||
# is finished it is no longer active and wait_for_unit will fail.
|
||||
# When that happens we check if it actually failed.
|
||||
try:
|
||||
machine.wait_for_unit(name)
|
||||
except:
|
||||
machine.fail(f"systemctl is-failed {name}")
|
||||
|
||||
start_all()
|
||||
|
||||
machine.wait_for_open_port(5232)
|
||||
machine.wait_for_open_port(8080)
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
|
||||
with subtest("alice -> radicale"):
|
||||
# vdirsyncer cannot create create collections on Radicale,
|
||||
# see https://vdirsyncer.pimutils.org/en/stable/tutorials/radicale.html
|
||||
machine.succeed("runuser -u radicale -- install -Dm 644 ${mkRadicaleProps "VCALENDAR"} /var/lib/radicale/collections/collection-root/alice/foocal/.Radicale.props")
|
||||
machine.succeed("runuser -u radicale -- install -Dm 644 ${mkRadicaleProps "VADDRESSBOOK"} /var/lib/radicale/collections/collection-root/alice/foocard/.Radicale.props")
|
||||
|
||||
machine.succeed("runuser -u alice -- install -Dm 644 ${foo_ics} /home/alice/calendars/foocal/foo.ics")
|
||||
machine.succeed("runuser -u alice -- install -Dm 644 ${foo_vcf} /home/alice/contacts/foocard/foo.vcf")
|
||||
run_unit("vdirsyncer@alice.service")
|
||||
|
||||
# test statusPath
|
||||
machine.succeed("test -d /home/alice/.vdirsyncer")
|
||||
machine.fail("test -e /var/lib/private/vdirsyncer/alice")
|
||||
|
||||
with subtest("bob -> xandikos"):
|
||||
# I suspect Radicale shares the namespace for calendars and
|
||||
# contacts, but Xandikos separates them. We just use `barcal` and
|
||||
# `barcard` with Xandikos as well to avoid conflicts.
|
||||
machine.succeed("runuser -u bob -- install -Dm 644 ${bar_ics} /home/bob/calendars/barcal/bar.ics")
|
||||
machine.succeed("runuser -u bob -- install -Dm 644 ${bar_vcf} /home/bob/contacts/barcard/bar.vcf")
|
||||
run_unit("vdirsyncer@bob.service")
|
||||
|
||||
# test statusPath
|
||||
machine.succeed("test -d /home/bob/.vdirsyncer")
|
||||
machine.fail("test -e /var/lib/private/vdirsyncer/bob")
|
||||
|
||||
with subtest("radicale <-> xandikos"):
|
||||
# vdirsyncer cannot create create collections on Radicale,
|
||||
# see https://vdirsyncer.pimutils.org/en/stable/tutorials/radicale.html
|
||||
machine.succeed("runuser -u radicale -- install -Dm 644 ${mkRadicaleProps "VCALENDAR"} /var/lib/radicale/collections/collection-root/alice/barcal/.Radicale.props")
|
||||
machine.succeed("runuser -u radicale -- install -Dm 644 ${mkRadicaleProps "VADDRESSBOOK"} /var/lib/radicale/collections/collection-root/alice/barcard/.Radicale.props")
|
||||
|
||||
run_unit("vdirsyncer@remote.service")
|
||||
|
||||
# test statusPath
|
||||
machine.succeed("test -d /var/lib/private/vdirsyncer/remote")
|
||||
|
||||
with subtest("radicale -> alice"):
|
||||
run_unit("vdirsyncer@alice.service")
|
||||
|
||||
with subtest("xandikos -> bob"):
|
||||
run_unit("vdirsyncer@bob.service")
|
||||
|
||||
with subtest("compare synced files"):
|
||||
# iCalendar files get reordered
|
||||
machine.succeed("diff -u --strip-trailing-cr <(sort /home/alice/calendars/foocal/foo.ics) <(sort /home/bob/calendars/foocal/foo.ics) >&2")
|
||||
machine.succeed("diff -u --strip-trailing-cr <(sort /home/bob/calendars/barcal/bar.ics) <(sort /home/alice/calendars/barcal/bar.ics) >&2")
|
||||
|
||||
machine.succeed("diff -u --strip-trailing-cr /home/alice/contacts/foocard/foo.vcf /home/bob/contacts/foocard/foo.vcf >&2")
|
||||
machine.succeed("diff -u --strip-trailing-cr /home/bob/contacts/barcard/bar.vcf /home/alice/contacts/barcard/bar.vcf >&2")
|
||||
'';
|
||||
}
|
||||
)
|
||||
Loading…
x
Reference in New Issue
Block a user