;;; 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