;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2021, 2022, 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>. ;;; ;;; This file is part of GNU Guix. ;;; ;;; GNU Guix is free software; you can redistribute it and/or modify it ;;; under the terms of the GNU General Public License as published by ;;; the Free Software Foundation; either version 3 of the License, or (at ;;; your option) any later version. ;;; ;;; GNU Guix is distributed in the hope that it will be useful, but ;;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;;; GNU General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. (define-module (gnu tests telephony) #:use-module (gnu) #:use-module (gnu packages) #:use-module (gnu packages guile) #:use-module (gnu packages guile-xyz) #:use-module (gnu tests) #:use-module (gnu system vm) #:use-module (gnu services) #:use-module (gnu services dbus) #:use-module (gnu services networking) #:use-module (gnu services ssh) #:use-module (gnu services telephony) #:use-module (guix gexp) #:use-module (guix modules) #:export (%test-jami %test-jami-provisioning %test-jami-provisioning-partial)) ;;; ;;; Jami daemon. ;;; (define %jami-account-content-sexp (call-with-input-file (search-path %load-path "gnu/tests/data/jami-dummy-account.dat") read)) (define %dummy-jami-account-archive ;; A Jami account archive is a gzipped JSON file. (computed-file "dummy-jami-account.gz" (with-extensions (list guile-json-4 guile-zlib) #~(begin (use-modules (json) (zlib)) (let ((port (open-output-file #$output))) (call-with-gzip-output-port port (lambda (port) (scm->json '#$%jami-account-content-sexp port)))))))) (define %allowed-contacts '("1dbcb0f5f37324228235564b79f2b9737e9a008f" "2dbcb0f5f37324228235564b79f2b9737e9a008f")) (define %moderators '("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")) (define %dummy-jami-account (jami-account (archive %dummy-jami-account-archive) (allowed-contacts %allowed-contacts) (moderators %moderators) (rendezvous-point? #t) (peer-discovery? #f) (bootstrap-hostnames '("bootstrap.me" "fallback.another.host")) (name-server-uri "https://my.name.server"))) ;;; Like %dummy-jami-account, but with allowed-contacts and moderators left ;;; unset (thus taking the value *unspecified*). (define %dummy-jami-account-partial (jami-account (archive %dummy-jami-account-archive) (rendezvous-point? #t) (peer-discovery? #f) (bootstrap-hostnames '("bootstrap.me" "fallback.another.host")) (name-server-uri "https://my.name.server"))) (define* (make-jami-os #:key provisioning? partial?) (operating-system (host-name "jami") (timezone "America/Montreal") (locale "en_US.UTF-8") (bootloader (bootloader-configuration (bootloader grub-bootloader) (targets '("/dev/sdX")))) (file-systems (cons (file-system (device (file-system-label "my-root")) (mount-point "/") (type "ext4")) %base-file-systems)) (firmware '()) (services (cons* (service jami-service-type (if provisioning? (jami-configuration (debug? #t) (accounts (list (if partial? %dummy-jami-account-partial %dummy-jami-account)))) (jami-configuration (debug? #t)))) (service dbus-root-service-type) ;; The following services/packages are added for ;; debugging purposes. (service dhcp-client-service-type) (service openssh-service-type (openssh-configuration (permit-root-login #t) (allow-empty-passwords? #t))) %base-services)) (packages (cons* (specification->package "recutils") (specification->package "strace") %base-packages)))) (define %jami-os (make-jami-os)) (define %jami-os-provisioning (make-jami-os #:provisioning? #t)) (define %jami-os-provisioning-partial (make-jami-os #:provisioning? #t #:partial? #t)) (define* (run-jami-test #:key provisioning? partial?) "Run tests in %JAMI-OS. When PROVISIONING? is true, test the accounts provisioning feature of the service. When PARTIAL? is #t, some fields of the jami account used as part of the jami configuration are left *unspecified*." (define os (marionette-operating-system (if provisioning? (if partial? %jami-os-provisioning-partial %jami-os-provisioning) %jami-os) #:imported-modules '((gnu services herd) (guix combinators) (gnu build jami-service) (gnu build dbus-service)))) (define vm (virtual-machine (operating-system os) (memory-size 512))) (define username (assoc-ref %jami-account-content-sexp "Account.username")) (define test (with-imported-modules (source-module-closure '((gnu build marionette))) #~(begin (use-modules (srfi srfi-64) (gnu build marionette)) (define marionette (make-marionette (list #$vm))) (test-runner-current (system-test-runner #$output)) (test-begin "jami") (test-assert "d-bus tooling loaded" ;; Add Guile-AC-D-Bus and related libraries to the marionette's ;; search path. (marionette-eval '(let ((libraries '(#$guile-ac-d-bus #$guile-packrat))) ;used by ac-d-bus (setenv "DBUS_SESSION_BUS_ADDRESS" "unix:path=/var/run/jami/bus") (set! %load-path (append %load-path (map (lambda (directory) (string-append directory "/share/guile/site/" (effective-version))) libraries))) (set! %load-compiled-path (append %load-compiled-path (map (lambda (directory) (string-append directory "/lib/guile/3.0/site-ccache")) libraries))) %load-path) marionette)) (test-assert "dbus session is up" (and (marionette-eval '(begin (use-modules (gnu services herd)) (wait-for-service 'jami-dbus-session #:timeout 40)) marionette) (wait-for-unix-socket "/var/run/jami/bus" marionette))) (test-assert "service is running" (marionette-eval '(begin (use-modules (gnu build jami-service) (gnu services herd)) (wait-for-service 'jami #:timeout 40) (jami-service-available?)) marionette)) (test-assert "service can be stopped" (marionette-eval '(begin (use-modules (gnu build dbus-service) (gnu build jami-service) (gnu services herd) (rnrs base)) (assert (jami-service-available?)) (stop-service 'jami) (with-retries 20 1 (not (jami-service-available?)))) marionette)) (test-assert "service can be restarted" (marionette-eval '(begin (use-modules (gnu build dbus-service) (gnu build jami-service) (gnu services herd) (rnrs base) ) ;; Start the service. (start-service 'jami) (with-retries 40 1 (jami-service-available?)) ;; Restart the service. (restart-service 'jami) (with-retries 40 1 (jami-service-available?))) marionette)) (unless #$provisioning? (test-skip 1)) (test-assert "jami accounts provisioning, account present" (marionette-eval '(begin (use-modules (gnu build dbus-service) (gnu services herd) (rnrs base)) ;; Accounts take some time to appear after being added. (with-retries 40 1 (with-shepherd-action 'jami ('list-accounts) results (let ((account (assoc-ref (car results) #$username))) (assert (string=? #$username (assoc-ref account "Account.username"))))))) marionette)) (unless #$(and provisioning? (not partial?)) (test-skip 1)) (test-assert "jami accounts provisioning, allowed-contacts" (marionette-eval '(begin (use-modules (gnu services herd) (rnrs base) (srfi srfi-1)) ;; Public mode is disabled. (with-shepherd-action 'jami ('list-account-details) results (let ((account (assoc-ref (car results) #$username))) (assert (string=? "false" (assoc-ref account "DHT.PublicInCalls"))))) ;; Allowed contacts match those declared in the configuration. (with-shepherd-action 'jami ('list-contacts) results (let ((contacts (assoc-ref (car results) #$username))) (assert (lset= string-ci=? contacts '#$%allowed-contacts))))) marionette)) (unless #$(and provisioning? (not partial?)) (test-skip 1)) (test-assert "jami accounts provisioning, moderators" (marionette-eval '(begin (use-modules (gnu services herd) (rnrs base) (srfi srfi-1)) ;; Moderators match those declared in the configuration. (with-shepherd-action 'jami ('list-moderators) results (let ((moderators (assoc-ref (car results) #$username))) (assert (lset= string-ci=? moderators '#$%moderators)))) ;; Moderators can be added via the Shepherd action. (with-shepherd-action 'jami ('add-moderator "cccccccccccccccccccccccccccccccccccccccc" #$username) results (let ((moderators (car results))) (assert (lset= string-ci=? moderators (cons "cccccccccccccccccccccccccccccccccccccccc" '#$%moderators)))))) marionette)) (unless #$provisioning? (test-skip 1)) (test-assert "jami service actions, ban/unban contacts" (marionette-eval '(begin (use-modules (gnu services herd) (ice-9 match) (rnrs base) (srfi srfi-1)) ;; Globally ban a contact. (with-shepherd-action 'jami ('ban-contact "1dbcb0f5f37324228235564b79f2b9737e9a008f") _ (with-shepherd-action 'jami ('list-banned-contacts) results (every (match-lambda ((username . banned-contacts) (member "1dbcb0f5f37324228235564b79f2b9737e9a008f" banned-contacts))) (car results)))) ;; Ban a contact for a single account. (with-shepherd-action 'jami ('ban-contact "dddddddddddddddddddddddddddddddddddddddd" #$username) _ (with-shepherd-action 'jami ('list-banned-contacts) results (every (match-lambda ((username . banned-contacts) (let ((found? (member "dddddddddddddddddddddddddddddddddddddddd" banned-contacts))) (if (string=? #$username username) found? (not found?))))) (car results))))) marionette)) (unless #$provisioning? (test-skip 1)) (test-assert "jami service actions, enable/disable accounts" (marionette-eval '(begin (use-modules (gnu services herd) (rnrs base)) (with-shepherd-action 'jami ('disable-account #$username) _ (with-shepherd-action 'jami ('list-accounts) results (let ((account (assoc-ref (car results) #$username))) (assert (string= "false" (assoc-ref account "Account.enable")))))) (with-shepherd-action 'jami ('enable-account #$username) _ (with-shepherd-action 'jami ('list-accounts) results (let ((account (assoc-ref (car results) #$username))) (assert (string= "true" (assoc-ref account "Account.enable"))))))) marionette)) (unless #$provisioning? (test-skip 1)) (test-assert "jami account parameters" (marionette-eval '(begin (use-modules (gnu services herd) (rnrs base) (srfi srfi-1)) (with-shepherd-action 'jami ('list-account-details) results (let ((account-details (assoc-ref (car results) #$username))) (assert (lset<= equal? '(("Account.hostname" . "bootstrap.me;fallback.another.host") ("Account.peerDiscovery" . "false") ("Account.rendezVous" . "true") ("RingNS.uri" . "https://my.name.server")) account-details))))) marionette)) (test-end)))) (gexp->derivation (if provisioning? (if partial? "jami-provisioning-partial-test" "jami-provisioning-test") "jami-test") test)) (define %test-jami (system-test (name "jami") (description "Basic tests for the jami service.") (value (run-jami-test)))) (define %test-jami-provisioning (system-test (name "jami-provisioning") (description "Provisioning test for the jami service.") (value (run-jami-test #:provisioning? #t)))) ;;; This test verifies that <jami-account> values can be left unspecified ;;; without causing any issue (see: https://issues.guix.gnu.org/56799). (define %test-jami-provisioning-partial (system-test (name "jami-provisioning-partial") (description "Provisioning test for the jami service, when some of the 'maybe' fields aren't provided (such that their value end up being *unspecified*.") (value (run-jami-test #:provisioning? #t #:partial? #t))))