Skip to content

feat: API client with non-blocking CI integration (Story 1.4)#3

Merged
maximn merged 1 commit into
mainfrom
feat/1-4-action-api-client-and-non-blocking-integration
Mar 6, 2026
Merged

feat: API client with non-blocking CI integration (Story 1.4)#3
maximn merged 1 commit into
mainfrom
feat/1-4-action-api-client-and-non-blocking-integration

Conversation

@maximn
Copy link
Copy Markdown
Contributor

@maximn maximn commented Mar 6, 2026

Summary

  • Add HTTP API client (src/api/client.ts) that POSTs parsed test results to POST /api/v1/runs with Bearer token auth, exponential backoff retry (3 attempts: 1s, 2s delays), and 10s AbortController timeout
  • Add format auto-detection (src/utils/detect-format.ts) from file extension (.xml → JUnit, .json → CTRF) with fallback to trying both parsers
  • Add non-blocking error handlers (src/utils/errors.ts) — all errors use core.warning(), never core.setFailed() (FR5 compliance)
  • Wire full pipeline in src/index.ts: read file → detect format → parse → send to API → log result
  • Update CLAUDE.md with current architecture tree and remove completed stories from upcoming list

Test plan

  • 18 API client unit tests (success, auth errors, retryable errors, network failures, timeout, retry exhaustion)
  • 9 format detection tests (extensions, case sensitivity, complex paths, edge cases)
  • 10 error handler tests (correct warning messages, setFailed never called)
  • 21 integration tests (full run() pipeline: happy paths, all error scenarios, format detection, explicit override, AC7 guarantee)
  • All 127 tests pass (58 new + 69 existing)
  • pnpm build compiles cleanly
  • pnpm lint passes
  • grep confirms core.setFailed absent from all source files
  • Test suite optimized: 18.3s → 526ms via proper fake timer usage

Add HTTP API client that POSTs parsed test results to the TestGlance
API with exponential backoff retry (3 attempts), format auto-detection,
and comprehensive error handling. The Action never calls core.setFailed()
ensuring CI pipelines are never broken by TestGlance failures.

- src/api/client.ts: HTTP client with retry logic, AbortController timeout
- src/utils/detect-format.ts: file extension-based format detection
- src/utils/errors.ts: non-blocking warning handlers (core.warning only)
- src/index.ts: full pipeline wiring (read → detect → parse → send)
- 58 new tests (127 total), all passing
- Test suite optimized from 18.3s to 526ms via proper fake timers
- CLAUDE.md updated with new architecture tree
@maximn maximn merged commit 958c6a5 into main Mar 6, 2026
maximn added a commit that referenced this pull request Apr 28, 2026
Handlebars.compile() defers parsing until render time, so without the eager
parse() call, syntax errors in user templates surface as "render error"
warnings instead of "parse error" warnings. The spec for story 8.3 requires
the "parse error in <path>: <reason>" format for compile-time failures
(AC #3).

The previous code-review labelled this call "redundant" but it is load-bearing.
maximn added a commit that referenced this pull request Apr 29, 2026
Handlebars.compile() defers parsing until render time, so without the eager
parse() call, syntax errors in user templates surface as "render error"
warnings instead of "parse error" warnings. The spec for story 8.3 requires
the "parse error in <path>: <reason>" format for compile-time failures
(AC #3).

The previous code-review labelled this call "redundant" but it is load-bearing.
maximn added a commit that referenced this pull request Apr 29, 2026
* feat: add custom Handlebars templates for CI summary and PR comments

Introduces two new optional inputs that let users replace the default
rendering of the CI Job Summary and the PR comment body with their own
Handlebars templates:

- summary-template: replaces the default CI summary
- comment-template: replaces the default per-job PR comment body

Both inputs share a single TemplateContext shape (results, failures,
slowest, suites, history, delta, flaky, trends, perfRegression, meta)
built once via buildTemplateContext, so the mental model is identical
across surfaces. Compiled templates are cached by absolute path.

Render errors (missing file, parse error, runtime error) are caught and
surfaced via core.warning, and the Action falls back to the default
rendering — exit code remains 0 (non-blocking, per ANFR1). PR-comment
custom output is wrapped in the existing per-job markers so multi-job
merging continues to work.

Helpers registered: formatDuration, truncate, escapeHtml, passRate.

Closes story 8.3.

* docs: keep handlebars examples readable with prettier-ignore

* fix(templates): harden custom-template renderer and wire missing context

Code-review follow-up for story 8.3.

Security:
- Reject template paths that resolve outside GITHUB_WORKSPACE (lexical and via realpath)
- Reject symlinks pointing outside the workspace
- Reject paths containing newline characters (log-injection vector)
- Reject non-regular files and files larger than 1 MB
- Disable Handlebars prototype-property/method access at render time
- Use an isolated Handlebars.create() instance so helpers cannot leak across processes

Correctness:
- Plumb loadedHistory.entries through to both surfaces so {{#each history}} actually populates
- Pass slowestLimit to the PR-comment buildTemplateContext call so summary and comment match
- Strip embedded <!-- tj:* --> markers from rendered comment bodies before wrapping
- Use lastIndexOf for the closing marker in mergeTestJobSection (defense-in-depth)
- Fall back to "tests" when the sanitized job-name marker key is empty
- Wrap generateSummary and postPrComment in try/catch in index.ts so renderer errors
  cannot break CI
- Cap rendered summary at ~900K and rendered comment body at ~60K to fit GitHub limits
- Trim summary-template/comment-template inputs so whitespace-only values are treated as unset
- Strip leading UTF-8 BOM after reading template files
- Key the compile cache on (path, mtimeMs, size) so edits invalidate stale entries
- Drop the redundant Handlebars.parse() call before compile()

Docs:
- Replace the broken @index_lt_25 example with a working {{#each (limit … N)}} example
- Add a "limit" helper to the registered set
- Document the {{{triple-stash}}} risk on failures.* fields with adversarial test names
- Document the workspace-containment and 1 MB sandboxing rules

Tests:
- Add coverage for path-traversal rejection, newline rejection, symlink escape, prototype
  access denial, and history flattening
- Switch helper-registration and compile-spy tests to use an internal instance hook
- Add coverage for marker-injection scrubbing and empty-jobName fallback
- Set GITHUB_WORKSPACE per-test where templates are rendered
- Remove unused runtime-error.hbs fixture

* fix(templates): restore eager Handlebars.parse for syntax-error warnings

Handlebars.compile() defers parsing until render time, so without the eager
parse() call, syntax errors in user templates surface as "render error"
warnings instead of "parse error" warnings. The spec for story 8.3 requires
the "parse error in <path>: <reason>" format for compile-time failures
(AC #3).

The previous code-review labelled this call "redundant" but it is load-bearing.

* chore: rebuild dist
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