about summary refs log tree commit diff
path: root/nixos/tests/radicale.nix
blob: 1d3679c82a20cc6c5f257aa202cbd8f07f5ebe2e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
let
  user = "someuser";
  password = "some_password";
  port = builtins.toString 5232;

  common = { pkgs, ... }: {
    services.radicale = {
      enable = true;
      config = ''
        [auth]
        type = htpasswd
        htpasswd_filename = /etc/radicale/htpasswd
        htpasswd_encryption = bcrypt

        [storage]
        filesystem_folder = /tmp/collections
      '';
    };
    # WARNING: DON'T DO THIS IN PRODUCTION!
    # This puts unhashed secrets directly into the Nix store for ease of testing.
    environment.etc."radicale/htpasswd".source = pkgs.runCommand "htpasswd" {} ''
      ${pkgs.apacheHttpd}/bin/htpasswd -bcB "$out" ${user} ${password}
    '';
  };

in

  import ./make-test-python.nix ({ lib, ... }@args: {
    name = "radicale";
    meta.maintainers = with lib.maintainers; [ aneeshusa infinisil ];

    nodes = rec {
      radicale = radicale1; # Make the test script read more nicely
      radicale1 = lib.recursiveUpdate (common args) {
        nixpkgs.overlays = [
          (self: super: {
            radicale1 = super.radicale1.overrideAttrs (oldAttrs: {
              propagatedBuildInputs = with self.pythonPackages;
                (oldAttrs.propagatedBuildInputs or []) ++ [ passlib ];
            });
          })
        ];
        system.stateVersion = "17.03";
      };
      radicale1_export = lib.recursiveUpdate radicale1 {
        services.radicale.extraArgs = [
          "--export-storage" "/tmp/collections-new"
        ];
        system.stateVersion = "17.03";
      };
      radicale2_verify = lib.recursiveUpdate radicale2 {
        services.radicale.extraArgs = [ "--debug" "--verify-storage" ];
        system.stateVersion = "17.09";
      };
      radicale2 = lib.recursiveUpdate (common args) {
        system.stateVersion = "17.09";
      };
      radicale3 = lib.recursiveUpdate (common args) {
        system.stateVersion = "20.09";
      };
    };

    # This tests whether the web interface is accessible to an authenticated user
    testScript = { nodes }: let
      switchToConfig = nodeName: let
        newSystem = nodes.${nodeName}.config.system.build.toplevel;
      in "${newSystem}/bin/switch-to-configuration test";
    in ''
      with subtest("Check Radicale 1 functionality"):
          radicale.succeed(
              "${switchToConfig "radicale1"} >&2"
          )
          radicale.wait_for_unit("radicale.service")
          radicale.wait_for_open_port(${port})
          radicale.succeed(
              "curl --fail http://${user}:${password}@localhost:${port}/someuser/calendar.ics/"
          )

      with subtest("Export data in Radicale 2 format"):
          radicale.succeed("systemctl stop radicale")
          radicale.succeed("ls -al /tmp/collections")
          radicale.fail("ls -al /tmp/collections-new")

      with subtest("Radicale exits immediately after exporting storage"):
          radicale.succeed(
              "${switchToConfig "radicale1_export"} >&2"
          )
          radicale.wait_until_fails("systemctl status radicale")
          radicale.succeed("ls -al /tmp/collections")
          radicale.succeed("ls -al /tmp/collections-new")

      with subtest("Verify data in Radicale 2 format"):
          radicale.succeed("rm -r /tmp/collections/${user}")
          radicale.succeed("mv /tmp/collections-new/collection-root /tmp/collections")
          radicale.succeed(
              "${switchToConfig "radicale2_verify"} >&2"
          )
          radicale.wait_until_fails("systemctl status radicale")

          (retcode, logs) = radicale.execute("journalctl -u radicale -n 10")
          assert (
              retcode == 0 and "Verifying storage" in logs
          ), "Radicale 2 didn't verify storage"
          assert (
              "failed" not in logs and "exception" not in logs
          ), "storage verification failed"

      with subtest("Check Radicale 2 functionality"):
          radicale.succeed(
              "${switchToConfig "radicale2"} >&2"
          )
          radicale.wait_for_unit("radicale.service")
          radicale.wait_for_open_port(${port})

          (retcode, output) = radicale.execute(
              "curl --fail http://${user}:${password}@localhost:${port}/someuser/calendar.ics/"
          )
          assert (
              retcode == 0 and "VCALENDAR" in output
          ), "Could not read calendar from Radicale 2"

          radicale.succeed("curl --fail http://${user}:${password}@localhost:${port}/.web/")

      with subtest("Check Radicale 3 functionality"):
          radicale.succeed(
              "${switchToConfig "radicale3"} >&2"
          )
          radicale.wait_for_unit("radicale.service")
          radicale.wait_for_open_port(${port})

          (retcode, output) = radicale.execute(
              "curl --fail http://${user}:${password}@localhost:${port}/someuser/calendar.ics/"
          )
          assert (
              retcode == 0 and "VCALENDAR" in output
          ), "Could not read calendar from Radicale 3"

          radicale.succeed("curl --fail http://${user}:${password}@localhost:${port}/.web/")
    '';
})