Adapt Emacs SQLite to the core ETAF Data Source contract with typed schema validation, safe queries, transactions, pagination, mutations, tests, and bilingual usage documentation.
83 lines
3.4 KiB
EmacsLisp
83 lines
3.4 KiB
EmacsLisp
;;; etaf-sqlite-tests.el --- ETAF SQLite source tests -*- lexical-binding: t; -*-
|
|
|
|
(require 'ert)
|
|
(require 'etaf-sqlite)
|
|
|
|
(defun etaf-sqlite-test--database (file)
|
|
"Return a small typed test DATABASE at FILE."
|
|
(etaf-sqlite-database
|
|
file
|
|
(etaf-sqlite-table
|
|
'items
|
|
(list
|
|
(etaf-sqlite-column :id "id" :type 'integer :primary t)
|
|
(etaf-sqlite-column :name "name" :type 'text)
|
|
(etaf-sqlite-column :score "score" :type 'integer))
|
|
:id)))
|
|
|
|
(cl-defmacro etaf-sqlite-test--with-database ((database) &rest body)
|
|
"Create a temporary DATABASE while evaluating BODY."
|
|
(declare (indent 1))
|
|
`(let* ((directory (make-temp-file "etaf-sqlite-" t))
|
|
(file (expand-file-name "data.sqlite" directory))
|
|
(,database (etaf-sqlite-test--database file)))
|
|
(unwind-protect
|
|
(progn
|
|
(etaf-sqlite-initialize ,database)
|
|
(let ((mutate (plist-get (etaf-sqlite-source ,database) :mutate)))
|
|
(funcall mutate 'insert '(:id 1 :name "Ada" :score 10))
|
|
(funcall mutate 'insert '(:id 2 :name "Grace" :score 20)))
|
|
,@body)
|
|
(when (file-exists-p directory)
|
|
(delete-directory directory t)))))
|
|
|
|
(ert-deftest etaf-sqlite-schema-rejects-unsafe-identifiers ()
|
|
"Identifiers are validated before they can reach SQL text."
|
|
(should-error (etaf-sqlite-column :id "id; DROP TABLE items")
|
|
:type 'etaf-sqlite-schema-error)
|
|
(should-error
|
|
(etaf-sqlite-table
|
|
'items (list (etaf-sqlite-column :id "id")) :missing)
|
|
:type 'etaf-sqlite-schema-error))
|
|
|
|
(ert-deftest etaf-sqlite-source-loads-and-filters-pages ()
|
|
"The source should implement ETAF Data's load capability."
|
|
(etaf-sqlite-test--with-database (database)
|
|
(let* ((source (etaf-sqlite-source database))
|
|
(result (funcall (plist-get source :load) nil 1 1))
|
|
(filtered (funcall (plist-get source :load)
|
|
'(:name "Grace") 1 10)))
|
|
(should (= (plist-get result :total) 2))
|
|
(should (= (length (plist-get result :items)) 1))
|
|
(should (equal (plist-get (car (plist-get result :items)) :name)
|
|
"Ada"))
|
|
(should (= (plist-get filtered :total) 1)))))
|
|
|
|
(ert-deftest etaf-sqlite-source-mutations-are-visible-to-data-controller ()
|
|
"Insert, update, and delete should share the Data source contract."
|
|
(etaf-sqlite-test--with-database (database)
|
|
(let ((controller (etaf-data-controller (etaf-sqlite-source database)
|
|
:page-size 20 :auto-load t)))
|
|
(unwind-protect
|
|
(progn
|
|
(etaf-data-mutate controller 'insert
|
|
'(:id 3 :name "Alan" :score 30))
|
|
(should (= (etaf-value (etaf-data-total controller)) 3))
|
|
(etaf-data-mutate controller 'update
|
|
'(:id 3 :name "Alan Turing" :score 31))
|
|
(should (string= (plist-get
|
|
(car (cl-remove-if-not
|
|
(lambda (row)
|
|
(= (plist-get row :id) 3))
|
|
(etaf-value
|
|
(etaf-data-items controller))))
|
|
:name)
|
|
"Alan Turing"))
|
|
(etaf-data-mutate controller 'delete 3)
|
|
(should (= (etaf-value (etaf-data-total controller)) 2)))
|
|
(etaf-data-stop controller)))))
|
|
|
|
(provide 'etaf-sqlite-tests)
|
|
|
|
;;; etaf-sqlite-tests.el ends here
|