Skip to content

feat(test): bats-core suite for cdcf_queue_worker.sh (Refs #63)#87

Merged
JohnRDOrazio merged 13 commits into
mainfrom
feat/bats-worker-suite
May 15, 2026
Merged

feat(test): bats-core suite for cdcf_queue_worker.sh (Refs #63)#87
JohnRDOrazio merged 13 commits into
mainfrom
feat/bats-worker-suite

Conversation

@JohnRDOrazio
Copy link
Copy Markdown
Member

@JohnRDOrazio JohnRDOrazio commented May 14, 2026

Third of three planned test suites for #63 (following the Vitest pilot in #86 and parallel to the PHPUnit plan in docs/superpowers/plans/2026-05-02-test-suites-phpunit.md).

Summary

  • bats-core vendored as a git submodule at scripts/tests/bats/, pinned to v1.13.0 (latest stable; the plan suggested v1.11.0 but v1.13.0 is current as of plan execution).
  • Mechanical refactor: helpers extracted from cdcf_queue_worker.sh into a sibling cdcf_queue_worker.lib.sh that the main script sources at startup. No runtime behaviour change — the inner cadence check inside run_daily_tasks was retained alongside the new outer should_run_daily_tasks gate for strict byte-equivalence.
  • Five test files covering 20 cases:
    • in_maintenance.bats (3) — Redis key present / absent / redis-down fallback
    • queue_is_empty.bats (4) — empty / immediate work / delayed work / redis-down
    • parse_processed.bats (5) — flat shape / nested shape / missing key / invalid JSON / empty input
    • should_run_daily_tasks.bats (4) — never-run sentinel / within-interval / exact-boundary / short-interval override
    • process_one.bats (4) — HTTP 200+valid / HTTP 500 with HTML stripped / HTTP 000 connection failure / HTTP 200+invalid JSON
  • PATH-injected shims for redis-cli and curl configured per test via SHIM_* env vars; real python3 for the JSON parsing tests (so the actual parser is exercised, not a mock).
  • Non-blocking CI (continue-on-error: true) gated on changes to scripts/cdcf_queue_worker*, scripts/tests/**, or the workflow itself. Mirrors the gating pattern from test-nextjs.yml.
  • AGENTS.md updated with the bats invocation under "Build & Development Commands"; scripts/tests/README.md documents the shim convention and how to extend the suite.

Plan deviations

  • bats-core v1.13.0 instead of v1.11.0 — checked the submodule's tags at plan-execution time and pinned the latest stable.
  • setup() instead of setup_file() in every .bats file. setup_file() runs in a different process where the sourced lib's functions are not visible to @test blocks (got exit code 127 on first run). Per-test setup() runs in the same shell as the test bodies, so the helpers are reachable. Documented in scripts/tests/README.md.
  • Inner cadence check retained inside run_daily_tasks — the plan suggested moving the cadence check entirely to the main loop, but keeping the original inner guard alongside the new outer should_run_daily_tasks gate is strictly byte-equivalent (the inner check becomes a no-op when the outer gate has already let us through).

Verification

  • scripts/tests/bats/bin/bats scripts/tests/ exits 0 locally; 20/20 green.
  • bash -n clean on both the refactored cdcf_queue_worker.sh and the new cdcf_queue_worker.lib.sh, plus both shims. shellcheck was not available in the execution environment.
  • All function symbols expected by the plan are defined after sourcing the lib: parse_processed, parse_domain_count, run_daily_tasks, should_run_daily_tasks, in_maintenance, queue_is_empty, process_one.

Post-merge step (manual)

The worker isn't deployed via the CI pipeline, so the operator needs to copy both scripts/cdcf_queue_worker.sh and the new sibling scripts/cdcf_queue_worker.lib.sh onto the VPS at /usr/local/bin/, then systemctl restart cdcf-queue-worker. The lib file is new — installing only the main script will leave systemd failing on the source call.

Verify by checking the unit is active and tailing its journal — expect output identical in shape to before (silent on empty queue, periodic transition lines on maintenance flips).

Refs #63. Related: #66 (in_maintenance), #85 (queue_is_empty), #86 (Vitest pilot).

Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added comprehensive test suite for queue worker functionality with documentation on running and writing tests.
  • Tests

    • Integrated bats-core testing framework for bash unit testing.
    • Added unit tests covering queue worker components and operations.
  • Refactor

    • Modularized queue worker code into helper library for improved maintainability and testability.

Review Change Stack

JohnRDOrazio and others added 10 commits May 14, 2026 18:12
Pure-mechanical extraction — no behaviour change. Every function moves
verbatim into a sibling lib file the main script sources at startup.
Two new pure helpers (parse_processed, parse_domain_count) replace
inline python3 -c '...' heredocs that the callers previously embedded.
A new should_run_daily_tasks function replaces the inline cadence
check in the main loop (the original inner cadence check inside
run_daily_tasks is retained for byte-equivalence).

Sets up the helpers for unit testing with bats-core in scripts/tests/.

Refs #63.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refs #63.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refs #63.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cases: redis EXISTS=1 (paused), EXISTS=0 (not paused), redis-cli fails
(safe fall-back to not-paused so a Redis outage does not stall the
worker indefinitely).

Refs #63.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cases: truly empty (ZCARD=0+ZCOUNT=0), immediate work pending
(ZCARD=3), delayed work ready (ZCOUNT=2), redis-cli failure (safe
fall-back to HTTP poll so a Redis outage does not silently mask
backlog).

Uses the shim's multi-call mode (SHIM_REDIS_OUTPUTS + SHIM_REDIS_STATE)
since queue_is_empty issues two ZCARD/ZCOUNT calls in one invocation.

Refs #63.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cases: flat shape ({\"processed\": 5}), nested shape (the actual
production response shape — {\"processed\": {\"processed\": N}}),
missing key (defaults to 0), invalid JSON (\"error\"), empty input
(\"error\").

python3 is intentionally NOT shimmed — the real JSON parser is the
helper's actual dependency and the thing we want to exercise.

Refs #63.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cases: never-run sentinel (LAST_DAILY_RUN=0), within-interval (1s
ago, 24h interval — should not fire), exact-boundary (delta ==
DAILY_INTERVAL — should fire, since the check is >=), short-interval
override (DAILY_INTERVAL=1 with never-run).

Refs #63.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cases: HTTP 200 + valid JSON (\"Processed N job(s)\"), HTTP 500 with
HTML body (\"WARNING: HTTP 500\" with tags stripped), HTTP 000 /
curl-level failure (\"WARNING: request failed\"), HTTP 200 + invalid
JSON (\"WARNING: invalid JSON response\").

curl is PATH-shimmed; python3 is the real binary (so parse_processed
exercises the actual JSON parser in the 200 + valid-JSON case).

Refs #63.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Non-blocking job (continue-on-error: true) gated on changes to
scripts/cdcf_queue_worker*, scripts/tests/**, or this workflow itself.
Mirrors the gating pattern from test-nextjs.yml. Checks out the
bats-core submodule recursively and verifies python3 is available
(parse_processed tests exercise the real JSON parser).

Refs #63.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…Commands" entry

Documents how to run the suite, the shim convention (PATH-injected
fakes configured via SHIM_* env vars), how to add a new test, and how
to bump the pinned bats-core tag.

AGENTS.md / CLAUDE.md (symlink) now lists the bats invocation in the
"Build & Development Commands" block alongside npm test, with a note
that fresh clones need --recurse-submodules.

Refs #63.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

Warning

Rate limit exceeded

@JohnRDOrazio has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 40 minutes and 54 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bf2d9817-1ebb-42ba-abe4-441478583d4b

📥 Commits

Reviewing files that changed from the base of the PR and between 356f5e1 and 436e231.

📒 Files selected for processing (4)
  • AGENTS.md
  • package.json
  • scripts/cdcf_queue_worker.lib.sh
  • scripts/tests/README.md
📝 Walkthrough

Walkthrough

This PR refactors the queue worker script by extracting testable functions into a shared library, adds comprehensive Bash unit tests using Bats, and establishes CI automation. The main behavioral change is conditional gating of daily maintenance tasks.

Changes

Queue Worker Testing and Refactoring

Layer / File(s) Summary
CI workflow and test infrastructure foundation
.github/workflows/test-worker.yml, .gitmodules, scripts/tests/bats, scripts/tests/README.md, AGENTS.md
GitHub Actions workflow (test-worker.yml) runs Bats tests on pull requests and pushes to main when queue worker or test files change. The bats-core test framework is vendored as a git submodule. Documentation guides running individual test files, explains the shim/mocking convention for redis-cli and curl, and describes how to extend the test suite.
Queue worker library extraction and refactoring
scripts/cdcf_queue_worker.lib.sh, scripts/cdcf_queue_worker.sh
Extracts seven core functions from the main worker script into cdcf_queue_worker.lib.sh: JSON parsing (parse_processed, parse_domain_count), daily maintenance scheduling (should_run_daily_tasks, run_daily_tasks), state checks (in_maintenance, queue_is_empty), and request handling (process_one). The main script sources the library and replaces inline implementations. Daily maintenance is now gated by should_run_daily_tasks before execution instead of running unconditionally. Deployment guidance updated to copy the library alongside the worker script.
Test shims and comprehensive test suites
scripts/tests/helpers/shims/curl, scripts/tests/helpers/shims/redis-cli, scripts/tests/parse_processed.bats, scripts/tests/in_maintenance.bats, scripts/tests/queue_is_empty.bats, scripts/tests/process_one.bats, scripts/tests/should_run_daily_tasks.bats
Two environment-variable-driven Bash shims intercept curl and redis-cli calls, enabling deterministic test scenarios without external services or network access. Five test files provide coverage: parse_processed.bats validates JSON extraction (flat/nested objects, defaults, error cases); in_maintenance.bats tests Redis maintenance-flag behavior (key exists, absent, service failure); queue_is_empty.bats simulates Redis sorted-set queries for immediate and delayed job counts; process_one.bats covers HTTP success/failure and JSON parsing outcomes; should_run_daily_tasks.bats validates interval boundary conditions and time-based scheduling.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Test File<br/>(*.bats)
    participant Setup as setup() Hook
    participant Shim as PATH-injected<br/>Shim
    participant Lib as cdcf_queue_worker<br/>.lib.sh
    participant LogOut as stdout<br/>(log/result)

    Test->>Setup: Bats runs setup
    Setup->>Setup: Prepend shims dir to PATH
    Setup->>Setup: Set SHIM_* env vars
    Setup->>Lib: source lib.sh
    Test->>Lib: call library function<br/>(e.g., process_one)
    Lib->>Shim: curl/redis-cli<br/>(mocked via PATH)
    Shim-->>Lib: controlled output/exit code
    Lib->>LogOut: log results
    Lib-->>Test: return status
    Test->>Test: assert stdout/status
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • CatholicOS/cdcf-website#66: Introduces and tests the in_maintenance() function that directly parallels the maintenance-flag behavior extracted in this PR.
  • CatholicOS/cdcf-website#14: Refactors the daily maintenance flow (should_run_daily_tasks, run_daily_tasks, disposable domains endpoint) that this PR extracts and tests.

Poem

🐰 Hop, test, and refactor with glee,
Library functions now testable and free,
Shims mock the network, no Redis to call,
Bats tests it all—the complete coverage haul!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: adding a bats-core test suite for the cdcf_queue_worker.sh script, which aligns with the changeset's main objective.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/bats-worker-suite

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 14, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · 0 duplication

Metric Results
Complexity 0
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@JohnRDOrazio
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
scripts/cdcf_queue_worker.lib.sh (1)

109-115: 💤 Low value

Consider using -eq for numeric comparison.

Line 114 uses string comparison = for an arithmetic result. While this works (arithmetic expansion produces canonical integer strings), -eq is more idiomatic for numeric comparisons in bash.

♻️ More idiomatic numeric comparison
-    [ $((immediate + delayed)) = "0" ]
+    [ $((immediate + delayed)) -eq 0 ]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/cdcf_queue_worker.lib.sh` around lines 109 - 115, In
queue_is_empty(), the final test uses a string comparison ([ $((immediate +
delayed)) = "0" ]) which is non-idiomatic for numbers; change it to a numeric
comparison (e.g., use [ $((immediate + delayed)) -eq 0 ] or (( immediate +
delayed == 0 )) ) so the sum of immediate and delayed is compared as integers;
update the test in the queue_is_empty function accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/tests/README.md`:
- Line 73: The fenced code block in scripts/tests/README.md (the file-layout
example using triple backticks) lacks a language tag and triggers markdownlint
MD040; update the opening fence from ``` to something like ```text (e.g., change
the opening fence that precedes the directory tree to ```text) so the block is
annotated and the linter warning is resolved.

---

Nitpick comments:
In `@scripts/cdcf_queue_worker.lib.sh`:
- Around line 109-115: In queue_is_empty(), the final test uses a string
comparison ([ $((immediate + delayed)) = "0" ]) which is non-idiomatic for
numbers; change it to a numeric comparison (e.g., use [ $((immediate + delayed))
-eq 0 ] or (( immediate + delayed == 0 )) ) so the sum of immediate and delayed
is compared as integers; update the test in the queue_is_empty function
accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2bb47ee0-634c-4adc-a5a9-5a012beb5a2f

📥 Commits

Reviewing files that changed from the base of the PR and between d88fb92 and 356f5e1.

📒 Files selected for processing (14)
  • .github/workflows/test-worker.yml
  • .gitmodules
  • AGENTS.md
  • scripts/cdcf_queue_worker.lib.sh
  • scripts/cdcf_queue_worker.sh
  • scripts/tests/README.md
  • scripts/tests/bats
  • scripts/tests/helpers/shims/curl
  • scripts/tests/helpers/shims/redis-cli
  • scripts/tests/in_maintenance.bats
  • scripts/tests/parse_processed.bats
  • scripts/tests/process_one.bats
  • scripts/tests/queue_is_empty.bats
  • scripts/tests/should_run_daily_tasks.bats

Comment thread scripts/tests/README.md Outdated
JohnRDOrazio and others added 3 commits May 14, 2026 19:31
- scripts/cdcf_queue_worker.lib.sh: queue_is_empty() now uses arithmetic
  evaluation `(( immediate + delayed == 0 ))` instead of the string-form
  `[ $((immediate + delayed)) = "0" ]`. Identical behaviour, idiomatic
  numeric comparison.
- scripts/tests/README.md: file-layout fenced block annotated as `text`
  (was a bare ```), clearing markdownlint MD040.

Bats suite re-run after both edits: 20/20 green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
scripts/tests/bats/ is the upstream bats-core git submodule — its
markdown is the project's, not ours, and we shouldn't reformat or
gate on it. Adding the path to both negative globs (the markdownlint-
cli2 `#...` form and the prettier `!.../**` form) brings the runs
down from a dozen-files-with-warnings to clean across the 18 .md
files this repo actually owns.

Also prettier-formatted scripts/tests/README.md and applied the
markdownlint --fix for MD031 (blanks around fences) on it — that
file was authored before the prettier/markdownlint tooling landed on
main, so it didn't match the current style.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JohnRDOrazio JohnRDOrazio merged commit d8122d0 into main May 15, 2026
13 checks passed
@JohnRDOrazio JohnRDOrazio deleted the feat/bats-worker-suite branch May 15, 2026 00:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant