summaryrefslogtreecommitdiff
path: root/test/lisp/progmodes/sql-tests.el
diff options
context:
space:
mode:
Diffstat (limited to 'test/lisp/progmodes/sql-tests.el')
-rw-r--r--test/lisp/progmodes/sql-tests.el456
1 files changed, 454 insertions, 2 deletions
diff --git a/test/lisp/progmodes/sql-tests.el b/test/lisp/progmodes/sql-tests.el
index ad22906ecf1..c644d115df6 100644
--- a/test/lisp/progmodes/sql-tests.el
+++ b/test/lisp/progmodes/sql-tests.el
@@ -1,6 +1,6 @@
;;; sql-tests.el --- Tests for sql.el -*- lexical-binding: t; -*-
-;; Copyright (C) 2016-2017 Free Software Foundation, Inc.
+;; Copyright (C) 2016-2022 Free Software Foundation, Inc.
;; Author: Simen Heggestøyl <simenheg@gmail.com>
;; Keywords:
@@ -28,6 +28,7 @@
(require 'cl-lib)
(require 'ert)
+(require 'ert-x)
(require 'sql)
(ert-deftest sql-tests-postgres-list-databases ()
@@ -50,8 +51,459 @@
(lambda (_command) t))
((symbol-function 'process-lines)
(lambda (_program &rest _args)
- (error))))
+ (error "Some error"))))
(should-not (sql-postgres-list-databases))))
+;;; Check Connection Password Handling/Wallet
+
+(defvar sql-test-login-params nil)
+(defmacro with-sql-test-connect-harness (id login-params connection expected)
+ "Set-up and tear-down SQL connect related test.
+
+Identify tests by ID. Set :sql-login dialect attribute to
+LOGIN-PARAMS. Provide the CONNECTION parameters and the EXPECTED
+string of values passed to the comint function for validation."
+ (declare (indent 2))
+ `(ert-with-temp-file tempfile
+ :suffix "sql-test-netrc"
+ :text (concat
+ "machine aMachine user aUserName password \"netrc-A aPassword\""
+ "machine aServer user aUserName password \"netrc-B aPassword\""
+ "machine aMachine server aServer user aUserName password \"netrc-C aPassword\""
+ "machine aMachine database aDatabase user aUserName password \"netrc-D aPassword\""
+ "machine aDatabase user aUserName password \"netrc-E aPassword\""
+ "machine aMachine server aServer database aDatabase user aUserName password \"netrc-F aPassword\""
+ "machine \"aServer/aDatabase\" user aUserName password \"netrc-G aPassword\""
+ "\n")
+ (cl-letf
+ ((sql-test-login-params ' ,login-params)
+ ((symbol-function 'sql-comint-test)
+ (lambda (product options &optional buf-name)
+ (with-current-buffer (get-buffer-create buf-name)
+ (insert (pp-to-string (list product options sql-user sql-password sql-server sql-database))))))
+ ((symbol-function 'sql-run-test)
+ (lambda (&optional buffer)
+ (interactive "P")
+ (sql-product-interactive 'sqltest buffer)))
+ (sql-user nil)
+ (sql-server nil)
+ (sql-database nil)
+ (sql-product-alist
+ '((ansi)
+ (sqltest
+ :name "SqlTest"
+ :sqli-login sql-test-login-params
+ :sqli-comint-func sql-comint-test)))
+ (sql-connection-alist
+ '((,(format "test-%s" id)
+ ,@connection)))
+ (sql-password-wallet (list tempfile)))
+ (let* ((connection ,(format "test-%s" id))
+ (buffername (format "*SQL: ERT TEST <%s>*" connection)))
+ (when (get-buffer buffername)
+ (kill-buffer buffername))
+ (sql-connect connection buffername)
+ (should (get-buffer buffername))
+ (should (string-equal (with-current-buffer buffername (buffer-string)) ,expected))
+ (when (get-buffer buffername)
+ (kill-buffer buffername))))))
+
+(ert-deftest sql-test-connect ()
+ "Test of basic `sql-connect'."
+ (with-sql-test-connect-harness 1 (user password server database)
+ ((sql-product 'sqltest)
+ (sql-user "aUserName")
+ (sql-password "test-1 aPassword")
+ (sql-server "aServer")
+ (sql-database "aDatabase"))
+ "(sqltest nil \"aUserName\" \"test-1 aPassword\" \"aServer\" \"aDatabase\")\n"))
+
+(ert-deftest sql-test-connect-password-func ()
+ "Test of password function."
+ (with-sql-test-connect-harness 2 (user password server database)
+ ((sql-product 'sqltest)
+ (sql-user "aUserName")
+ (sql-password (lambda () (concat [?t ?e ?s ?t ?- ?2 ?\s
+ ?a ?P ?a ?s ?s ?w ?o ?r ?d])))
+ (sql-server "aServer")
+ (sql-database "aDatabase"))
+ "(sqltest nil \"aUserName\" \"test-2 aPassword\" \"aServer\" \"aDatabase\")\n"))
+
+(ert-deftest sql-test-connect-wallet-server-database ()
+ "Test of password function."
+ (with-sql-test-connect-harness 3 (user password server database)
+ ((sql-product 'sqltest)
+ (sql-user "aUserName")
+ (sql-server "aServer")
+ (sql-database "aDatabase"))
+ "(sqltest nil \"aUserName\" \"netrc-G aPassword\" \"aServer\" \"aDatabase\")\n"))
+
+(ert-deftest sql-test-connect-wallet-database ()
+ "Test of password function."
+ (with-sql-test-connect-harness 4 (user password database)
+ ((sql-product 'sqltest)
+ (sql-user "aUserName")
+ (sql-database "aDatabase"))
+ "(sqltest nil \"aUserName\" \"netrc-E aPassword\" nil \"aDatabase\")\n"))
+
+(ert-deftest sql-test-connect-wallet-server ()
+ "Test of password function."
+ (with-sql-test-connect-harness 5 (user password server)
+ ((sql-product 'sqltest)
+ (sql-user "aUserName")
+ (sql-server "aServer"))
+ "(sqltest nil \"aUserName\" \"netrc-B aPassword\" \"aServer\" nil)\n"))
+
+;;; Set/Get Product Features
+
+(defvar sql-test-feature-value-a nil "Indirect value A.")
+(defvar sql-test-feature-value-b nil "Indirect value B.")
+(defvar sql-test-feature-value-c nil "Indirect value C.")
+(defvar sql-test-feature-value-d nil "Indirect value D.")
+(defmacro sql-test-product-feature-harness (&rest action)
+ "Set-up and tear-down of testing product/feature API.
+
+Perform ACTION and validate results"
+ (declare (indent 2))
+ `(cl-letf
+ ((sql-product-alist
+ (list (list 'a :X 1 :Y 2 :Z 'sql-test-feature-value-a)
+ (list 'b :X 3 :Z 'sql-test-feature-value-b)
+ (list 'c :Y 6 :Z 'sql-test-feature-value-c)
+ (list 'd :X 7 :Y 8 )))
+ (sql-indirect-features '(:Z :W))
+ (sql-test-feature-value-a "original A")
+ (sql-test-feature-value-b "original B")
+ (sql-test-feature-value-c "original C")
+ (sql-test-feature-value-d "original D"))
+ ,@action))
+
+(ert-deftest sql-test-add-product ()
+ "Add a product"
+
+ (sql-test-product-feature-harness
+ (sql-add-product 'xyz "XyzDb")
+
+ (should (equal (pp-to-string (assoc 'xyz sql-product-alist))
+ "(xyz :name \"XyzDb\")\n")))
+
+ (sql-test-product-feature-harness
+ (sql-add-product 'stu "StuDb" :X 1 :Y "2")
+
+ (should (equal (pp-to-string (assoc 'stu sql-product-alist))
+ "(stu :name \"StuDb\" :X 1 :Y \"2\")\n"))))
+
+(ert-deftest sql-test-add-existing-product ()
+ "Add a product that already exists."
+
+ (sql-test-product-feature-harness
+ (should-error (sql-add-product 'a "Aaa"))
+ (should (equal (pp-to-string (assoc 'a sql-product-alist))
+ "(a :X 1 :Y 2 :Z sql-test-feature-value-a)\n"))))
+
+(ert-deftest sql-test-set-feature ()
+ "Add a feature"
+
+ (sql-test-product-feature-harness
+ (sql-set-product-feature 'b :Y 4)
+ (should (equal (pp-to-string (assoc 'b sql-product-alist))
+ "(b :Y 4 :X 3 :Z sql-test-feature-value-b)\n"))))
+
+(ert-deftest sql-test-set-indirect-feature ()
+ "Set a new indirect feature"
+
+ (sql-test-product-feature-harness
+ (sql-set-product-feature 'd :Z 'sql-test-feature-value-d)
+ (should (equal (pp-to-string (assoc 'd sql-product-alist))
+ "(d :Z sql-test-feature-value-d :X 7 :Y 8)\n"))))
+
+(ert-deftest sql-test-set-existing-feature ()
+ "Set an existing feature."
+
+ (sql-test-product-feature-harness
+ (sql-set-product-feature 'b :X 33)
+ (should (equal (pp-to-string (assoc 'b sql-product-alist))
+ "(b :X 33 :Z sql-test-feature-value-b)\n"))))
+
+(ert-deftest sql-test-set-existing-indirect-feature ()
+ "Set an existing indirect feature."
+
+ (sql-test-product-feature-harness
+ (should (equal sql-test-feature-value-b "original B"))
+ (sql-set-product-feature 'b :Z "Hurray!")
+ (should (equal (pp-to-string (assoc 'b sql-product-alist))
+ "(b :X 3 :Z sql-test-feature-value-b)\n")) ;; unchanged
+ (should (equal sql-test-feature-value-b "Hurray!"))))
+
+(ert-deftest sql-test-set-missing-product ()
+ "Add a feature to a missing product."
+
+ (sql-test-product-feature-harness
+ (should-error (sql-set-product-feature 'x :Y 4))
+ (should-not (assoc 'x sql-product-alist))))
+
+(ert-deftest sql-test-get-feature ()
+ "Get a feature value."
+
+ (sql-test-product-feature-harness
+ (should (equal (sql-get-product-feature 'c :Y) 6))))
+
+(ert-deftest sql-test-get-indirect-feature ()
+ "Get a feature indirect value."
+
+ (sql-test-product-feature-harness
+ (should (equal (sql-get-product-feature 'c :Z nil t) 'sql-test-feature-value-c))
+ (should (equal sql-test-feature-value-c "original C"))
+ (should (equal (sql-get-product-feature 'c :Z) "original C"))))
+
+(ert-deftest sql-test-get-missing-product ()
+ "Get a feature value from a missing product."
+
+ (sql-test-product-feature-harness
+ (should-error (sql-get-product-feature 'x :Y))))
+
+(ert-deftest sql-test-get-missing-feature ()
+ "Get a missing feature value."
+
+ (sql-test-product-feature-harness
+ (should-not (sql-get-product-feature 'c :X))))
+
+(ert-deftest sql-test-get-missing-indirect-feature ()
+ "Get a missing indirect feature value."
+
+ (sql-test-product-feature-harness
+ (should-not (sql-get-product-feature 'd :Z))))
+
+;;; SQL Oracle SCAN/DEFINE
+(defmacro sql-tests-placeholder-filter-harness (orig repl outp)
+ "Set-up and tear-down of testing of placeholder filter.
+
+The placeholder in ORIG will be replaced by REPL which should
+yield OUTP."
+
+ (declare (indent 0))
+ `(let ((syntab (syntax-table))
+ (sql-oracle-scan-on t))
+ (set-syntax-table sql-mode-syntax-table)
+
+ (cl-letf
+ (((symbol-function 'read-from-minibuffer)
+ (lambda (&rest _) ,repl)))
+
+ (should (equal (sql-placeholders-filter ,orig) ,outp)))
+
+ (set-syntax-table syntab)))
+
+(ert-deftest sql-tests-placeholder-filter-simple ()
+ "Test that placeholder relacement of simple replacement text."
+ (sql-tests-placeholder-filter-harness
+ "select '&x' from dual;" "XX"
+ "select 'XX' from dual;"))
+
+(ert-deftest sql-tests-placeholder-filter-ampersand ()
+ "Test that placeholder relacement of replacement text with ampersand."
+ (sql-tests-placeholder-filter-harness
+ "select '&x' from dual;" "&Y"
+ "select '&Y' from dual;")
+
+ (sql-tests-placeholder-filter-harness
+ "select '&x' from dual;" "Y&"
+ "select 'Y&' from dual;")
+
+ (sql-tests-placeholder-filter-harness
+ "select '&x' from dual;" "Y&Y"
+ "select 'Y&Y' from dual;"))
+
+(ert-deftest sql-tests-placeholder-filter-period ()
+ "Test that placeholder relacement of token terminated by a period."
+ (sql-tests-placeholder-filter-harness
+ "select '&x.' from dual;" "&Y"
+ "select '&Y' from dual;")
+
+ (sql-tests-placeholder-filter-harness
+ "select '&x.y' from dual;" "&Y"
+ "select '&Yy' from dual;")
+
+ (sql-tests-placeholder-filter-harness
+ "select '&x..y' from dual;" "&Y"
+ "select '&Y.y' from dual;"))
+
+;; Buffer naming
+(defmacro sql-tests-buffer-naming-harness (product &rest action)
+ "Set-up and tear-down of test of buffer naming.
+
+The ACTION will be tested after set-up of PRODUCT."
+
+ (declare (indent 1))
+ `(progn
+ (ert--skip-unless (executable-find sql-sqlite-program))
+ (let (new-bufs)
+ (cl-letf
+ (((symbol-function 'make-comint-in-buffer)
+ (lambda (_name buffer _program &optional _startfile &rest _switches)
+ (let ((b (get-buffer-create buffer)))
+ (message ">>make-comint-in-buffer %S" b)
+ (cl-pushnew b new-bufs) ;; Keep track of what we create
+ b))))
+
+ (let (,(intern (format "sql-%s-login-params" product)))
+ ,@action)
+
+ (let (kill-buffer-query-functions) ;; Kill what we create
+ (mapc #'kill-buffer new-bufs))))))
+
+(ert-deftest sql-tests-buffer-naming-default ()
+ "Test buffer naming."
+ (sql-tests-buffer-naming-harness sqlite
+ (sql-sqlite)
+ (message ">> %S" (current-buffer))
+ (should (equal (buffer-name) "*SQL: SQLite*"))))
+
+(ert-deftest sql-tests-buffer-naming-multiple ()
+ "Test buffer naming of multiple buffers."
+ (sql-tests-buffer-naming-harness sqlite
+ (sql-sqlite)
+ (should (equal (buffer-name) "*SQL: SQLite*"))
+
+ (switch-to-buffer "*scratch*")
+
+ (sql-sqlite)
+ (should (equal (buffer-name) "*SQL: SQLite*"))))
+
+(ert-deftest sql-tests-buffer-naming-explicit ()
+ "Test buffer naming with explicit name."
+ (sql-tests-buffer-naming-harness sqlite
+ (sql-sqlite "A")
+ (should (equal (buffer-name) "*SQL: A*"))
+
+ (switch-to-buffer "*scratch*")
+
+ (sql-sqlite "A")
+ (should (equal (buffer-name) "*SQL: A*"))))
+
+(ert-deftest sql-tests-buffer-naming-universal-argument ()
+ "Test buffer naming with explicit name."
+ (sql-tests-buffer-naming-harness sqlite
+ (cl-letf
+ (((symbol-function 'read-string)
+ (lambda (_prompt &optional _initial-input _history _default-value _inherit-input-method)
+ "1")))
+ (sql-sqlite '(4))
+ (should (equal (buffer-name) "*SQL: 1*")))
+
+ (switch-to-buffer "*scratch*")
+
+ (cl-letf
+ (((symbol-function 'read-string)
+ (lambda (_prompt &optional _initial-input _history _default-value _inherit-input-method)
+ "2")))
+ (sql-sqlite '(16))
+ (should (equal (buffer-name) "*SQL: 2*")))))
+
+(ert-deftest sql-tests-buffer-naming-existing ()
+ "Test buffer naming with an existing non-SQLi buffer."
+ (sql-tests-buffer-naming-harness sqlite
+ (get-buffer-create "*SQL: exist*")
+
+ (cl-letf
+ (((symbol-function 'read-string)
+ (lambda (_prompt &optional _initial-input _history _default-value _inherit-input-method)
+ "exist")))
+ (sql-sqlite '(4))
+ (should (equal (buffer-name) "*SQL: exist-1*")))
+
+ (kill-buffer "*SQL: exist*")))
+
+(ert-deftest sql-tests-comint-automatic-password ()
+ (let ((sql-password nil))
+ (should-not (sql-comint-automatic-password "Password: ")))
+ (let ((sql-password ""))
+ (should-not (sql-comint-automatic-password "Password: ")))
+ (let ((sql-password "password"))
+ (should (equal "password" (sql-comint-automatic-password "Password: "))))
+ ;; Also, we shouldn't care what the password is - we rely on comint for that.
+ (let ((sql-password "password"))
+ (should (equal "password" (sql-comint-automatic-password "")))))
+
+
+
+;; Tests for sql-interactive-remove-continuation-prompt
+
+(defmacro sql-tests-remove-cont-prompts-harness (&rest body)
+ "Set-up and tear-down for tests of
+`sql-interactive-remove-continuation-prompt'."
+ (declare (indent 0))
+ `(let ((comint-prompt-regexp "^ +\\.\\{3\\} ")
+ (sql-output-newline-count nil)
+ (sql-preoutput-hold nil))
+ ,@body
+ (should (null sql-output-newline-count))
+ (should (null sql-preoutput-hold))))
+
+(ert-deftest sql-tests-remove-cont-prompts-pass-through ()
+ "Test that `sql-interactive-remove-continuation-prompt' just
+passes the output line through when it doesn't expect prompts."
+ (sql-tests-remove-cont-prompts-harness
+ (should
+ (equal " ... "
+ (sql-interactive-remove-continuation-prompt
+ " ... ")))))
+
+(ert-deftest sql-tests-remove-cont-prompts-anchored-successive ()
+ "Test that `sql-interactive-remove-continuation-prompt' is able
+to delete multiple prompts (anchored to bol) even if they appear
+in a single line, but not more than `sql-output-newline-count'."
+ (sql-tests-remove-cont-prompts-harness
+ (setq sql-output-newline-count 2)
+ (should
+ (equal
+ ;; 2 of 3 prompts are deleted
+ "some output ... more output...\n\
+ ... \n\
+output after prompt"
+ (sql-interactive-remove-continuation-prompt
+ "some output ... more output...\n\
+ ... ... ... \n\
+output after prompt")))))
+
+(ert-deftest sql-tests-remove-cont-prompts-collect-chunked-output ()
+ "Test that `sql-interactive-remove-continuation-prompt' properly
+collects output when output arrives in chunks, with prompts
+intermixed."
+ (sql-tests-remove-cont-prompts-harness
+ (setq sql-output-newline-count 2)
+
+ ;; Part of first prompt gets held. Complete line is passed
+ ;; through.
+ (should (equal "line1\n"
+ (sql-interactive-remove-continuation-prompt
+ "line1\n ..")))
+ (should (equal " .." sql-preoutput-hold))
+ (should (equal 2 sql-output-newline-count))
+
+ ;; First prompt is complete - remove it. Hold part of line2.
+ (should (equal ""
+ (sql-interactive-remove-continuation-prompt ". li")))
+ (should (equal "li" sql-preoutput-hold))
+ (should (equal 1 sql-output-newline-count))
+
+ ;; Remove second prompt. Flush output & don't hold / process any
+ ;; output further on.
+ (should (equal "line2\nli"
+ (sql-interactive-remove-continuation-prompt "ne2\n ... li")))
+ (should (null sql-preoutput-hold))
+ (should (null sql-output-newline-count))
+ (should (equal "line3\n ... "
+ (sql-interactive-remove-continuation-prompt "line3\n ... ")))))
+
+(ert-deftest sql-tests-remove-cont-prompts-flush-held ()
+ "Test that when we don't wait for prompts,
+ `sql-interactive-remove-continuation-prompt' just 'flushes' held
+ output, with no prompt processing."
+ (sql-tests-remove-cont-prompts-harness
+ (setq sql-preoutput-hold "line1\n ..")
+ (should (equal "line1\n ... line2 .."
+ (sql-interactive-remove-continuation-prompt ". line2 ..")))))
+
(provide 'sql-tests)
;;; sql-tests.el ends here