diff options
| -rw-r--r-- | ai-prompts/quality-engineer.org | 114 | ||||
| -rw-r--r-- | modules/org-agenda-config.el | 27 | ||||
| -rw-r--r-- | modules/org-gcal-config.el | 16 | ||||
| -rwxr-xr-x | scripts/delete-elisp-compiled-files.sh | 23 |
4 files changed, 144 insertions, 36 deletions
diff --git a/ai-prompts/quality-engineer.org b/ai-prompts/quality-engineer.org index d6bb7ecb..385dbec1 100644 --- a/ai-prompts/quality-engineer.org +++ b/ai-prompts/quality-engineer.org @@ -638,6 +638,119 @@ Don't duplicate coverage: - Format test cases as clear, numbered lists within each category - Focus on practical, implementable tests that catch real-world bugs +## Test Future-Proofing & Time-Based Testing + +*** Dynamic Timestamp Generation +When tests involve dates and times: +- **Never hardcode dates** in tests (e.g., "2025-10-24") + - Tests will fail when that date passes + - Creates maintenance burden (annual test updates) + - Makes tests brittle and non-deterministic over time + +- **Use dynamic timestamp generation** instead: + - Create test utilities that generate timestamps relative to "now" + - Examples: `test-time-today-at`, `test-time-tomorrow-at`, `test-time-days-from-now` + - Base time can be offset into future to avoid past-date edge cases + +- **Mock time via function substitution**: + - Replace `current-time` with test-controlled time source + - Use macros like `with-test-time` to scope time mocking + - Preserves existing function mocking patterns (don't nest cl-letf*) + +- **Pattern for time-based tests**: + #+begin_src elisp + (let* ((now (test-time-today-at 14 0)) + (event-time (test-time-today-at 14 10)) + (timestamp-str (test-timestamp-string event-time))) + (with-test-time now + (cl-letf (((symbol-function 'other-mock) (lambda () ...))) + (let* ((result (function-under-test event-time))) + (should (equal expected result)))))) + #+end_src + +- **Benefits of dynamic timestamps**: + - Tests work indefinitely without modification + - Time relationships remain consistent (10 minutes = 10 minutes) + - Clearer test intent (relative times vs absolute dates) + - Easier to understand what's being tested + - No test failures from date expiration + +*** Large-Scale Test Refactoring Strategy +When refactoring many test files (e.g., removing hardcoded timestamps): + +**** Strategic Planning +- **Tackle biggest challenges first** - Largest/most complex files + - Eliminates intimidation factor early + - Makes remaining work feel manageable + - Builds confidence and momentum + - Subsequent files feel easier by comparison + +- **Establish consistent patterns early**: + - First few files set the template + - Document patterns in commit messages + - Later files follow established conventions + - Reduces decision fatigue + +- **Identify files that don't need refactoring**: + - Not all timestamp references require changes + - Copyright dates, version numbers: leave alone + - Tests without time dependencies: skip + - Focus effort where it matters + +**** Execution Approach +- **Maintain 100% test pass rate throughout**: + - Run tests after each file refactored + - Never commit broken tests + - Validates refactoring correctness immediately + - Builds trust in the process + +- **Work in batches but commit individually**: + - Refactor similar test patterns together + - But commit each file separately with detailed message + - Makes git history navigable + - Easier to revert specific changes if needed + +- **Use validation infrastructure**: + - Pre-commit hooks catch syntax errors + - Makefile targets (`make validate`, `make test-file`) + - Automated checks prevent regressions + +- **Document patterns in commits**: + - Before/after examples in commit messages + - List of changes made + - Pattern transformations explained + - Future maintainers understand the approach + +**** Project Management Lessons +- **Track progress visibly**: + - Use todo lists to show what's done vs remaining + - Update counts: "16/23 files (70%) complete" + - Provides motivation and clarity + +- **Celebrate milestones**: + - Acknowledge when biggest challenges complete + - Note when majority threshold reached (>50%) + - Recognize systematic progress + +- **Know when you're done**: + - Some files genuinely don't need changes + - Don't force work where it doesn't apply + - Project can complete before all files touched + +**** Real-World Example: chime.el Timestamp Refactoring +Project scope: 23 test files, 339 tests total +- 16 files needed timestamp refactoring (251 tests) +- 7 files had no timestamp dependencies (88 tests) +- Completed over multiple sessions maintaining 100% pass rate +- Strategic approach: Always chose largest remaining file +- Result: Future-proof test suite, tests never expire + +Key insight: **Not all refactoring projects require touching every file** +- Analyze which files actually need work +- Skip files without relevant issues +- Focus effort on high-impact changes +- Don't let perfectionism create unnecessary work + ## Red Flags Watch for and report these issues: @@ -652,3 +765,4 @@ Watch for and report these issues: - Flaky tests that pass/fail intermittently - Tests that are too slow - Tests that require manual setup or verification +- **Hardcoded dates in tests** - Will fail when dates pass, creates maintenance burden diff --git a/modules/org-agenda-config.el b/modules/org-agenda-config.el index 30f4606c..211ff4fe 100644 --- a/modules/org-agenda-config.el +++ b/modules/org-agenda-config.el @@ -54,7 +54,7 @@ (window-height . fit-window-to-buffer))) ;; reset s-left/right each time org-agenda is enabled - (add-hook 'org-agenda-mode-hook (lambda () +s (add-hook 'org-agenda-mode-hook (lambda () (local-set-key (kbd "s-<right>") #'org-agenda-todo-nextset) (local-set-key (kbd "s-<left>") #'org-agenda-todo-previousset))) @@ -68,7 +68,6 @@ (defun cj/add-files-to-org-agenda-files-list (directory) "Search for files named \\='todo.org\\=' add them to org-project-files. - DIRECTORY is a string of the path to begin the search." (interactive "D") (setq org-agenda-files @@ -81,10 +80,9 @@ DIRECTORY is a string of the path to begin the search." ;; agenda targets is the schedule, contacts, project todos, ;; inbox, and org roam projects. (defun cj/build-org-agenda-list () - "Rebuilds the org agenda list without checking org-roam for projects. - + "Rebuilds the org agenda list. Begins with the inbox-file, schedule-file, and contacts-file. -Then adds all todo.org files from projects-dir and code-dir. +Then adds all todo.org files from projects-dir. Reports elapsed time in the messages buffer." (interactive) (let ((start-time (current-time))) @@ -105,7 +103,6 @@ Reports elapsed time in the messages buffer." (defun cj/todo-list-all-agenda-files () "Displays an \\='org-agenda\\=' todo list. - The contents of the agenda will be built from org-project-files and org-roam files that have project in their filetag." (interactive) @@ -118,7 +115,6 @@ files that have project in their filetag." (defun cj/todo-list-from-this-buffer () "Displays an \\='org-agenda\\=' todo list built from the current buffer. - If the current buffer isn't an org buffer, inform the user." (interactive) (if (eq major-mode 'org-mode) @@ -153,7 +149,6 @@ If the current buffer isn't an org buffer, inform the user." (defun cj/org-agenda-skip-subtree-if-not-overdue () "Skip an agenda subtree if it is not an overdue deadline or scheduled task. - An entry is considered overdue if it has a scheduled or deadline date strictly before today, is not marked as done, and is not a habit." (let* ((subtree-end (save-excursion (org-end-of-subtree t))) @@ -176,7 +171,6 @@ before today, is not marked as done, and is not a habit." (defun cj/org-skip-subtree-if-priority (priority) "Skip an agenda subtree if it has a priority of PRIORITY. - PRIORITY may be one of the characters ?A, ?B, or ?C." (let ((subtree-end (save-excursion (org-end-of-subtree t))) (pri-value (* 1000 (- org-lowest-priority priority))) @@ -187,7 +181,6 @@ PRIORITY may be one of the characters ?A, ?B, or ?C." (defun cj/org-skip-subtree-if-keyword (keywords) "Skip an agenda subtree if it has a TODO keyword in KEYWORDS. - KEYWORDS must be a list of strings." (let ((subtree-end (save-excursion (org-end-of-subtree t)))) (if (member (org-get-todo-state) keywords) @@ -224,12 +217,10 @@ KEYWORDS must be a list of strings." (defun cj/main-agenda-display () "Display the main daily org-agenda view. - This uses all org-agenda targets and presents three sections: - All unfinished priority A tasks - Today's schedule, including habits with consistency graphs - All priority B and C unscheduled/undeadlined tasks - The agenda is rebuilt from all sources before display, including: - inbox-file and schedule-file - Org-roam nodes tagged as \"Project\" @@ -263,9 +254,11 @@ This allows a line to show in an agenda without being scheduled or a deadline." ;; Install CHIME from GitHub using use-package :vc (Emacs 29+) (use-package chime - :vc (:url "https://github.com/cjennings/chime.el" :rev :newest) - :after (alert org-agenda) :demand t + ;; :vc (:url "https://github.com/cjennings/chime.el" :rev :newest) ;; using latest on github + :after (alert org-agenda) + :ensure nil ;; using local version + :load-path "~/code/chime.el" :bind ("C-c A" . chime-check) :config @@ -275,9 +268,13 @@ This allows a line to show in an agenda without being scheduled or a deadline." ;; Modeline display: show upcoming events within 2 hours (setq chime-enable-modeline t) - (setq chime-modeline-lookahead 120) + (setq chime-modeline-lookahead 180) (setq chime-modeline-format " ⏰ %s") + ;; Tooltip settings: show up to 20 upcoming events (regardless of how far in future) + ;; chime-modeline-tooltip-lookahead defaults to 525600 (1 year) - effectively unlimited + (setq chime-modeline-tooltip-max-events 20) + ;; Modeline content: show title and countdown only (omit event time) (setq chime-notification-text-format "%t (%u)") diff --git a/modules/org-gcal-config.el b/modules/org-gcal-config.el index b3c63663..0c309a0e 100644 --- a/modules/org-gcal-config.el +++ b/modules/org-gcal-config.el @@ -109,7 +109,21 @@ enabling bidirectional sync so changes push back to Google Calendar." (setq org-gcal-local-timezone (cj/detect-system-timezone)) ;; Reload client credentials (should already be loaded by org-gcal, but ensure it's set) - (org-gcal-reload-client-id-secret)) + (org-gcal-reload-client-id-secret) + + ;; Auto-save gcal files after sync completes + (defun cj/org-gcal-save-files-after-sync (&rest _) + "Save all org-gcal files after sync completes." + (dolist (entry org-gcal-fetch-file-alist) + (let* ((file (cdr entry)) + (buffer (get-file-buffer file))) + (when (and buffer (buffer-modified-p buffer)) + (with-current-buffer buffer + (save-buffer) + (message "Saved %s after org-gcal sync" (file-name-nondirectory file))))))) + + ;; Advise org-gcal--sync-unlock which is called when sync completes + (advice-add 'org-gcal--sync-unlock :after #'cj/org-gcal-save-files-after-sync)) ;; Set up automatic initial sync on boot with error handling ;;(run-with-idle-timer diff --git a/scripts/delete-elisp-compiled-files.sh b/scripts/delete-elisp-compiled-files.sh index b64a6540..5976c92b 100755 --- a/scripts/delete-elisp-compiled-files.sh +++ b/scripts/delete-elisp-compiled-files.sh @@ -2,23 +2,6 @@ location=$HOME/.emacs.d/ -echo ""; echo "You are about to delete emacs lisp compiled files (.eln and .elc) recursively from $location"; - -# Show the files it will delete -echo "The following files will be deleted:" -find $location -type f \( -name "*.eln" -o -name "*.elc" \) -print - - -echo ""; echo "" -read -p "Are you sure you want to continue? (y/n) " -n 1 -r -echo # move to a new line -if [[ $REPLY =~ ^[Yy]$ ]] -then - echo "Deleting files..." - find $location -type f \( -name "*.eln" -o -name "*.elc" \) -exec rm -f {} + - echo "Files deleted." -else - echo "Operation cancelled." -fi - -echo ""; echo "" +echo "Deleting emacs lisp compiled files (.eln and .elc) from $location..." +find $location -type f \( -name "*.eln" -o -name "*.elc" \) -exec rm -f {} + +echo "Done." |
