1let
2 user = "someuser";
3 password = "some_password";
4 port = builtins.toString 5232;
5
6 common = { pkgs, ... }: {
7 services.radicale = {
8 enable = true;
9 config = ''
10 [auth]
11 type = htpasswd
12 htpasswd_filename = /etc/radicale/htpasswd
13 htpasswd_encryption = bcrypt
14
15 [storage]
16 filesystem_folder = /tmp/collections
17
18 [logging]
19 debug = True
20 '';
21 };
22 # WARNING: DON'T DO THIS IN PRODUCTION!
23 # This puts unhashed secrets directly into the Nix store for ease of testing.
24 environment.etc."radicale/htpasswd".source = pkgs.runCommand "htpasswd" {} ''
25 ${pkgs.apacheHttpd}/bin/htpasswd -bcB "$out" ${user} ${password}
26 '';
27 };
28
29in
30
31 import ./make-test.nix ({ lib, ... }@args: {
32 name = "radicale";
33 meta.maintainers = with lib.maintainers; [ aneeshusa infinisil ];
34
35 nodes = rec {
36 radicale = radicale1; # Make the test script read more nicely
37 radicale1 = lib.recursiveUpdate (common args) {
38 nixpkgs.overlays = [
39 (self: super: {
40 radicale1 = super.radicale1.overrideAttrs (oldAttrs: {
41 propagatedBuildInputs = with self.pythonPackages;
42 (oldAttrs.propagatedBuildInputs or []) ++ [ passlib ];
43 });
44 })
45 ];
46 system.stateVersion = "17.03";
47 };
48 radicale1_export = lib.recursiveUpdate radicale1 {
49 services.radicale.extraArgs = [
50 "--export-storage" "/tmp/collections-new"
51 ];
52 };
53 radicale2_verify = lib.recursiveUpdate radicale2 {
54 services.radicale.extraArgs = [ "--verify-storage" ];
55 };
56 radicale2 = lib.recursiveUpdate (common args) {
57 system.stateVersion = "17.09";
58 };
59 };
60
61 # This tests whether the web interface is accessible to an authenticated user
62 testScript = { nodes }: let
63 switchToConfig = nodeName: let
64 newSystem = nodes.${nodeName}.config.system.build.toplevel;
65 in "${newSystem}/bin/switch-to-configuration test";
66 in ''
67 # Check Radicale 1 functionality
68 $radicale->succeed('${switchToConfig "radicale1"} >&2');
69 $radicale->waitForUnit('radicale.service');
70 $radicale->waitForOpenPort(${port});
71 $radicale->succeed('curl --fail http://${user}:${password}@localhost:${port}/someuser/calendar.ics/');
72
73 # Export data in Radicale 2 format
74 $radicale->succeed('systemctl stop radicale');
75 $radicale->succeed('ls -al /tmp/collections');
76 $radicale->fail('ls -al /tmp/collections-new');
77 # Radicale exits immediately after exporting storage
78 $radicale->succeed('${switchToConfig "radicale1_export"} >&2');
79 $radicale->waitUntilFails('systemctl status radicale');
80 $radicale->succeed('ls -al /tmp/collections');
81 $radicale->succeed('ls -al /tmp/collections-new');
82
83 # Verify data in Radicale 2 format
84 $radicale->succeed('rm -r /tmp/collections/${user}');
85 $radicale->succeed('mv /tmp/collections-new/collection-root /tmp/collections');
86 $radicale->succeed('${switchToConfig "radicale2_verify"} >&2');
87 $radicale->waitUntilFails('systemctl status radicale');
88 my ($retcode, $logs) = $radicale->execute('journalctl -u radicale -n 5');
89 if ($retcode != 0 || index($logs, 'Verifying storage') == -1) {
90 die "Radicale 2 didn't verify storage"
91 }
92 if (index($logs, 'failed') != -1 || index($logs, 'exception') != -1) {
93 die "storage verification failed"
94 }
95
96 # Check Radicale 2 functionality
97 $radicale->succeed('${switchToConfig "radicale2"} >&2');
98 $radicale->waitForUnit('radicale.service');
99 $radicale->waitForOpenPort(${port});
100 my ($retcode, $output) = $radicale->execute('curl --fail http://${user}:${password}@localhost:${port}/someuser/calendar.ics/');
101 if ($retcode != 0 || index($output, 'VCALENDAR') == -1) {
102 die "Could not read calendar from Radicale 2"
103 }
104 $radicale->succeed('curl --fail http://${user}:${password}@localhost:${port}/.web/');
105 '';
106})