diff options
| -rw-r--r-- | .localrepo/archive-contents | 8 | ||||
| -rw-r--r-- | .localrepo/dired-sidebar-20250212.629.tar | bin | 51200 -> 0 bytes | |||
| -rw-r--r-- | .localrepo/erc-image-20210604.753.tar | bin | 0 -> 20480 bytes | |||
| -rw-r--r-- | .localrepo/erc-yank-20210220.1815.tar | bin | 0 -> 10240 bytes | |||
| -rw-r--r-- | .localrepo/nerd-icons-dired-20250506.1729.tar | bin | 10240 -> 0 bytes | |||
| -rw-r--r-- | .localrepo/org-msg-4.0.tar | bin | 0 -> 10240 bytes | |||
| -rw-r--r-- | .localrepo/org-web-tools-20231220.1515.tar | bin | 0 -> 51200 bytes | |||
| -rw-r--r-- | .localrepo/plz-0.9.1.tar | bin | 0 -> 204800 bytes | |||
| -rw-r--r-- | .localrepo/wttrin-0.2.3.tar | bin | 0 -> 10240 bytes | |||
| -rw-r--r-- | ai-prompts/quality-engineer.org | 367 | ||||
| -rw-r--r-- | init.el | 11 | ||||
| -rw-r--r-- | modules/custom-buffer-file.el (renamed from modules/custom-file-buffer.el) | 6 | ||||
| -rw-r--r-- | modules/mail-config.el | 4 | ||||
| -rw-r--r-- | modules/org-agenda-config.el | 20 | ||||
| -rw-r--r-- | modules/org-gcal-config.el | 13 | ||||
| -rw-r--r-- | modules/org-roam-config.el | 1 | ||||
| -rw-r--r-- | modules/popper-config.el | 1 | ||||
| -rw-r--r-- | modules/prog-general.el | 8 | ||||
| -rw-r--r-- | modules/selection-framework.el | 25 | ||||
| -rw-r--r-- | modules/system-commands.el | 138 | ||||
| -rw-r--r-- | modules/weather-config.el | 3 | ||||
| -rw-r--r-- | modules/wip.el | 154 | ||||
| -rw-r--r-- | tests/test-custom-buffer-file-clear-to-bottom-of-buffer.el (renamed from tests/test-custom-file-buffer-clear-to-bottom-of-buffer.el) | 10 | ||||
| -rw-r--r-- | tests/test-custom-buffer-file-clear-to-top-of-buffer.el (renamed from tests/test-custom-file-buffer-clear-to-top-of-buffer.el) | 10 | ||||
| -rw-r--r-- | tests/test-custom-buffer-file-copy-link-to-buffer-file.el (renamed from tests/test-custom-file-buffer-copy-link-to-buffer-file.el) | 10 | ||||
| -rw-r--r-- | tests/test-custom-buffer-file-copy-path-to-buffer-file-as-kill.el (renamed from tests/test-custom-file-buffer-copy-path-to-buffer-file-as-kill.el) | 10 | ||||
| -rw-r--r-- | tests/test-custom-buffer-file-copy-whole-buffer.el (renamed from tests/test-custom-file-buffer-copy-whole-buffer.el) | 10 | ||||
| -rw-r--r-- | tests/test-custom-buffer-file-delete-buffer-and-file.el (renamed from tests/test-custom-file-buffer-delete-buffer-and-file.el) | 10 | ||||
| -rw-r--r-- | tests/test-custom-buffer-file-move-buffer-and-file.el (renamed from tests/test-custom-file-buffer-move-buffer-and-file.el) | 10 | ||||
| -rw-r--r-- | tests/test-custom-buffer-file-rename-buffer-and-file.el (renamed from tests/test-custom-file-buffer-rename-buffer-and-file.el) | 10 | ||||
| -rw-r--r-- | tests/test-org-gcal-mock.el | 112 |
31 files changed, 706 insertions, 245 deletions
diff --git a/.localrepo/archive-contents b/.localrepo/archive-contents index d6c4f6b4..c823da50 100644 --- a/.localrepo/archive-contents +++ b/.localrepo/archive-contents @@ -39,7 +39,6 @@ (devdocs . [(0 6 1) ((emacs (27 1))) "Emacs viewer for DevDocs" tar]) (dired-hacks-utils . [(20240629 1906) ((dash (2 5 0)) (emacs (24 3))) "Utilities and helpers for dired-hacks collection." tar]) (dired-hide-dotfiles . [(20240727 1720) ((emacs (25 1))) "Hide dotfiles in dired." tar]) - (dired-sidebar . [(20250212 629) ((emacs (25 1)) (dired-subtree (0 0 1)) (compat (30 0 0 0))) "Tree browser leveraging dired." tar]) (dired-subtree . [(20240629 1859) ((dash (2 5 0)) (dired-hacks-utils (0 0 1)) (emacs (24 3))) "Insert subdirectories in a tree-like fashion." tar]) (dirvish . [(2 3 0) ((emacs (28 1)) (compat (30))) "A modern file manager based on dired mode" tar]) (doom-modeline . [(20250718 1833) ((emacs (25 1)) (compat (30 1 0 0)) (nerd-icons (0 1 0)) (shrink-path (0 3 1))) "A minimal and modern mode-line." tar]) @@ -57,6 +56,8 @@ (emms . [(24) ((cl-lib (0 5)) (nadvice (0 3)) (seq (0))) "The Emacs Multimedia System" tar]) (emojify . [(20210108 1111) ((seq (1 11)) (ht (2 0)) (emacs (24 3))) "Display emojis in Emacs." tar]) (eradio . [(20210327 1000) ((emacs (24 1))) "A simple Internet radio player." tar]) + (erc-image . [(20210604 753) nil "Show received image urls in the ERC buffer." tar]) + (erc-yank . [(20210220 1815) nil "Automagically create a Gist if pasting more than 5 lines." tar]) (eshell-syntax-highlighting . [(20241222 2030) ((emacs (25 1))) "Highlight eshell commands." tar]) (eshell-toggle . [(20250513 1742) ((emacs (25 1)) (dash (2 11 0))) "Show/hide eshell under active window." tar]) (eshell-up . [(20240226 1747) ((emacs (24))) "Quickly go to a specific parent directory in eshell." tar]) @@ -112,7 +113,6 @@ (move-text . [(20231204 1514) nil "Move current line or region with Mdown." tar]) (nerd-icons . [(20250718 355) ((emacs (24 3))) "Emacs Nerd Font Icons Library." tar]) (nerd-icons-completion . [(20250509 1949) ((emacs (25 1)) (nerd-icons (0 0 1)) (compat (30))) "Add icons to completion candidates." tar]) - (nerd-icons-dired . [(20250506 1729) ((emacs (24 4)) (nerd-icons (0 0 1))) "Shows icons for each file in dired mode." tar]) (nerd-icons-ibuffer . [(20250307 958) ((emacs (24 3)) (nerd-icons (0 0 1))) "Display nerd icons in ibuffer." tar]) (noflet . [(20141102 1454) nil "Locally override functions." tar]) (nov . [(20250615 1051) ((esxml (0 3 6)) (emacs (25 1))) "Featureful EPUB reader mode." tar]) @@ -124,8 +124,10 @@ (org-contacts . [(1 1) ((emacs (27 1)) (org (9 3 4))) "Contacts management system for Org Mode" tar]) (org-drill . [(2 7 0) ((emacs (25 3)) (seq (2 14)) (org (9 3)) (persist (0 3))) "Self-testing using spaced repetition" tar]) (org-gcal . [(20250624 1628) ((aio (1 0)) (alert (1 2)) (elnode (20190702 1509)) (emacs (26 1)) (oauth2-auto (20240326 2225)) (org (9 3)) (persist (0 4)) (request (20190901)) (request-deferred (20181129))) "Org sync with Google Calendar." tar]) + (org-msg . [(4 0) ((emacs (24 4)) (htmlize (1 54))) "No description available." tar]) (org-roam . [(20250701 528) ((emacs (26 1)) (dash (2 13)) (org (9 6)) (emacsql (4 1 0)) (magit-section (3 0 0))) "A database abstraction layer for Org-mode." tar]) (org-superstar . [(1 5 1) ((org (9 1 9)) (emacs (26 1))) "Prettify headings and plain lists in Org mode" tar]) + (org-web-tools . [(20231220 1515) ((emacs (27 1)) (org (9 0)) (compat (29 1 4 2)) (dash (2 12)) (esxml (0 3 4)) (s (1 10 0)) (plz (0 7 1)) (request (0 3 0))) "Display and capture web content with Org-mode." tar]) (ox-pandoc . [(20250424 908) ((org (8 2)) (emacs (24 4)) (dash (2 8)) (ht (2 0))) "An Org-mode exporter using pandoc." tar]) (package-build . [(20250708 1908) ((emacs (26 1)) (compat (30 0 0 0))) "Tools for assembling a package archive." tar]) (package-lint . [(0 26) ((emacs (24 4)) (let-alist (1 0 6))) "A linting library for elisp package authors" tar]) @@ -133,6 +135,7 @@ (pdf-tools . [(1 1 0) ((emacs (26 3)) (tablist (1 0)) (let-alist (1 0 4))) "Support library for PDF documents" tar]) (pdf-view-restore . [(20190904 1708) ((pdf-tools (0 90)) (emacs (26 0))) "Support for opening last known pdf position in pdfview mode." tar]) (persist . [(0 6 1) ((emacs (26 1))) "Persist Variables between Emacs Sessions" tar]) + (plz . [(0 9 1) ((emacs (27 1))) "HTTP library" tar]) (poetry . [(20240329 1103) ((transient (0 2 0)) (pyvenv (1 2)) (emacs (25 1))) "Interface to Poetry." tar]) (popper . [(0 4 8) ((emacs (26 1))) "Summon and dismiss buffers as popups" tar]) (popup . [(0 5 9) ((emacs (24 3))) "Visual Popup User Interface" tar]) @@ -176,6 +179,7 @@ (windsize . [(20181029 2257) nil "Simple, intuitive window resizing." tar]) (with-editor . [(3 4 4) ((emacs (26 1)) (compat (30 1))) "Use the Emacsclient as $EDITOR" tar]) (ws-butler . [(1 3) ((emacs (24 1))) "Unobtrusively remove trailing whitespace" tar]) + (wttrin . [(0 2 3) ((emacs (24 4)) (xterm-color (1 0))) "No description available." tar]) (xterm-color . [(20230321 3) ((emacs (24 4))) "ANSI, XTERM 256 and Truecolor support." tar]) (yaml . [(1 2 0) ((emacs (25 1))) "YAML parser for Elisp" tar]) (yaml-mode . [(0 0 16) ((emacs (24 1))) "Major mode for editing YAML files" tar]) diff --git a/.localrepo/dired-sidebar-20250212.629.tar b/.localrepo/dired-sidebar-20250212.629.tar Binary files differdeleted file mode 100644 index e6920db9..00000000 --- a/.localrepo/dired-sidebar-20250212.629.tar +++ /dev/null diff --git a/.localrepo/erc-image-20210604.753.tar b/.localrepo/erc-image-20210604.753.tar Binary files differnew file mode 100644 index 00000000..66c998ac --- /dev/null +++ b/.localrepo/erc-image-20210604.753.tar diff --git a/.localrepo/erc-yank-20210220.1815.tar b/.localrepo/erc-yank-20210220.1815.tar Binary files differnew file mode 100644 index 00000000..74c1b960 --- /dev/null +++ b/.localrepo/erc-yank-20210220.1815.tar diff --git a/.localrepo/nerd-icons-dired-20250506.1729.tar b/.localrepo/nerd-icons-dired-20250506.1729.tar Binary files differdeleted file mode 100644 index 5c6d9b5c..00000000 --- a/.localrepo/nerd-icons-dired-20250506.1729.tar +++ /dev/null diff --git a/.localrepo/org-msg-4.0.tar b/.localrepo/org-msg-4.0.tar Binary files differnew file mode 100644 index 00000000..9df64990 --- /dev/null +++ b/.localrepo/org-msg-4.0.tar diff --git a/.localrepo/org-web-tools-20231220.1515.tar b/.localrepo/org-web-tools-20231220.1515.tar Binary files differnew file mode 100644 index 00000000..c7136aa3 --- /dev/null +++ b/.localrepo/org-web-tools-20231220.1515.tar diff --git a/.localrepo/plz-0.9.1.tar b/.localrepo/plz-0.9.1.tar Binary files differnew file mode 100644 index 00000000..2ff4507b --- /dev/null +++ b/.localrepo/plz-0.9.1.tar diff --git a/.localrepo/wttrin-0.2.3.tar b/.localrepo/wttrin-0.2.3.tar Binary files differnew file mode 100644 index 00000000..9df64990 --- /dev/null +++ b/.localrepo/wttrin-0.2.3.tar diff --git a/ai-prompts/quality-engineer.org b/ai-prompts/quality-engineer.org index 253afc93..d6bb7ecb 100644 --- a/ai-prompts/quality-engineer.org +++ b/ai-prompts/quality-engineer.org @@ -17,11 +17,26 @@ You are an expert software quality engineer specializing in Emacs Lisp testing a - Example: test-org-gcal--safe-substring.el - Tests a single function in isolation with no external dependencies - Focus: All normal, boundary, and error cases for ONE method -- **Integration Tests**: One file per functional area - - Naming: test-integration-<area-name>.el (naming scheme TBD) - - Example: org-gcal-test.el (will be renamed later) +- **Integration Tests**: One file per functional area or workflow + - Naming: test-integration-<area-or-workflow>.el + - Examples: + - test-integration-recurring-events.el (recurring event workflow) + - test-integration-complex-event-formatting.el (multiple formatting functions together) + - test-integration-empty-missing-data.el (edge case handling across functions) + - test-integration-multi-event-sync.el (multiple events interacting) + - test-integration-sync-workflow.el (full fetch → update → push cycle) - Tests multiple components working together - - May involve file I/O, multiple functions, org-mode buffers, etc. + - May involve file I/O, multiple functions, org-mode buffers, API interactions, etc. + - Focus on workflows, component interactions, and end-to-end scenarios + - Good integration test areas: + - Complete user workflows (sync, create, update, delete) + - Complex features involving multiple functions (recurring events, timezone handling) + - Cross-component interactions (org-mode ↔ API ↔ file system) + - Edge cases that span multiple functions (empty data, conflicts, errors) + - Anti-patterns to avoid: + - test-integration-<single-function>.el (too narrow, that's a unit test) + - test-integration-stuff.el (too vague, not descriptive) + - test-integration-1.el (numbered tests are not discoverable) - Test utilities are in testutil-<category>.el files - Analyze and leverage existing test utilities as appropriate @@ -98,13 +113,160 @@ For each test case, provide: - Handle missing dependencies by mocking them before loading the module *** Test Naming -- Use descriptive names: test-<module>-<function>-<scenario>-<expected-result> + +**** Unit Test Naming +- Pattern: test-<module>-<function>-<category>-<scenario>-<expected-result> - Examples: - - test-buffer-kill-undead-buffer-should-bury - test-org-gcal--safe-substring-normal-full-string-returns-string - test-org-gcal--alldayp-boundary-leap-year-returns-true + - test-org-gcal--format-iso2org-error-nil-input-returns-nil +- Category: normal, boundary, or error - Make the test name self-documenting -- Expected result should clarify what the test verifies (returns-true, returns-string, throws-error, etc.) +- Expected result clarifies what the test verifies (returns-true, returns-string, throws-error, etc.) +- Focus: Single function behavior in isolation + +**** Integration Test Naming +- Pattern: test-integration-<area>-<scenario>-<expected-outcome> +- Examples: + - test-integration-recurring-events-preserves-old-timestamps + - test-integration-multi-event-updates-dont-affect-others + - test-integration-sync-workflow-fetch-creates-new-entries + - test-integration-complex-formatting-description-escapes-asterisks + - test-integration-empty-missing-minimal-event-succeeds +- Area: Repeat the integration area from filename for clarity +- Scenario: What situation/workflow is being tested +- Outcome: What should happen across the integrated components +- Focus: Multiple components working together, not single function +- Make the name readable as a sentence describing the integration behavior + +**** Integration Test Docstrings +Integration tests should have more detailed docstrings than unit tests: + +Example structure: +#+begin_src elisp +(ert-deftest test-integration-recurring-events-preserves-old-timestamps () + "Test that recurring events preserve original timestamps across updates. + +When a recurring event is updated with a new instance date from Google Calendar, +the timestamp in the org entry should remain the original series start date, not +jump to the current instance date. + +Components integrated: +- org-gcal--format-event-timestamp (timestamp formatting with recurrence) +- org-gcal--determine-headline (headline selection) +- org-gcal--format-description-for-drawer (description escaping) +- org-gcal--update-entry (entry update orchestration) +- org-element-at-point (org-mode property extraction) + +Validates: +- Recurrence parameter triggers old timestamp preservation +- Old-start/old-end passed through update workflow correctly +- Full workflow: JSON event → parsed data → formatted timestamp → org entry" + ...) +#+end_src + +Docstring requirements: +1. **First line**: Brief summary (< 80 chars) - what is being tested +2. **Context paragraph**: Why this matters, user scenario, or problem being solved +3. **Components integrated**: Explicit list of functions/modules working together + - List each component with brief description of its role + - Include external dependencies (org-mode functions, file I/O, etc.) + - Show the integration boundary (what's real vs mocked) +4. **Validates section**: What specific integration behavior is verified + - Data flow between components + - State changes across function calls + - Error propagation through the system +5. **Optional sections**: + - Edge cases being tested + - Known limitations + - Related integration tests + - Performance considerations + +Why detailed docstrings matter for integration tests: +- Integration failures are harder to debug than unit test failures +- Need to understand which component interaction broke +- Documents the integration contract between components +- Helps maintainers understand system architecture +- Makes test intent clear when test name is necessarily brief + +**CRITICAL**: Always list integrated components in docstrings: +- Explicitly enumerate every function/module being tested together +- Include external dependencies (org-mode, file I/O, parsers) +- Distinguish between what's real and what's mocked +- Show the data flow path through components +- Name the integration boundary points + +Bad docstring (insufficient detail): +#+begin_src elisp +(ert-deftest test-integration-sync-workflow-updates-entries () + "Test that sync updates org entries." + ...) +#+end_src + +Good docstring (lists all components): +#+begin_src elisp +(ert-deftest test-integration-sync-workflow-updates-entries () + "Test that calendar sync workflow updates org entries correctly. + +When user runs org-gcal-sync, events from Google Calendar should be +fetched and org entries updated with new data while preserving local edits. + +Components integrated: +- org-gcal-sync (main entry point) +- org-gcal--get-calendar-events (API fetching) +- org-gcal--json-read (JSON parsing) +- org-gcal--update-entry (entry modification) +- org-gcal--format-event-timestamp (timestamp formatting) +- org-element-at-point (org-mode property reading) +- write-file (persisting changes) + +Validates: +- API response flows correctly through parsing → formatting → updating +- Entry properties are updated while preserving manual edits +- File is saved with correct content and encoding +- Error in one event doesn't break processing of others" + ...) +#+end_src + +Component listing best practices: +1. **Order by call flow**: List components in the order they're called +2. **Group by layer**: API → parsing → business logic → persistence +3. **Include return path**: Don't forget callbacks or response handlers +4. **Note side effects**: File writes, cache updates, state changes +5. **Mark test doubles**: Indicate which components are mocked/stubbed +6. **Show boundaries**: Where does your code end and framework begins? + +Examples of component descriptions: +- ~org-gcal--update-entry (entry orchestration)~ - what it does in this test +- ~org-element-at-point (REAL org-mode function)~ - not mocked +- ~request-deferred (MOCKED, returns test data)~ - test double +- ~file-exists-p → find-file → save-buffer (file I/O chain)~ - flow path +- ~org-gcal--format-iso2org (date conversion, TESTED via integration)~ - tested indirectly + +**** Naming Comparison +Unit tests are narrow and specific: +- test-org-gcal--format-iso2org-error-nil-input-returns-nil + - Tests ONE function with ONE input scenario + - Very granular: specific input → specific output + +Integration tests are broader and scenario-focused: +- test-integration-recurring-events-preserves-old-timestamps + - Tests MULTIPLE functions working together + - Workflow-oriented: describes behavior across components + +**** Naming Checklist +For integration test files: +- [ ] Does the name describe a coherent area/workflow? +- [ ] Is it discoverable with glob test-integration-*.el? +- [ ] Could someone guess what's being tested from the name? +- [ ] Is it distinct from other integration test files? + +For integration test methods: +- [ ] Does it start with test-integration-? +- [ ] Does it include the area from the filename? +- [ ] Can you read it as a sentence? +- [ ] Does it describe both scenario AND expected outcome? +- [ ] Is it specific enough to understand what failed if it breaks? *** Code Coverage - Aim for high coverage of critical paths (80%+ for core functionality) @@ -273,6 +435,197 @@ Example timeline: - Generate appropriate integration test cases for the specific implementation - Consider testing interactions between modules +**** When to Write Integration Tests +Write integration tests when: +- Multiple components must work together (API + parser + file I/O) +- Testing complete user workflows (fetch → update → display → save) +- Complex features span multiple functions (recurring events, timezone handling) +- State management across function calls matters +- Real-world scenarios combine multiple edge cases +- Component boundaries and contracts need validation + +Don't write integration tests when: +- Single function behavior can be fully tested in isolation +- No meaningful interaction between components +- Mocking would remove all real integration logic +- Unit tests already cover the integration paths adequately + +**** What Integration Tests Should Cover +Focus on: +- **Complete workflows**: Full user scenarios from start to finish +- **Component interactions**: How functions call each other and pass data +- **State management**: Data persistence, caching, updates across calls +- **Real dependencies**: Actual file I/O, org-mode buffers, data structures +- **Edge case combinations**: Multiple edge cases interacting together +- **Error propagation**: How errors flow through the system +- **Data integrity**: Events don't interfere, state remains consistent + +Avoid: +- Re-testing individual function logic (that's unit tests) +- Testing framework/library behavior (trust it works) +- Over-mocking that removes actual integration + +**** Integration Test Characteristics +- **Slower** than unit tests (acceptable tradeoff) +- **More setup** required (buffers, files, mock data) +- **Broader scope** than unit tests (multiple functions) +- **Higher value** for catching real-world bugs +- **Less granular** in pinpointing exact failures +- **More realistic** scenarios and data + +**** Integration Test Organization +Structure integration tests by: +1. **Workflow**: test-integration-sync-workflow.el (complete sync cycle) +2. **Feature**: test-integration-recurring-events.el (recurring event handling) +3. **Component interaction**: test-integration-multi-event-sync.el (multiple events) +4. **Edge case category**: test-integration-empty-missing-data.el (nil/empty across system) + +Each test file should: +- Focus on one coherent integration area +- Include setup helpers specific to that area +- Test realistic scenarios, not artificial combinations +- Have clear test names describing the integration behavior +- Include detailed docstrings explaining what's being integrated + +**** Integration Test File Structure +Organize tests within each file using comment headers to group related scenarios: + +#+begin_src elisp +;;; test-integration-recurring-events.el --- Integration tests for recurring events + +;;; Commentary: +;; Integration tests covering the complete recurring event workflow: +;; - Creating recurring events from Google Calendar API +;; - Preserving timestamps across updates +;; - Handling different recurrence patterns (WEEKLY, DAILY, etc.) +;; - Managing recurrence metadata in org properties +;; +;; Components integrated: org-gcal--format-event-timestamp, +;; org-gcal--update-entry, org-element-at-point + +;;; Code: + +(require 'org-gcal) +(require 'ert) + +;; Test data constants +(defconst test-integration-recurring-events-weekly-json ...) +(defconst test-integration-recurring-events-daily-json ...) + +;; Helper functions +(defun test-integration-recurring-events--json-read-string (json) ...) + +;;; Normal Cases - Recurring Event Creation + +(ert-deftest test-integration-recurring-events-weekly-creates-with-recurrence () + "Test that weekly recurring event is created with recurrence property. + +Components integrated: +- org-gcal--update-entry +- org-gcal--format-event-timestamp +- org-element-at-point" + ...) + +(ert-deftest test-integration-recurring-events-daily-creates-with-count () + "Test that daily recurring event with COUNT creates correctly. + +Components integrated: +- org-gcal--update-entry +- org-gcal--format-event-timestamp" + ...) + +;;; Boundary Cases - Recurring Event Updates + +(ert-deftest test-integration-recurring-events-update-preserves-recurrence () + "Test that updating recurring event preserves recurrence property. + +Components integrated: +- org-gcal--update-entry (update path) +- org-entry-get (property retrieval)" + ...) + +(ert-deftest test-integration-recurring-events-preserves-old-timestamps () + "Test that recurring events preserve original timestamps across updates. + +This is the KEY test validating the refactored timestamp logic. + +Components integrated: +- org-gcal--format-event-timestamp (with recurrence parameter) +- org-gcal--update-entry (preserving old-start/old-end) +- Full workflow: JSON → parsed data → formatted timestamp → org entry" + ...) + +;;; Edge Cases - Missing or Invalid Recurrence + +(ert-deftest test-integration-recurring-events-no-recurrence-uses-new-timestamps () + "Test that events without recurrence use new timestamps on update. + +Components integrated: +- org-gcal--format-event-timestamp (no recurrence path) +- org-gcal--update-entry" + ...) + +(provide 'test-integration-recurring-events) +;;; test-integration-recurring-events.el ends here +#+end_src + +File structure guidelines: +1. **Commentary section**: High-level overview of what's being integrated + - List the main workflow or feature + - Enumerate key components being tested together + - Explain the integration scope + +2. **Test data section**: Constants and fixtures + - Group related test data together + - Use descriptive constant names + - Document data format if non-obvious + +3. **Helper functions section**: Test utilities + - Functions used by multiple tests in this file + - Setup/teardown helpers + - Data transformation utilities + +4. **Grouped test sections**: Use comment headers to organize tests + - Start with `;;;` (three semicolons) for section headers + - Group by category: "Normal Cases", "Boundary Cases", "Edge Cases", "Error Cases" + - Or group by scenario: "Event Creation", "Event Updates", "Event Deletion" + - Or group by workflow stage: "Fetch Phase", "Update Phase", "Sync Phase" + +5. **Test ordering**: Organize tests logically + - Simple/common cases first + - Complex scenarios build on earlier tests + - Edge cases at the end + - Easier to understand test intent by reading top to bottom + +6. **Section headers should be discoverable**: + - Use grep-friendly patterns: `^;;;.*Cases` or `^;;; Test:` + - Consistent naming: always use "Normal/Boundary/Error Cases" + - Or use workflow stages consistently across files + +Benefits of grouping: +- Easier to find related tests +- Clear structure when file has 20+ tests +- Documents test coverage patterns +- Helps identify gaps (no error cases section? add some!) +- Makes test maintenance easier +- Improves test file readability + +**** Balancing Unit vs Integration Tests +The testing pyramid: +- **Base (most)**: Unit tests - Fast, isolated, granular +- **Middle**: Integration tests - Realistic, component interactions +- **Top (fewest)**: End-to-end tests - Full system, slowest + +For most projects: +- 70-80% unit tests (individual functions) +- 15-25% integration tests (component interactions) +- 5-10% end-to-end tests (full workflows) + +Don't duplicate coverage: +- If unit tests fully cover logic, integration tests focus on interactions +- If integration test covers a workflow, don't repeat every unit test case +- Integration tests validate unit-tested components work together correctly + *** Test Reviews - Review tests with the same rigor as production code - Check for proper assertions and failure messages @@ -29,7 +29,7 @@ (require 'custom-case) ;; operations for upper/lower/title case (require 'custom-comments) ;; operations with comments (require 'custom-datetime) ;; date/timestamp insertion in various formats -(require 'custom-file-buffer) ;; custom buffer and file operations and keymap +(require 'custom-buffer-file) ;; custom buffer and file operations and keymap (require 'custom-line-paragraph) ;; operations on lines and paragraphs (require 'custom-misc) ;; miscellaneous functions (require 'custom-ordering) ;; ordering and sorting operations @@ -110,7 +110,7 @@ (require 'org-contacts-config) ;; fully integrated org-mode contacts management (require 'org-drill-config) (require 'org-export-config) -(require 'org-gcal-config) +(require 'org-gcal-config) ;; bi directional sync google calendar for org-agenda (require 'org-refile-config) ;; refile org-branches (require 'org-roam-config) ;; personal knowledge management in org mode (require 'org-webclipper) ;; "instapaper" to org-roam workflow @@ -134,7 +134,7 @@ ;; ------------------------- Personal Workflow Related ------------------------- (require 'reconcile-open-repos) -(require 'local-repository) +(require 'local-repository) ;; local repository for easy config portability ;; ------------------------------- Entertainment ------------------------------- @@ -144,8 +144,9 @@ ;; ------------------------------ Modules In Test ------------------------------ ;;(require 'wip) -(require 'lorem-optimum) -(require 'jumper) +(require 'lorem-optimum) ;; best fake latin text generator ever +(require 'jumper) ;; navigation help for large projects/lotsa buffers +(require 'system-commands) ;; reboot, logout, etc. ;; ---------------------------------- Wrap Up ---------------------------------- diff --git a/modules/custom-file-buffer.el b/modules/custom-buffer-file.el index 08f974fd..9438e8ed 100644 --- a/modules/custom-file-buffer.el +++ b/modules/custom-buffer-file.el @@ -1,4 +1,4 @@ -;;; custom-file-buffer.el --- Custom Buffer and File Operations -*- coding: utf-8; lexical-binding: t; -*- +;;; custom-buffer-file.el --- Custom Buffer and File Operations -*- coding: utf-8; lexical-binding: t; -*- ;; ;;; Commentary: ;; This module provides custom buffer and file operations including PostScript @@ -256,5 +256,5 @@ Do not save the deleted text in the kill ring." "C-; b P" "copy file path")) -(provide 'custom-file-buffer) -;;; custom-file-buffer.el ends here. +(provide 'custom-buffer-file) +;;; custom-buffer-file.el ends here. diff --git a/modules/mail-config.el b/modules/mail-config.el index 402c2589..1d5a14ea 100644 --- a/modules/mail-config.el +++ b/modules/mail-config.el @@ -283,9 +283,9 @@ Prompts user for the action when executing." ;; user composes org mode; recipient receives html (use-package org-msg - :ensure nil ;; loading locally for fixes + ;; :vc (:url "https://github.com/cjennings/org-msg" :rev :newest) + :load-path "/home/cjennings/code/org-msg" :defer 1 - :load-path "~/code/org-msg/" :after (org mu4e) :preface (defvar-keymap cj/email-map diff --git a/modules/org-agenda-config.el b/modules/org-agenda-config.el index 7b436424..30f4606c 100644 --- a/modules/org-agenda-config.el +++ b/modules/org-agenda-config.el @@ -273,13 +273,25 @@ This allows a line to show in an agenda without being scheduled or a deadline." ;; This gives two notifications per event without any after-event notifications (setq chime-alert-time '(5 0)) - ;; Modeline display: show upcoming events within 60 minutes + ;; Modeline display: show upcoming events within 2 hours + (setq chime-enable-modeline t) (setq chime-modeline-lookahead 120) (setq chime-modeline-format " ⏰ %s") - ;; Chime sound: plays when notifications appear - (setq chime-play-sound t) - ;; Uses bundled chime.wav by default + ;; Modeline content: show title and countdown only (omit event time) + (setq chime-notification-text-format "%t (%u)") + + ;; Time-until format: compact style like " in 10m" or " in 1h 37m" + (setq chime-time-left-format-short " in %mm") ; Under 1 hour: " in 10m" + (setq chime-time-left-format-long " in %hh %mm") ; 1 hour+: " in 1h 37m" + (setq chime-time-left-format-at-event "right now") + + ;; Title truncation: limit long event titles to 15 characters + ;; This affects only the title, not the icon or countdown + (setq chime-max-title-length 25) ; "Very Long Me... ( in 10m)" + + ;; Chime sound: disabled + (setq chime-play-sound nil) ;; Notification settings (setq chime-notification-title "Reminder") diff --git a/modules/org-gcal-config.el b/modules/org-gcal-config.el index f3e1b7e0..b3c63663 100644 --- a/modules/org-gcal-config.el +++ b/modules/org-gcal-config.el @@ -38,6 +38,11 @@ (require 'host-environment) (require 'user-constants) +;; Forward declare org-gcal internal variables and functions +(eval-when-compile + (defvar org-gcal--sync-lock)) +(declare-function org-gcal-reload-client-id-secret "org-gcal") + (defun cj/org-gcal-clear-sync-lock () "Clear the org-gcal sync lock. Useful when a sync fails and leaves the lock in place, preventing future syncs." @@ -48,7 +53,7 @@ Useful when a sync fails and leaves the lock in place, preventing future syncs." (defun cj/org-gcal-convert-all-to-org-managed () "Convert all org-gcal events in current buffer to Org-managed. -Changes all events with org-gcal-managed property from 'gcal' to 'org', +Changes all events with org-gcal-managed property from `gcal' to `org', enabling bidirectional sync so changes push back to Google Calendar." (interactive) (let ((count 0)) @@ -62,6 +67,7 @@ enabling bidirectional sync so changes push back to Google Calendar." (message "Converted %d event(s) to Org-managed" count))) (use-package org-gcal + :vc (:url "https://github.com/cjennings/org-gcal" :rev :newest) :defer t ;; unless idle timer is set below :bind (("C-; g" . org-gcal-sync) ("C-; G" . cj/org-gcal-clear-sync-lock)) @@ -99,11 +105,6 @@ enabling bidirectional sync so changes push back to Google Calendar." (require 'plstore) (setq plstore-cache-passphrase-for-symmetric-encryption t) - ;; Enable debugging for HTTP requests - (require 'request) - (setq request-log-level 'debug) - (setq request-message-level 'debug) - ;; set org-gcal timezone based on system timezone (setq org-gcal-local-timezone (cj/detect-system-timezone)) diff --git a/modules/org-roam-config.el b/modules/org-roam-config.el index f78b68da..a6b42ce7 100644 --- a/modules/org-roam-config.el +++ b/modules/org-roam-config.el @@ -19,6 +19,7 @@ ;; ---------------------------------- Org Roam --------------------------------- (use-package org-roam + :defer 1 :commands (org-roam-node-find org-roam-node-insert org-roam-db-autosync-mode) :config ;; Enable autosync mode after org-roam loads diff --git a/modules/popper-config.el b/modules/popper-config.el index b0f503e8..d9a9d9b0 100644 --- a/modules/popper-config.el +++ b/modules/popper-config.el @@ -26,6 +26,7 @@ '("\\*Messages\\*" "Output\\*$" "\\*Async Shell Command\\*" + "\\*Async-native-compile-log\\*" help-mode compilation-mode)) (add-to-list 'display-buffer-alist diff --git a/modules/prog-general.el b/modules/prog-general.el index 669922ef..d8d9627d 100644 --- a/modules/prog-general.el +++ b/modules/prog-general.el @@ -264,12 +264,8 @@ If no such file exists there, display a message." ("C-c s n" . yas-new-snippet) ("C-c s e" . yas-visit-snippet-file) :config - (setq yas-snippet-dirs '(snippets-dir))) - -(use-package ivy-yasnippet - :after yasnippet - :bind - ("C-c s i" . ivy-yasnippet)) + (setq yas-snippet-dirs (list snippets-dir)) + (yas-reload-all)) ;; --------------------- Display Color On Color Declaration -------------------- ;; display the actual color as highlight to color hex code diff --git a/modules/selection-framework.el b/modules/selection-framework.el index 0bc71f64..a89afc02 100644 --- a/modules/selection-framework.el +++ b/modules/selection-framework.el @@ -27,7 +27,6 @@ (vertico-resize nil) ; Don't resize the minibuffer (vertico-sort-function #'vertico-sort-history-alpha) ; History first, then alphabetical :bind (:map vertico-map - ;; Match ivy's C-j C-k behavior ("C-j" . vertico-next) ("C-k" . vertico-previous) ("C-l" . vertico-insert) ; Insert current candidate @@ -128,7 +127,7 @@ ;; Use Consult for completion-at-point (setq completion-in-region-function #'consult-completion-in-region)) -(global-unset-key (kbd "C-s")) +;; Override default search with consult-line (keymap-global-set "C-s" #'consult-line) ;; Consult integration with Embark @@ -152,10 +151,10 @@ (use-package orderless :demand t :custom - (completion-styles '(orderless)) + (completion-styles '(orderless basic)) (completion-category-defaults nil) - (completion-category-overrides '((file (styles partial-completion)) - (multi-category (styles orderless)))) + (completion-category-overrides '((file (styles partial-completion orderless basic)) + (multi-category (styles orderless basic)))) (orderless-matching-styles '(orderless-literal orderless-regexp orderless-initialism @@ -183,16 +182,10 @@ nil (window-parameters (mode-line-format . none))))) -;; this typo causes crashes -;; (add-to-list 'display-buffer-alist -;; '("\\=\\*Embark Collect \\(Live\\|Completions\\)\\*" -;; nil -;; (window-parameters (mode-line-format . none))))) - ;; --------------------------- Consult Integration ---------------------------- ;; Additional integrations for specific features -;; Yasnippet integration - replaces ivy-yasnippet +;; Yasnippet integration (use-package consult-yasnippet :after yasnippet :bind ("C-c s i" . consult-yasnippet)) @@ -204,7 +197,7 @@ ("C-c ! c" . consult-flycheck))) ;; ---------------------------------- Company ---------------------------------- -;; In-buffer completion (retained from original configuration) +;; In-buffer completion for text and code (use-package company :demand t @@ -261,7 +254,11 @@ ;; which-key labels (with-eval-after-load 'which-key - (which-key-add-key-based-replacements "C-c h" "consult history")) + (which-key-add-key-based-replacements + "C-c h" "consult history" + "C-c s i" "insert snippet" + "M-g" "goto menu" + "M-s" "search menu")) (provide 'selection-framework) ;;; selection-framework.el ends here diff --git a/modules/system-commands.el b/modules/system-commands.el new file mode 100644 index 00000000..fb8c0611 --- /dev/null +++ b/modules/system-commands.el @@ -0,0 +1,138 @@ +;;; system-commands.el --- System power and session management -*- lexical-binding: t; coding: utf-8; -*- +;; author: Craig Jennings <c@cjennings.net> +;; +;;; Commentary: +;; +;; System commands for logout, lock, suspend, shutdown, reboot, and Emacs +;; exit/restart. Provides both a keymap (C-; !) and a completing-read menu. +;; +;; Commands include: +;; - Logout (terminate user session) +;; - Lock screen (slock) +;; - Suspend (systemctl suspend) +;; - Shutdown (systemctl poweroff) +;; - Reboot (systemctl reboot) +;; - Exit Emacs (kill-emacs) +;; - Restart Emacs (via systemctl --user restart emacs.service) +;; +;; Dangerous commands (logout, suspend, shutdown, reboot) require confirmation. +;; +;;; Code: + +(eval-when-compile (require 'keybindings)) +(eval-when-compile (require 'subr-x)) +(require 'rx) + +;; ------------------------------ System Commands ------------------------------ + +(defun cj/system-cmd--resolve (cmd) + "Return (values symbol-or-nil command-string label) for CMD." + (cond + ((symbolp cmd) + (let ((val (and (boundp cmd) (symbol-value cmd)))) + (unless (and (stringp val) (not (string-empty-p val))) + (user-error "Variable %s is not a non-empty string" cmd)) + (list cmd val (symbol-name cmd)))) + ((stringp cmd) + (let ((s (string-trim cmd))) + (when (string-empty-p s) (user-error "Command string is empty")) + (list nil s "command"))) + (t (user-error "Error: cj/system-cmd expects a string or a symbol")))) + +(defun cj/system-cmd (cmd) + "Run CMD (string or symbol naming a string) detached via the shell. +Shell expansions like $(...) are supported. Output is silenced. +If CMD is deemed dangerous, ask for confirmation." + (interactive (list (read-shell-command "System command: "))) + (pcase-let ((`(,sym ,cmdstr ,label) (cj/system-cmd--resolve cmd))) + (when (and sym (get sym 'cj/system-confirm) + (memq (read-char-choice + (format "Run %s now (%s)? (Y/n) " label cmdstr) + '(?y ?Y ?n ?N ?\r ?\n ?\s)) + '(?n ?N))) + (user-error "Aborted")) + (let ((proc (start-process-shell-command "cj/system-cmd" nil + (format "nohup %s >/dev/null 2>&1 &" cmdstr)))) + (set-process-query-on-exit-flag proc nil) + (set-process-sentinel proc #'ignore) + (message "Running %s..." label)))) + +(defmacro cj/defsystem-command (name var cmdstr &optional confirm) + "Define VAR with CMDSTR and interactive command NAME to run it. +If CONFIRM is non-nil, mark VAR to always require confirmation." + (declare (indent defun)) + `(progn + (defvar ,var ,cmdstr) + ,(when confirm `(put ',var 'cj/system-confirm t)) + (defun ,name () + ,(format "Run %s via `cj/system-cmd'." var) + (interactive) + (cj/system-cmd ',var)))) + +;; Define system commands +(cj/defsystem-command cj/system-cmd-logout logout-cmd "loginctl terminate-user $(whoami)" t) +(cj/defsystem-command cj/system-cmd-lock lockscreen-cmd "slock") +(cj/defsystem-command cj/system-cmd-suspend suspend-cmd "systemctl suspend" t) +(cj/defsystem-command cj/system-cmd-shutdown shutdown-cmd "systemctl poweroff" t) +(cj/defsystem-command cj/system-cmd-reboot reboot-cmd "systemctl reboot" t) + +(defun cj/system-cmd-exit-emacs () + "Exit Emacs server and all clients." + (interactive) + (when (memq (read-char-choice + "Exit Emacs? (Y/n) " + '(?y ?Y ?n ?N ?\r ?\n ?\s)) + '(?n ?N)) + (user-error "Aborted")) + (kill-emacs)) + +(defun cj/system-cmd-restart-emacs () + "Restart Emacs server after saving buffers." + (interactive) + (when (memq (read-char-choice + "Restart Emacs? (Y/n) " + '(?y ?Y ?n ?N ?\r ?\n ?\s)) + '(?n ?N)) + (user-error "Aborted")) + (save-some-buffers) + ;; Start the restart process before killing Emacs + (run-at-time 0.5 nil + (lambda () + (call-process-shell-command + "systemctl --user restart emacs.service && emacsclient -c" + nil 0))) + (run-at-time 1 nil #'kill-emacs) + (message "Restarting Emacs...")) + +(defvar-keymap cj/system-command-map + :doc "Keymap for system commands." + "L" #'cj/system-cmd-logout + "r" #'cj/system-cmd-reboot + "s" #'cj/system-cmd-shutdown + "S" #'cj/system-cmd-suspend + "l" #'cj/system-cmd-lock + "E" #'cj/system-cmd-exit-emacs + "e" #'cj/system-cmd-restart-emacs) +(keymap-set cj/custom-keymap "!" cj/system-command-map) + +(defun cj/system-command-menu () + "Present system commands via \='completing-read\='." + (interactive) + (let* ((commands '(("Logout System" . cj/system-cmd-logout) + ("Lock Screen" . cj/system-cmd-lock) + ("Suspend System" . cj/system-cmd-suspend) + ("Shutdown System" . cj/system-cmd-shutdown) + ("Reboot System" . cj/system-cmd-reboot) + ("Exit Emacs" . cj/system-cmd-exit-emacs) + ("Restart Emacs" . cj/system-cmd-restart-emacs))) + (choice (completing-read "System command: " commands nil t))) + (when-let ((cmd (alist-get choice commands nil nil #'equal))) + (call-interactively cmd)))) + +(keymap-set cj/custom-keymap "!" #'cj/system-command-menu) + +(with-eval-after-load 'which-key + (which-key-add-key-based-replacements "C-; !" "system commands")) + +(provide 'system-commands) +;;; system-commands.el ends here diff --git a/modules/weather-config.el b/modules/weather-config.el index 526a0b41..31fb1b70 100644 --- a/modules/weather-config.el +++ b/modules/weather-config.el @@ -11,9 +11,8 @@ ;; ----------------------------------- Wttrin ---------------------------------- (use-package wttrin + :vc (:url "https://github.com/cjennings/emacs-wttrin" :rev :newest) :defer t - :load-path ("~/code/wttrin") - :ensure nil ;; local package :preface ;; dependency for wttrin (use-package xterm-color diff --git a/modules/wip.el b/modules/wip.el index db94cdb1..93c799fb 100644 --- a/modules/wip.el +++ b/modules/wip.el @@ -14,135 +14,6 @@ ;; ;;; Code: -(eval-when-compile (require 'user-constants)) -(eval-when-compile (require 'keybindings)) -(eval-when-compile (require 'subr-x)) ;; for system commands -(require 'rx) ;; for system commands - -;; ------------------------------ System Commands ------------------------------ - -(defun cj/system-cmd--resolve (cmd) - "Return (values symbol-or-nil command-string label) for CMD." - (cond - ((symbolp cmd) - (let ((val (and (boundp cmd) (symbol-value cmd)))) - (unless (and (stringp val) (not (string-empty-p val))) - (user-error "Variable %s is not a non-empty string" cmd)) - (list cmd val (symbol-name cmd)))) - ((stringp cmd) - (let ((s (string-trim cmd))) - (when (string-empty-p s) (user-error "Command string is empty")) - (list nil s "command"))) - (t (user-error "Error: cj/system-cmd expects a string or a symbol")))) - -(defun cj/system-cmd (cmd) - "Run CMD (string or symbol naming a string) detached via the shell. -Shell expansions like $(...) are supported. Output is silenced. -If CMD is deemed dangerous, ask for confirmation." - (interactive (list (read-shell-command "System command: "))) - (pcase-let ((`(,sym ,cmdstr ,label) (cj/system-cmd--resolve cmd))) - (when (and sym (get sym 'cj/system-confirm) - (memq (read-char-choice - (format "Run %s now (%s)? (Y/n) " label camdstr) - '(?y ?Y ?n ?N ?\r ?\n ?\s)) - '(?n ?N))) - (user-error "Aborted")) - (let ((proc (start-process-shell-command "cj/system-cmd" nil - (format "nohup %s >/dev/null 2>&1 &" cmdstr)))) - (set-process-query-on-exit-flag proc nil) - (set-process-sentinel proc #'ignore) - (message "Running %s..." label)))) - -(defmacro cj/defsystem-command (name var cmdstr &optional confirm) - "Define VAR with CMDSTR and interactive command NAME to run it. -If CONFIRM is non-nil, mark VAR to always require confirmation." - (declare (indent defun)) - `(progn - (defvar ,var ,cmdstr) - ,(when confirm `(put ',var 'cj/system-confirm t)) - (defun ,name () - ,(format "Run %s via `cj/system-cmd'." var) - (interactive) - (cj/system-cmd ',var)))) - -;; Define system commands -(cj/defsystem-command cj/system-cmd-logout logout-cmd "loginctl terminate-user $(whoami)" t) -(cj/defsystem-command cj/system-cmd-lock lockscreen-cmd "slock") -(cj/defsystem-command cj/system-cmd-suspend suspend-cmd "systemctl suspend" t) -(cj/defsystem-command cj/system-cmd-shutdown shutdown-cmd "systemctl poweroff" t) -(cj/defsystem-command cj/system-cmd-reboot reboot-cmd "systemctl reboot" t) - -(defun cj/system-cmd-exit-emacs () - "Exit Emacs server and all clients." - (interactive) - (when (memq (read-char-choice - "Exit Emacs? (Y/n) " - '(?y ?Y ?n ?N ?\r ?\n ?\s)) - '(?n ?N)) - (user-error "Aborted")) - (kill-emacs)) - -(defun cj/system-cmd-restart-emacs () - "Restart Emacs server after saving buffers." - (interactive) - (when (memq (read-char-choice - "Restart Emacs? (Y/n) " - '(?y ?Y ?n ?N ?\r ?\n ?\s)) - '(?n ?N)) - (user-error "Aborted")) - (save-some-buffers) - ;; Start the restart process before killing Emacs - (run-at-time 0.5 nil - (lambda () - (call-process-shell-command - "systemctl --user restart emacs.service && emacsclient -c" - nil 0))) - (run-at-time 1 nil #'kill-emacs) - (message "Restarting Emacs...")) - -;; (defvar-keymap cj/system-command-map -;; :doc "Keymap for system commands." -;; "L" #'cj/system-cmd-logout -;; "r" #'cj/system-cmd-reboot -;; "s" #'cj/system-cmd-shutdown -;; "S" #'cj/system-cmd-suspend -;; "l" #'cj/system-cmd-lock -;; "E" #'cj/system-cmd-exit-emacs -;; "e" #'cj/system-cmd-restart-emacs) -;; (keymap-set cj/custom-keymap "!" cj/system-command-map) - -(defun cj/system-command-menu () - "Present system commands via \='completing-read\='." - (interactive) - (let* ((commands '(("Logout System" . cj/system-cmd-logout) - ("Lock Screen" . cj/system-cmd-lock) - ("Suspend System" . cj/system-cmd-suspend) - ("Shutdown System" . cj/system-cmd-shutdown) - ("Reboot System" . cj/system-cmd-reboot) - ("Exit Emacs" . cj/system-cmd-exit-emacs) - ("Restart Emacs" . cj/system-cmd-restart-emacs))) - (choice (completing-read "System command: " commands nil t))) - (when-let ((cmd (alist-get choice commands nil nil #'equal))) - (call-interactively cmd)))) - -(keymap-set cj/custom-keymap "!" #'cj/system-command-menu) - -(with-eval-after-load 'which-key - (which-key-add-key-based-replacements "C-; !" "system commands")) - -;; --------------------------- Org Upcoming Modeline --------------------------- - -;; (use-package org-upcoming-modeline -;; :after org -;; :load-path "~/code/org-upcoming-modeline/org-upcoming-modeline.el" -;; :config -;; (setq org-upcoming-modeline-keep-late 300) -;; (setq org-upcoming-modeline-ignored-keywords '("DONE" "CANCELLED" "FAILED")) -;; (setq org-upcoming-modeline-trim 30) -;; (setq org-upcoming-modeline-days-ahead 5) -;; (setq org-upcoming-modeline-format (lambda (ms mh) (format "📅 %s %s" ms mh))) -;; (org-upcoming-modeline-mode)) - ;; ----------------------------------- Efrit ----------------------------------- ;; not working as of Wednesday, September 03, 2025 at 12:44:09 AM CDT @@ -185,30 +56,5 @@ If CONFIRM is non-nil, mark VAR to always require confirmation." :bind ("M-p" . pomm) :commands (pomm pomm-third-time)) -;; ----------------------------------- Popper ---------------------------------- - -;; (use-package popper -;; :bind (("C-`" . popper-toggle) -;; ("M-`" . popper-cycle) -;; ("C-M-`" . popper-toggle-type)) -;; :custom -;; (popper-display-control-nil) -;; :init -;; (setq popper-reference-buffers -;; '("\\*Messages\\*" -;; "Output\\*$" -;; "\\*Async Shell Command\\*" -;; ;; "\\*scratch\\*" -;; help-mode -;; compilation-mode)) -;; (add-to-list 'display-buffer-alist -;; '(popper-display-control-p ; Predicate to match popper buffers -;; (display-buffer-in-side-window) -;; (side . bottom) -;; (slot . 0) -;; (window-height . 0.5))) ; Half the frame height -;; (popper-mode +1) -;; (popper-echo-mode +1)) - (provide 'wip) ;;; wip.el ends here. diff --git a/tests/test-custom-file-buffer-clear-to-bottom-of-buffer.el b/tests/test-custom-buffer-file-clear-to-bottom-of-buffer.el index 969f9bb7..bd309880 100644 --- a/tests/test-custom-file-buffer-clear-to-bottom-of-buffer.el +++ b/tests/test-custom-buffer-file-clear-to-bottom-of-buffer.el @@ -1,7 +1,7 @@ -;;; test-custom-file-buffer-clear-to-bottom-of-buffer.el --- Tests for cj/clear-to-bottom-of-buffer -*- lexical-binding: t; -*- +;;; test-custom-buffer-file-clear-to-bottom-of-buffer.el --- Tests for cj/clear-to-bottom-of-buffer -*- lexical-binding: t; -*- ;;; Commentary: -;; Tests for the cj/clear-to-bottom-of-buffer function from custom-file-buffer.el +;; Tests for the cj/clear-to-bottom-of-buffer function from custom-buffer-file.el ;; ;; This function deletes all text from point to the end of the current buffer. ;; It does not save the deleted text in the kill ring. @@ -22,7 +22,7 @@ (provide 'ps-print) ;; Now load the actual production module -(require 'custom-file-buffer) +(require 'custom-buffer-file) ;;; Setup and Teardown @@ -159,5 +159,5 @@ (should-error (cj/clear-to-bottom-of-buffer))) (test-clear-to-bottom-teardown))) -(provide 'test-custom-file-buffer-clear-to-bottom-of-buffer) -;;; test-custom-file-buffer-clear-to-bottom-of-buffer.el ends here +(provide 'test-custom-buffer-file-clear-to-bottom-of-buffer) +;;; test-custom-buffer-file-clear-to-bottom-of-buffer.el ends here diff --git a/tests/test-custom-file-buffer-clear-to-top-of-buffer.el b/tests/test-custom-buffer-file-clear-to-top-of-buffer.el index 18e3f71b..2bf79b27 100644 --- a/tests/test-custom-file-buffer-clear-to-top-of-buffer.el +++ b/tests/test-custom-buffer-file-clear-to-top-of-buffer.el @@ -1,7 +1,7 @@ -;;; test-custom-file-buffer-clear-to-top-of-buffer.el --- Tests for cj/clear-to-top-of-buffer -*- lexical-binding: t; -*- +;;; test-custom-buffer-file-clear-to-top-of-buffer.el --- Tests for cj/clear-to-top-of-buffer -*- lexical-binding: t; -*- ;;; Commentary: -;; Tests for the cj/clear-to-top-of-buffer function from custom-file-buffer.el +;; Tests for the cj/clear-to-top-of-buffer function from custom-buffer-file.el ;; ;; This function deletes all text from point to the beginning of the current buffer. ;; It does not save the deleted text in the kill ring. @@ -22,7 +22,7 @@ (provide 'ps-print) ;; Now load the actual production module -(require 'custom-file-buffer) +(require 'custom-buffer-file) ;;; Setup and Teardown @@ -158,5 +158,5 @@ (should-error (cj/clear-to-top-of-buffer))) (test-clear-to-top-teardown))) -(provide 'test-custom-file-buffer-clear-to-top-of-buffer) -;;; test-custom-file-buffer-clear-to-top-of-buffer.el ends here +(provide 'test-custom-buffer-file-clear-to-top-of-buffer) +;;; test-custom-buffer-file-clear-to-top-of-buffer.el ends here diff --git a/tests/test-custom-file-buffer-copy-link-to-buffer-file.el b/tests/test-custom-buffer-file-copy-link-to-buffer-file.el index 94d1e01e..262968d6 100644 --- a/tests/test-custom-file-buffer-copy-link-to-buffer-file.el +++ b/tests/test-custom-buffer-file-copy-link-to-buffer-file.el @@ -1,7 +1,7 @@ -;;; test-custom-file-buffer-copy-link-to-buffer-file.el --- Tests for cj/copy-link-to-buffer-file -*- lexical-binding: t; -*- +;;; test-custom-buffer-file-copy-link-to-buffer-file.el --- Tests for cj/copy-link-to-buffer-file -*- lexical-binding: t; -*- ;;; Commentary: -;; Tests for the cj/copy-link-to-buffer-file function from custom-file-buffer.el +;; Tests for the cj/copy-link-to-buffer-file function from custom-buffer-file.el ;; ;; This function copies the full file:// path of the current buffer's file to ;; the kill ring. For non-file buffers, it does nothing (no error). @@ -22,7 +22,7 @@ (provide 'ps-print) ;; Now load the actual production module -(require 'custom-file-buffer) +(require 'custom-buffer-file) ;;; Setup and Teardown @@ -205,5 +205,5 @@ (should (null kill-ring)))) (test-copy-link-teardown))) -(provide 'test-custom-file-buffer-copy-link-to-buffer-file) -;;; test-custom-file-buffer-copy-link-to-buffer-file.el ends here +(provide 'test-custom-buffer-file-copy-link-to-buffer-file) +;;; test-custom-buffer-file-copy-link-to-buffer-file.el ends here diff --git a/tests/test-custom-file-buffer-copy-path-to-buffer-file-as-kill.el b/tests/test-custom-buffer-file-copy-path-to-buffer-file-as-kill.el index e7a6f64b..08959a85 100644 --- a/tests/test-custom-file-buffer-copy-path-to-buffer-file-as-kill.el +++ b/tests/test-custom-buffer-file-copy-path-to-buffer-file-as-kill.el @@ -1,7 +1,7 @@ -;;; test-custom-file-buffer-copy-path-to-buffer-file-as-kill.el --- Tests for cj/copy-path-to-buffer-file-as-kill -*- lexical-binding: t; -*- +;;; test-custom-buffer-file-copy-path-to-buffer-file-as-kill.el --- Tests for cj/copy-path-to-buffer-file-as-kill -*- lexical-binding: t; -*- ;;; Commentary: -;; Tests for the cj/copy-path-to-buffer-file-as-kill function from custom-file-buffer.el +;; Tests for the cj/copy-path-to-buffer-file-as-kill function from custom-buffer-file.el ;; ;; This function copies the full path of the current buffer's file to the kill ring ;; and returns the path. It signals an error if the buffer is not visiting a file. @@ -22,7 +22,7 @@ (provide 'ps-print) ;; Now load the actual production module -(require 'custom-file-buffer) +(require 'custom-buffer-file) ;;; Setup and Teardown @@ -201,5 +201,5 @@ (should-error (cj/copy-path-to-buffer-file-as-kill) :type 'user-error)) (test-copy-path-teardown))) -(provide 'test-custom-file-buffer-copy-path-to-buffer-file-as-kill) -;;; test-custom-file-buffer-copy-path-to-buffer-file-as-kill.el ends here +(provide 'test-custom-buffer-file-copy-path-to-buffer-file-as-kill) +;;; test-custom-buffer-file-copy-path-to-buffer-file-as-kill.el ends here diff --git a/tests/test-custom-file-buffer-copy-whole-buffer.el b/tests/test-custom-buffer-file-copy-whole-buffer.el index a0546b18..181c491a 100644 --- a/tests/test-custom-file-buffer-copy-whole-buffer.el +++ b/tests/test-custom-buffer-file-copy-whole-buffer.el @@ -1,7 +1,7 @@ -;;; test-custom-file-buffer-copy-whole-buffer.el --- Tests for cj/copy-whole-buffer -*- lexical-binding: t; -*- +;;; test-custom-buffer-file-copy-whole-buffer.el --- Tests for cj/copy-whole-buffer -*- lexical-binding: t; -*- ;;; Commentary: -;; Tests for the cj/copy-whole-buffer function from custom-file-buffer.el +;; Tests for the cj/copy-whole-buffer function from custom-buffer-file.el ;; ;; This function copies the entire contents of the current buffer to the kill ring. ;; Point and mark are left exactly where they were. No transient region is created. @@ -22,7 +22,7 @@ (provide 'ps-print) ;; Now load the actual production module -(require 'custom-file-buffer) +(require 'custom-buffer-file) ;;; Setup and Teardown @@ -190,5 +190,5 @@ (should (null (text-properties-at 0 (car kill-ring))))) (test-copy-whole-buffer-teardown))) -(provide 'test-custom-file-buffer-copy-whole-buffer) -;;; test-custom-file-buffer-copy-whole-buffer.el ends here +(provide 'test-custom-buffer-file-copy-whole-buffer) +;;; test-custom-buffer-file-copy-whole-buffer.el ends here diff --git a/tests/test-custom-file-buffer-delete-buffer-and-file.el b/tests/test-custom-buffer-file-delete-buffer-and-file.el index 1c43ff3b..4af8d2a7 100644 --- a/tests/test-custom-file-buffer-delete-buffer-and-file.el +++ b/tests/test-custom-buffer-file-delete-buffer-and-file.el @@ -1,7 +1,7 @@ -;;; test-custom-file-buffer-delete-buffer-and-file.el --- Tests for cj/delete-buffer-and-file -*- lexical-binding: t; -*- +;;; test-custom-buffer-file-delete-buffer-and-file.el --- Tests for cj/delete-buffer-and-file -*- lexical-binding: t; -*- ;;; Commentary: -;; Tests for the cj/delete-buffer-and-file function from custom-file-buffer.el +;; Tests for the cj/delete-buffer-and-file function from custom-buffer-file.el ;; ;; This function deletes both the current buffer and the file it visits. ;; It uses vc-delete-file for version-controlled files and delete-file @@ -42,7 +42,7 @@ (provide 'ps-print) ;; Now load the actual production module -(require 'custom-file-buffer) +(require 'custom-buffer-file) ;;; Setup and Teardown @@ -667,5 +667,5 @@ (should-error (cj/delete-buffer-and-file)))) (test-delete-buffer-and-file-teardown))) -(provide 'test-custom-file-buffer-delete-buffer-and-file) -;;; test-custom-file-buffer-delete-buffer-and-file.el ends here +(provide 'test-custom-buffer-file-delete-buffer-and-file) +;;; test-custom-buffer-file-delete-buffer-and-file.el ends here diff --git a/tests/test-custom-file-buffer-move-buffer-and-file.el b/tests/test-custom-buffer-file-move-buffer-and-file.el index 1fc16011..e8f4563d 100644 --- a/tests/test-custom-file-buffer-move-buffer-and-file.el +++ b/tests/test-custom-buffer-file-move-buffer-and-file.el @@ -1,7 +1,7 @@ -;;; test-custom-file-buffer-move-buffer-and-file.el --- Tests for cj/move-buffer-and-file -*- lexical-binding: t; -*- +;;; test-custom-buffer-file-move-buffer-and-file.el --- Tests for cj/move-buffer-and-file -*- lexical-binding: t; -*- ;;; Commentary: -;; Tests for the cj/--move-buffer-and-file function from custom-file-buffer.el +;; Tests for the cj/--move-buffer-and-file function from custom-buffer-file.el ;; ;; This is the internal (non-interactive) implementation that moves both the ;; current buffer and its visited file to a new directory. It handles trailing @@ -25,7 +25,7 @@ (provide 'ps-print) ;; Now load the actual production module -(require 'custom-file-buffer) +(require 'custom-buffer-file) ;;; Setup and Teardown @@ -932,5 +932,5 @@ (kill-buffer (current-buffer))) (test-move-buffer-and-file-teardown))) -(provide 'test-custom-file-buffer-move-buffer-and-file) -;;; test-custom-file-buffer-move-buffer-and-file.el ends here +(provide 'test-custom-buffer-file-move-buffer-and-file) +;;; test-custom-buffer-file-move-buffer-and-file.el ends here diff --git a/tests/test-custom-file-buffer-rename-buffer-and-file.el b/tests/test-custom-buffer-file-rename-buffer-and-file.el index ca8acff8..1eb61f1b 100644 --- a/tests/test-custom-file-buffer-rename-buffer-and-file.el +++ b/tests/test-custom-buffer-file-rename-buffer-and-file.el @@ -1,7 +1,7 @@ -;;; test-custom-file-buffer-rename-buffer-and-file.el --- Tests for cj/--rename-buffer-and-file -*- lexical-binding: t; -*- +;;; test-custom-buffer-file-rename-buffer-and-file.el --- Tests for cj/--rename-buffer-and-file -*- lexical-binding: t; -*- ;;; Commentary: -;; Tests for the cj/--rename-buffer-and-file function from custom-file-buffer.el +;; Tests for the cj/--rename-buffer-and-file function from custom-buffer-file.el ;; ;; This is the internal (non-interactive) implementation that renames both the ;; current buffer and its visited file. The interactive wrapper @@ -24,7 +24,7 @@ (provide 'ps-print) ;; Now load the actual production module -(require 'custom-file-buffer) +(require 'custom-buffer-file) ;;; Setup and Teardown @@ -935,5 +935,5 @@ (kill-buffer (current-buffer))) (test-rename-buffer-and-file-teardown))) -(provide 'test-custom-file-buffer-rename-buffer-and-file) -;;; test-custom-file-buffer-rename-buffer-and-file.el ends here +(provide 'test-custom-buffer-file-rename-buffer-and-file) +;;; test-custom-buffer-file-rename-buffer-and-file.el ends here diff --git a/tests/test-org-gcal-mock.el b/tests/test-org-gcal-mock.el new file mode 100644 index 00000000..4b063867 --- /dev/null +++ b/tests/test-org-gcal-mock.el @@ -0,0 +1,112 @@ +;;; test-org-gcal-mock.el --- Mock test for org-gcal sync -*- lexical-binding: t; -*- + +;;; Commentary: +;; Mock test to capture what org-gcal sends to Google Calendar API +;; This helps debug bidirectional sync issues without hitting the real API + +;;; Code: + +(require 'ert) +(require 'org) + +;; Add modules directory to load path +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) + +;; Load org-gcal (this will require auth, but we'll mock the requests) +(require 'org-gcal-config nil t) + +;; Variables to capture requests +(defvar test-org-gcal-captured-requests nil + "List of captured HTTP requests.") + +(defvar test-org-gcal-captured-url nil + "Last captured URL.") + +(defvar test-org-gcal-captured-type nil + "Last captured HTTP method (GET/POST/PUT/PATCH).") + +(defvar test-org-gcal-captured-data nil + "Last captured request body/data.") + +(defvar test-org-gcal-captured-headers nil + "Last captured request headers.") + +;;; Mock request-deferred to capture what org-gcal sends + +(defun test-org-gcal-mock-request-deferred (url &rest args) + "Mock request-deferred to capture requests instead of sending them. +URL is the API endpoint. ARGS contains :type, :data, :headers, etc." + (let* ((type (plist-get args :type)) + (data (plist-get args :data)) + (headers (plist-get args :headers))) + ;; Capture the request + (setq test-org-gcal-captured-url url) + (setq test-org-gcal-captured-type type) + (setq test-org-gcal-captured-data data) + (setq test-org-gcal-captured-headers headers) + (push (list :url url + :type type + :data data + :headers headers) + test-org-gcal-captured-requests) + + ;; Print for debugging + (message "MOCK REQUEST: %s %s" type url) + (when data + (message "MOCK DATA: %S" data)) + + ;; Return a mock deferred object that succeeds immediately + (require 'deferred) + (deferred:succeed + ;; Mock response with a fake event + (list :data '(:id "test-event-id-123" + :etag "test-etag-456" + :summary "Test Event" + :start (:dateTime "2025-10-28T14:00:00-05:00") + :end (:dateTime "2025-10-28T15:00:00-05:00")) + :status-code 200)))) + +(ert-deftest test-org-gcal-capture-post-request () + "Test capturing what org-gcal sends when posting an event." + ;; Reset captured requests + (setq test-org-gcal-captured-requests nil) + (setq test-org-gcal-captured-url nil) + (setq test-org-gcal-captured-type nil) + (setq test-org-gcal-captured-data nil) + + ;; Mock request-deferred + (cl-letf (((symbol-function 'request-deferred) #'test-org-gcal-mock-request-deferred)) + + ;; Create a test org buffer with an event + (with-temp-buffer + (org-mode) + (insert "* TEST: Mock Sync Test Event\n") + (insert "<2025-10-28 Tue 14:00-15:00>\n") + (insert "\n") + (insert "Test event for mocking.\n") + + ;; Go to the headline + (goto-char (point-min)) + (org-back-to-heading) + + ;; Try to post (this should be captured by our mock) + (condition-case err + (org-gcal-post-at-point) + (error + (message "Error during post: %S" err))))) + + ;; Check what was captured + (should test-org-gcal-captured-requests) + (let ((request (car test-org-gcal-captured-requests))) + (message "Captured URL: %s" (plist-get request :url)) + (message "Captured Type: %s" (plist-get request :type)) + (message "Captured Data: %S" (plist-get request :data)) + + ;; Verify it's trying to POST/PATCH + (should (member (plist-get request :type) '("POST" "PATCH" "PUT"))) + + ;; Verify URL contains calendar API + (should (string-match-p "googleapis.com/calendar" (plist-get request :url))))) + +(provide 'test-org-gcal-mock) +;;; test-org-gcal-mock.el ends here |
