branch: externals/org-gnosis
commit 19363adaed437d330adb95ba564193f77ea1b392
Author: Thanos Apollo <[email protected]>
Commit: Thanos Apollo <[email protected]>
tests: Add org-gnosis-test-db.
---
tests/org-gnosis-test-db.el | 523 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 523 insertions(+)
diff --git a/tests/org-gnosis-test-db.el b/tests/org-gnosis-test-db.el
new file mode 100644
index 0000000000..2802e149e4
--- /dev/null
+++ b/tests/org-gnosis-test-db.el
@@ -0,0 +1,523 @@
+;;; org-gnosis-test-db.el --- Tests for org-gnosis database layer -*-
lexical-binding: t; -*-
+
+;; Copyright (C) 2026 Free Software Foundation, Inc.
+
+;; Author: Thanos Apollo <[email protected]>
+;; Keywords: tests
+
+;; This program 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.
+
+;;; Commentary:
+
+;; Tests for database init, CRUD, tags, links, sync, file naming,
+;; journal, and org-gnosis-mode hook behavior.
+
+;;; Code:
+
+(require 'ert)
+(require 'org-element)
+
+;; Load the main org-gnosis.el file
+(let ((gnosis-file (expand-file-name "../org-gnosis.el"
+ (file-name-directory (or load-file-name
buffer-file-name)))))
+ (if (file-exists-p gnosis-file)
+ (load gnosis-file)
+ (error "Could not find org-gnosis.el at %s" gnosis-file)))
+
+;;; ---- Test infrastructure (reused from test-sync.el) ----
+
+(defvar org-gnosis-test-temp-dir nil
+ "Temporary directory for test files.")
+
+(defun org-gnosis-test-setup ()
+ "Set up test environment with temporary directory and database."
+ (setq org-gnosis-test-temp-dir (make-temp-file "org-gnosis-test" t))
+ (setq org-gnosis-dir org-gnosis-test-temp-dir)
+ (setq org-gnosis-journal-dir (expand-file-name "journal"
org-gnosis-test-temp-dir))
+ (setq org-gnosis-journal-file (expand-file-name "journal.org"
org-gnosis-journal-dir))
+ (setq org-gnosis-database-file (expand-file-name "test.db"
org-gnosis-test-temp-dir))
+ (make-directory org-gnosis-journal-dir t)
+ ;; Close any existing connection
+ (when (boundp 'org-gnosis-db--connection)
+ (when (and org-gnosis-db--connection
+ (emacsql-live-p org-gnosis-db--connection))
+ (emacsql-close org-gnosis-db--connection))
+ (setq org-gnosis-db--connection nil))
+ ;; Delete database file if it exists
+ (when (file-exists-p org-gnosis-database-file)
+ (delete-file org-gnosis-database-file)))
+
+(defun org-gnosis-test-teardown ()
+ "Clean up test environment."
+ (when (boundp 'org-gnosis-db--connection)
+ (when (and org-gnosis-db--connection
+ (emacsql-live-p org-gnosis-db--connection))
+ (emacsql-close org-gnosis-db--connection))
+ (setq org-gnosis-db--connection nil))
+ (when (and org-gnosis-test-temp-dir
+ (file-exists-p org-gnosis-test-temp-dir))
+ (delete-directory org-gnosis-test-temp-dir t)))
+
+(defun org-gnosis-test-create-test-file (filename content)
+ "Create a test file FILENAME with CONTENT in test directory."
+ (let ((filepath (expand-file-name filename org-gnosis-test-temp-dir)))
+ (with-temp-file filepath
+ (insert content))
+ filepath))
+
+(defun org-gnosis-test-create-journal-file (filename content)
+ "Create a journal test file FILENAME with CONTENT in journal directory."
+ (let ((filepath (expand-file-name filename org-gnosis-journal-dir)))
+ (with-temp-file filepath
+ (insert content))
+ filepath))
+
+(defun org-gnosis-test-db-tables ()
+ "Return list of user table names in current database."
+ (mapcar #'car
+ (emacsql (org-gnosis-db-get)
+ [:select name :from sqlite-master
+ :where (and (= type 'table)
+ (not-like name "sqlite_%"))])))
+
+(defun org-gnosis-test-db-indexes ()
+ "Return list of index names in current database."
+ (mapcar #'car
+ (emacsql (org-gnosis-db-get)
+ [:select name :from sqlite-master
+ :where (= type 'index)])))
+
+;;; ---- Group 1: Database Init & Schema ----
+
+(ert-deftest org-gnosis-test-db-init-creates-all-tables ()
+ "Verify all 5 tables exist after init."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (progn
+ (org-gnosis-db-init)
+ (let ((tables (org-gnosis-test-db-tables)))
+ (should (member 'nodes tables))
+ (should (member 'tags tables))
+ (should (member 'journal tables))
+ ;; emacsql converts hyphens to underscores in SQLite
+ (should (member 'node_tag tables))
+ (should (member 'links tables))
+ (should (= 5 (length tables)))))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-db-init-creates-indexes ()
+ "Verify idx-nodes-file and idx-journal-file indexes exist."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (progn
+ (org-gnosis-db-init)
+ (let ((indexes (org-gnosis-test-db-indexes)))
+ (should (member 'idx_nodes_file indexes))
+ (should (member 'idx_journal_file indexes))))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-db-version-set-correctly ()
+ "Verify pragma user-version matches `org-gnosis-db-version' after sync."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (progn
+ ;; db-sync sets user-version (db-init alone uses unquoted symbol)
+ (org-gnosis-db-sync 'force)
+ (let ((version (caar (emacsql (org-gnosis-db-get) [:pragma
user-version]))))
+ (should (= version org-gnosis-db-version))))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-force-sync-preserves-indexes ()
+ "Force sync then verify indexes still exist."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (progn
+ ;; Create a file so sync has something to process
+ (org-gnosis-test-create-test-file
+ "20260101120000--test.org"
+ ":PROPERTIES:\n:ID: idx-test\n:END:\n#+title: Index Test\n")
+ (org-gnosis-db-sync 'force)
+ (let ((indexes (org-gnosis-test-db-indexes)))
+ (should (member 'idx_nodes_file indexes))
+ (should (member 'idx_journal_file indexes))))
+ (org-gnosis-test-teardown)))
+
+;;; ---- Group 2: Database CRUD ----
+
+(ert-deftest org-gnosis-test-insert-and-select-node ()
+ "Create file, sync, verify node data via select."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (progn
+ (org-gnosis-test-create-test-file
+ "20260101120000--hello.org"
+ ":PROPERTIES:\n:ID: node-hello\n:END:\n#+title: Hello
World\n#+filetags: :test:\n\nBody text\n")
+ (org-gnosis-db-sync 'force)
+ (let ((title (car (org-gnosis-select 'title 'nodes '(= id
"node-hello") t)))
+ (file (car (org-gnosis-select 'file 'nodes '(= id "node-hello")
t))))
+ (should (string= title "Hello World"))
+ (should (string= file "20260101120000--hello.org"))))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-insert-multiple-nodes ()
+ "File with topic + 2 headings, verify 3 nodes + master relationships."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (progn
+ (org-gnosis-test-create-test-file
+ "20260101120000--multi.org"
+ ":PROPERTIES:\n:ID: multi-topic\n:END:\n#+title: Multi\n\n* Heading
One\n:PROPERTIES:\n:ID: multi-h1\n:END:\n\n* Heading Two\n:PROPERTIES:\n:ID:
multi-h2\n:END:\n")
+ (org-gnosis-db-sync 'force)
+ ;; 3 nodes total
+ (let ((nodes (org-gnosis-select 'id 'nodes '(like file "%multi%") t)))
+ (should (= 3 (length nodes))))
+ ;; Topic is level 0
+ (let ((level (car (org-gnosis-select 'level 'nodes '(= id
"multi-topic") t))))
+ (should (equal level 0)))
+ ;; Children link back to topic via links table
+ (let ((child-links (org-gnosis-select 'dest 'links '(= source
"multi-h1") t)))
+ (should (member "multi-topic" child-links))))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-delete-file-with-full-path ()
+ "Sync file, delete via full path, verify gone."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (let ((filepath (org-gnosis-test-create-test-file
+ "20260101120000--delme.org"
+ ":PROPERTIES:\n:ID: del-full\n:END:\n#+title: Delete
Me\n")))
+ (org-gnosis-db-sync 'force)
+ (should (org-gnosis-select 'id 'nodes '(= id "del-full")))
+ ;; Delete using full path
+ (org-gnosis--delete-file filepath)
+ (should-not (org-gnosis-select 'id 'nodes '(= id "del-full"))))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-delete-file-with-basename ()
+ "Sync file, delete via basename, verify gone."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (progn
+ (org-gnosis-test-create-test-file
+ "20260101120000--delbase.org"
+ ":PROPERTIES:\n:ID: del-base\n:END:\n#+title: Delete Base\n")
+ (org-gnosis-db-sync 'force)
+ (should (org-gnosis-select 'id 'nodes '(= id "del-base")))
+ ;; Delete using basename only
+ (org-gnosis--delete-file "20260101120000--delbase.org")
+ (should-not (org-gnosis-select 'id 'nodes '(= id "del-base"))))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-update-file-replaces-data ()
+ "Sync, modify file content, update, verify new title."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (let ((filepath (org-gnosis-test-create-test-file
+ "20260101120000--upd.org"
+ ":PROPERTIES:\n:ID: upd-node\n:END:\n#+title: Original
Title\n")))
+ (org-gnosis-db-sync 'force)
+ (should (string= "Original Title"
+ (car (org-gnosis-select 'title 'nodes '(= id
"upd-node") t))))
+ ;; Overwrite file with new title
+ (with-temp-file filepath
+ (insert ":PROPERTIES:\n:ID: upd-node\n:END:\n#+title: Updated
Title\n"))
+ (org-gnosis-update-file filepath)
+ (should (string= "Updated Title"
+ (car (org-gnosis-select 'title 'nodes '(= id
"upd-node") t)))))
+ (org-gnosis-test-teardown)))
+
+;;; ---- Group 3: Tags ----
+
+(ert-deftest org-gnosis-test-tags-inserted-on-sync ()
+ "Sync file with filetags, verify tags + node-tag tables."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (progn
+ (org-gnosis-test-create-test-file
+ "20260101120000--tagged.org"
+ ":PROPERTIES:\n:ID: tagged-node\n:END:\n#+title: Tagged\n#+filetags:
:emacs:lisp:\n")
+ (org-gnosis-db-sync 'force)
+ ;; Tags table should have both tags
+ (let ((tags (org-gnosis-select 'tag 'tags nil t)))
+ (should (member "emacs" tags))
+ (should (member "lisp" tags)))
+ ;; node-tag should link node to both tags
+ (let ((node-tags (org-gnosis-select 'tag 'node-tag '(= node-id
"tagged-node") t)))
+ (should (member "emacs" node-tags))
+ (should (member "lisp" node-tags))))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-tags-no-duplicates ()
+ "Two files share a tag, verify tags table has one row for it."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (progn
+ (org-gnosis-test-create-test-file
+ "20260101120000--dup1.org"
+ ":PROPERTIES:\n:ID: dup1\n:END:\n#+title: Dup1\n#+filetags:
:shared:\n")
+ (org-gnosis-test-create-test-file
+ "20260101120001--dup2.org"
+ ":PROPERTIES:\n:ID: dup2\n:END:\n#+title: Dup2\n#+filetags:
:shared:\n")
+ (org-gnosis-db-sync 'force)
+ (let ((shared-count (length (org-gnosis-select 'tag 'tags '(= tag
"shared")))))
+ (should (= 1 shared-count))))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-orphaned-tags-cleanup ()
+ "Delete file, cleanup, verify orphaned tags removed."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (let ((filepath (org-gnosis-test-create-test-file
+ "20260101120000--orphan.org"
+ ":PROPERTIES:\n:ID: orphan-node\n:END:\n#+title:
Orphan\n#+filetags: :lonely:\n")))
+ (org-gnosis-db-sync 'force)
+ (should (org-gnosis-select 'tag 'tags '(= tag "lonely")))
+ ;; Delete the file's data from DB
+ (org-gnosis--delete-file filepath)
+ ;; node-tag entries are cascade-deleted, but tags table still has the
tag
+ ;; Cleanup should remove it
+ (org-gnosis-tags--cleanup-orphaned)
+ (should-not (org-gnosis-select 'tag 'tags '(= tag "lonely"))))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-orphaned-cleanup-preserves-shared-tags ()
+ "Delete one file, verify shared tags survive cleanup."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (let ((filepath1 (org-gnosis-test-create-test-file
+ "20260101120000--sh1.org"
+ ":PROPERTIES:\n:ID: sh1\n:END:\n#+title:
Shared1\n#+filetags: :common:\n"))
+ (_filepath2 (org-gnosis-test-create-test-file
+ "20260101120001--sh2.org"
+ ":PROPERTIES:\n:ID: sh2\n:END:\n#+title:
Shared2\n#+filetags: :common:\n")))
+ (org-gnosis-db-sync 'force)
+ ;; Delete first file only
+ (org-gnosis--delete-file filepath1)
+ (org-gnosis-tags--cleanup-orphaned)
+ ;; Tag should survive because sh2 still uses it
+ (should (org-gnosis-select 'tag 'tags '(= tag "common"))))
+ (org-gnosis-test-teardown)))
+
+;;; ---- Group 4: Links ----
+
+(ert-deftest org-gnosis-test-links-inserted-on-sync ()
+ "File with id link, verify links table."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (progn
+ ;; Create target node first
+ (org-gnosis-test-create-test-file
+ "20260101120000--target.org"
+ ":PROPERTIES:\n:ID: link-target\n:END:\n#+title: Target\n")
+ ;; Create source node that links to target
+ (org-gnosis-test-create-test-file
+ "20260101120001--source.org"
+ ":PROPERTIES:\n:ID: link-source\n:END:\n#+title: Source\n\nSee
[[id:link-target][Target]]\n")
+ (org-gnosis-db-sync 'force)
+ (let ((links (org-gnosis-select '[source dest] 'links
+ '(and (= source "link-source")
+ (= dest "link-target")))))
+ (should (= 1 (length links)))))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-master-links-inserted ()
+ "Child heading creates master link in links table."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (progn
+ (org-gnosis-test-create-test-file
+ "20260101120000--parent.org"
+ ":PROPERTIES:\n:ID: parent-node\n:END:\n#+title: Parent\n\n*
Child\n:PROPERTIES:\n:ID: child-node\n:END:\n")
+ (org-gnosis-db-sync 'force)
+ ;; Child should have a link to parent (master relationship)
+ (let ((links (org-gnosis-select 'dest 'links '(= source "child-node")
t)))
+ (should (member "parent-node" links))))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-links-cascade-on-node-delete ()
+ "Delete node, verify its links are cascade-deleted."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (progn
+ (org-gnosis-test-create-test-file
+ "20260101120000--cascade.org"
+ ":PROPERTIES:\n:ID: cascade-parent\n:END:\n#+title: Cascade\n\n*
Child\n:PROPERTIES:\n:ID: cascade-child\n:END:\n")
+ (org-gnosis-db-sync 'force)
+ ;; Verify link exists
+ (should (org-gnosis-select 'dest 'links '(= source "cascade-child")))
+ ;; Delete the file from DB
+ (org-gnosis--delete-file "20260101120000--cascade.org")
+ ;; Links from cascade-child should be gone (cascade delete)
+ (should-not (org-gnosis-select 'dest 'links '(= source
"cascade-child"))))
+ (org-gnosis-test-teardown)))
+
+;;; ---- Group 5: Sync & Change Detection ----
+
+(ert-deftest org-gnosis-test-file-changed-p-new-file ()
+ "File not in DB returns non-nil."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (let ((filepath (org-gnosis-test-create-test-file
+ "20260101120000--new.org"
+ ":PROPERTIES:\n:ID: new-node\n:END:\n#+title: New\n")))
+ (org-gnosis-db-init)
+ (should (org-gnosis--file-changed-p filepath 'nodes)))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-file-changed-p-unchanged ()
+ "Synced file returns nil."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (let ((filepath (org-gnosis-test-create-test-file
+ "20260101120000--unchanged.org"
+ ":PROPERTIES:\n:ID: unchanged\n:END:\n#+title:
Unchanged\n")))
+ (org-gnosis-db-sync 'force)
+ (should-not (org-gnosis--file-changed-p filepath 'nodes)))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-file-changed-p-content-changed ()
+ "Modified file returns non-nil."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (let ((filepath (org-gnosis-test-create-test-file
+ "20260101120000--changed.org"
+ ":PROPERTIES:\n:ID: changed\n:END:\n#+title:
Before\n")))
+ (org-gnosis-db-sync 'force)
+ ;; Modify the file
+ (with-temp-file filepath
+ (insert ":PROPERTIES:\n:ID: changed\n:END:\n#+title: After\n"))
+ ;; Set DB mtime to old value so mtime check triggers hash comparison
+ (emacsql (org-gnosis-db-get)
+ [:update nodes :set (= mtime "0") :where (= id "changed")])
+ (should (org-gnosis--file-changed-p filepath 'nodes)))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-file-changed-p-mtime-only ()
+ "Touched file with same content returns nil (two-tier check).
+We simulate this by updating the DB mtime to an old value while keeping
+the hash correct, then checking with actual file (same content, newer mtime)."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (let ((filepath (org-gnosis-test-create-test-file
+ "20260101120000--touch.org"
+ ":PROPERTIES:\n:ID: touch-node\n:END:\n#+title:
Touch\n")))
+ (org-gnosis-db-sync 'force)
+ ;; Manually set DB mtime to an old value (hash stays correct)
+ (emacsql (org-gnosis-db-get)
+ [:update nodes :set (= mtime "0") :where (= id "touch-node")])
+ ;; File hasn't changed content, only mtime differs
+ ;; The two-tier check: mtime differs -> check hash -> hash matches ->
nil
+ (should-not (org-gnosis--file-changed-p filepath 'nodes)))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-force-sync-resyncs-all ()
+ "Force sync repopulates all nodes."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (progn
+ (org-gnosis-test-create-test-file
+ "20260101120000--resync1.org"
+ ":PROPERTIES:\n:ID: resync1\n:END:\n#+title: Resync1\n")
+ (org-gnosis-test-create-test-file
+ "20260101120001--resync2.org"
+ ":PROPERTIES:\n:ID: resync2\n:END:\n#+title: Resync2\n")
+ (org-gnosis-db-sync 'force)
+ (should (= 2 (length (org-gnosis-select 'id 'nodes nil t))))
+ ;; Force sync again — should still have both nodes
+ (org-gnosis-db-sync 'force)
+ (should (= 2 (length (org-gnosis-select 'id 'nodes nil t)))))
+ (org-gnosis-test-teardown)))
+
+;;; ---- Group 6: File Naming ----
+
+(ert-deftest org-gnosis-test-create-name-format ()
+ "Verify timestamp + underscores + .org format."
+ (let ((name (org-gnosis--create-name "My Test Note")))
+ (should (string-match-p "^[0-9]\\{14\\}--My_Test_Note\\.org$" name))))
+
+(ert-deftest org-gnosis-test-create-name-gpg ()
+ "Verify .org.gpg suffix when gpg is requested."
+ (let ((name (org-gnosis--create-name "Secret Note" nil t)))
+ (should (string-match-p "\\.org\\.gpg$" name))))
+
+(ert-deftest org-gnosis-test-create-name-strips-hash ()
+ "Verify # removed from filenames."
+ (let ((name (org-gnosis--create-name "C# Programming")))
+ (should-not (string-match-p "#" name))
+ (should (string-match-p "C_Programming\\.org$" name))))
+
+(ert-deftest org-gnosis-test-journal-file-path ()
+ "Verify default journal file path contains journal.org not jounral.org."
+ ;; Test the default value computation
+ (let* ((test-dir "/tmp/test-gnosis-notes")
+ (journal-dir (expand-file-name "journal" test-dir))
+ (journal-file (expand-file-name "journal.org" journal-dir)))
+ (should (string-match-p "journal\\.org$" journal-file))
+ (should-not (string-match-p "jounral" journal-file))))
+
+;;; ---- Group 7: Journal ----
+
+(ert-deftest org-gnosis-test-journal-file-syncs-to-journal-table ()
+ "Journal file goes in journal table, not nodes."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (progn
+ (org-gnosis-test-create-journal-file
+ "20260101120000--journal-entry.org"
+ ":PROPERTIES:\n:ID: j-entry\n:END:\n#+title: 2026-01-01\n")
+ (org-gnosis-db-sync 'force)
+ ;; Should be in journal table
+ (should (org-gnosis-select 'id 'journal '(= id "j-entry")))
+ ;; Should NOT be in nodes table
+ (should-not (org-gnosis-select 'id 'nodes '(= id "j-entry"))))
+ (org-gnosis-test-teardown)))
+
+(ert-deftest org-gnosis-test-journal-delete ()
+ "Delete journal entry from DB."
+ (org-gnosis-test-setup)
+ (unwind-protect
+ (let ((filepath (org-gnosis-test-create-journal-file
+ "20260101120000--jdel.org"
+ ":PROPERTIES:\n:ID: j-del\n:END:\n#+title:
2026-01-01\n")))
+ (org-gnosis-db-sync 'force)
+ (should (org-gnosis-select 'id 'journal '(= id "j-del")))
+ (org-gnosis--delete-file filepath)
+ (should-not (org-gnosis-select 'id 'journal '(= id "j-del"))))
+ (org-gnosis-test-teardown)))
+
+;;; ---- Group 8: org-gnosis-mode Hook ----
+
+(ert-deftest org-gnosis-test-mode-adds-local-hook ()
+ "Enable mode, verify after-save-hook is buffer-local."
+ (with-temp-buffer
+ (org-gnosis-mode 1)
+ (should (memq #'org-gnosis-update-file
+ (buffer-local-value 'after-save-hook (current-buffer))))
+ (org-gnosis-mode -1)))
+
+(ert-deftest org-gnosis-test-mode-removes-local-hook ()
+ "Disable mode, verify hook removed only from that buffer."
+ (let (buf1 buf2)
+ (unwind-protect
+ (progn
+ (setq buf1 (generate-new-buffer "*gnosis-hook-test-1*"))
+ (setq buf2 (generate-new-buffer "*gnosis-hook-test-2*"))
+ ;; Enable in both buffers
+ (with-current-buffer buf1 (org-gnosis-mode 1))
+ (with-current-buffer buf2 (org-gnosis-mode 1))
+ ;; Disable in buf1 only
+ (with-current-buffer buf1 (org-gnosis-mode -1))
+ ;; buf1 should not have the hook
+ (should-not (memq #'org-gnosis-update-file
+ (buffer-local-value 'after-save-hook buf1)))
+ ;; buf2 should still have it
+ (should (memq #'org-gnosis-update-file
+ (buffer-local-value 'after-save-hook buf2))))
+ (when (buffer-live-p buf1) (kill-buffer buf1))
+ (when (buffer-live-p buf2) (kill-buffer buf2)))))
+
+(provide 'org-gnosis-test-db)
+;;; org-gnosis-test-db.el ends here