etaf-db/tests/etaf-sqlite-tests.el
Kinneyzhang 299b9ff877 feat(sqlite): add concrete data source
Adapt Emacs SQLite to the core ETAF Data Source contract with typed schema validation, safe queries, transactions, pagination, mutations, tests, and bilingual usage documentation.
2026-08-05 06:59:23 +08:00

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