;;; secrets-tests.el --- Tests of Secret Service API -*- lexical-binding: t -*- ;; Copyright (C) 2018-2021 Free Software Foundation, Inc. ;; Author: Michael Albinus ;; This file is part of GNU Emacs. ;; ;; GNU Emacs 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 Emacs 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 Emacs. If not, see . ;;; Code: (require 'ert) (require 'secrets) (require 'seq) (require 'notifications) ;; We do not want chatty messages. (setq secrets-debug nil) (ert-deftest secrets-test00-availability () "Test availability of Secret Service API." :expected-result (if secrets-enabled :passed :failed) (should secrets-enabled) (should (dbus-ping :session secrets-service)) ;; Exit. (secrets--test-close-all-sessions)) (defun secrets--test-get-all-sessions () "Return all object paths for existing secrets sessions." (let ((session-path (concat secrets-path "/session"))) (delete session-path (dbus-introspect-get-all-nodes :session secrets-service session-path)))) (defun secrets--test-close-all-sessions () "Close all secrets sessions which are bound to this Emacs." (secrets-close-session) ;; We loop over all other sessions. If a session does not belong to ;; us, a `dbus-error' is fired, which we ignore. (dolist (path (secrets--test-get-all-sessions)) (dbus-ignore-errors (dbus-call-method :session secrets-service path secrets-interface-session "Close")))) (defun secrets--test-delete-all-session-items () "Delete all items of collection \"session\" bound to this Emacs." (dolist (item (secrets-list-items "session")) (secrets-delete-item "session" item))) (ert-deftest secrets-test01-sessions () "Test opening / closing a secrets session." (skip-unless secrets-enabled) (skip-unless (secrets-empty-path secrets-session-path)) (unwind-protect (progn ;; Simple opening / closing of a session. (should (secrets-open-session)) (should-not (secrets-empty-path secrets-session-path)) (should (secrets-close-session)) (should (secrets-empty-path secrets-session-path)) ;; Reopening a new session. (should (string-equal (secrets-open-session) (secrets-open-session))) (should (string-equal secrets-session-path (secrets-open-session))) (should-not (string-equal (secrets-open-session) (secrets-open-session 'reopen))) (should-not (string-equal secrets-session-path (secrets-open-session 'reopen)))) ;; Exit. (should (secrets-close-session)) (secrets--test-close-all-sessions))) (ert-deftest secrets-test02-collections () "Test creation / deletion a secrets collections." (skip-unless secrets-enabled) (skip-unless (secrets-empty-path secrets-session-path)) (unwind-protect (progn (should (secrets-open-session)) (should (member "session" (secrets-list-collections))) ;; Create a random collection. This asks for a password ;; outside our control, so we make it in the interactive case ;; only. (unless noninteractive (let ((collection (md5 (concat (prin1-to-string process-environment) (current-time-string)))) (alias (secrets-get-alias "default"))) (notifications-notify :title (symbol-name (ert-test-name (ert-running-test))) :body "Please enter the password \"secret\" twice") ;; The optional argument ALIAS does not seem to work. (should (secrets-create-collection collection)) (should (member collection (secrets-list-collections))) ;; We reset the alias. The temporary collection "session" ;; is not accepted. (secrets-set-alias collection "default") (should (string-equal (secrets-get-alias "default") collection)) ;; Delete alias. (secrets-delete-alias "default") (should-not (secrets-get-alias "default")) ;; Lock / unlock the collection. (secrets-lock-collection collection) (should (secrets-get-collection-property (secrets-collection-path collection) "Locked")) (notifications-notify :title (symbol-name (ert-test-name (ert-running-test))) :body "Please enter the password \"secret\"") (secrets-unlock-collection collection) (should-not (secrets-get-collection-property (secrets-collection-path collection) "Locked")) ;; Delete the collection. The alias disappears as well. (secrets-set-alias collection "default") (secrets-delete-collection collection) (should-not (secrets-get-alias "default")) ;; Reset alias. (when alias (secrets-set-alias alias "default") (should (string-equal (secrets-get-alias "default") alias)))))) ;; Exit. (should (secrets-close-session)) (secrets--test-close-all-sessions))) (ert-deftest secrets-test03-items () "Test creation / deletion a secret item." (skip-unless secrets-enabled) (skip-unless (secrets-empty-path secrets-session-path)) (unwind-protect (let (item-path) (should (secrets-open-session)) ;; Cleanup. There could be items in the "session" collection. (secrets--test-delete-all-session-items) ;; There shall be no items in the "session" collection. (should-not (secrets-list-items "session")) ;; Create a new item. (should (setq item-path (secrets-create-item "session" "foo" "secret"))) (dolist (item `("foo" ,item-path)) (should (string-equal (secrets-get-secret "session" item) "secret"))) ;; Create another item with same label. (should (secrets-create-item "session" "foo" "geheim")) (should (equal (secrets-list-items "session") '("foo" "foo"))) ;; Create an item with attributes. (should (setq item-path (secrets-create-item "session" "bar" "secret" :method "sudo" :user "joe" :host "remote-host"))) (dolist (item `("bar" ,item-path)) (should (string-equal (secrets-get-attribute "session" item :method) "sudo")) ;; The attribute :xdg:schema is added silently. (should (seq-set-equal-p (secrets-get-attributes "session" item) '((:xdg:schema . "org.freedesktop.Secret.Generic") (:host . "remote-host") (:user . "joe") (:method . "sudo"))))) ;; Create an item with another schema. (should (setq item-path (secrets-create-item "session" "baz" "secret" :xdg:schema "org.gnu.Emacs.foo"))) (dolist (item `("baz" ,item-path)) (should (equal (secrets-get-attributes "session" item) '((:xdg:schema . "org.gnu.Emacs.foo"))))) ;; Delete them. (dolist (item (secrets-list-items "session")) (secrets-delete-item "session" item)) (should-not (secrets-list-items "session"))) ;; Exit. (secrets--test-delete-all-session-items) (should (secrets-close-session)) (secrets--test-close-all-sessions))) (ert-deftest secrets-test04-search () "Test searching of secret items." (skip-unless secrets-enabled) (skip-unless (secrets-empty-path secrets-session-path)) (unwind-protect (progn (should (secrets-open-session)) ;; Cleanup. There could be items in the "session" collection. (secrets--test-delete-all-session-items) ;; There shall be no items in the "session" collection. (should-not (secrets-list-items "session")) ;; Create some items. (should (secrets-create-item "session" "foo" "secret" :method "sudo" :user "joe" :host "remote-host")) (should (secrets-create-item "session" "bar" "secret" :method "sudo" :user "smith" :host "remote-host")) (should (secrets-create-item "session" "baz" "secret" :method "ssh" :user "joe" :host "other-host")) ;; Search the items. `secrets-search-items' uses ;; `secrets-search-item-paths' internally, it is sufficient to ;; test only one of them. (should-not (secrets-search-item-paths "session" :user "john")) (should-not (secrets-search-items "session" :user "john")) (should-not (secrets-search-items "session" :xdg:schema "org.gnu.Emacs.foo")) (should (equal (sort (secrets-search-items "session" :user "joe") #'string-lessp) '("baz" "foo"))) (should (equal (secrets-search-items "session":method "sudo" :user "joe") '("foo"))) (should (equal (sort (secrets-search-items "session") #'string-lessp) '("bar" "baz" "foo")))) ;; Exit. (secrets--test-delete-all-session-items) (should (secrets-close-session)) (secrets--test-close-all-sessions))) (defun secrets-test-all (&optional interactive) "Run all tests for \\[secrets]." (interactive "p") (funcall (if interactive #'ert-run-tests-interactively #'ert-run-tests-batch) "^secrets")) (provide 'secrets-tests) ;;; secrets-tests.el ends here