Skip to content

[jsweep] Clean update_handler_factory.cjs#39019

Merged
pelikhan merged 2 commits into
mainfrom
signed/jsweep/update-handler-factory-2026-06-13-6dea25fb76945083
Jun 13, 2026
Merged

[jsweep] Clean update_handler_factory.cjs#39019
pelikhan merged 2 commits into
mainfrom
signed/jsweep/update-handler-factory-2026-06-13-6dea25fb76945083

Conversation

@github-actions

@github-actions github-actions Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Cleans up update_handler_factory.cjs and expands its test coverage with a dedicated control-flow describe block.

Changes

actions/setup/js/update_handler_factory.cjs

  • Refactored config-parts log message construction: replaced an imperative forEach loop with a declarative array spread using filter/map over Object.entries(additionalConfig).

actions/setup/js/update_handler_factory.test.cjs

  • Added a new handler control flow describe block containing five test cases:
    • staged-mode short-circuit
    • skipped-update propagation
    • deferred-flag passthrough from resolveItemNumber
    • itemFilter blocking execution
    • itemFilter passing through to executeUpdate

Impact Assessment

File Change type Breaking Impact
update_handler_factory.cjs Modified (refactor) No Low
update_handler_factory.test.cjs Modified (tests) No Low

Notes

  • No behavioural changes to production logic — the forEach→spread refactor is purely stylistic/declarative.
  • The five new test cases address review feedback and improve coverage of control-flow branches that were previously untested.
  • No API surface changes; safe to merge without downstream coordination.

Generated by PR Description Updater for issue #39019 · 91.8 AIC · ⌖ 12.6 AIC · ⊞ 19.8K ·

- Split long destructuring onto multiple lines for readability
- Replace forEach configParts building with spread + filter + map
- Break long withRetry call and ternary metadata captures across multiple lines
- Add 5 new tests: staged mode, buildUpdateData skipped, deferred resolution,
  itemFilter skip, itemFilter proceed (34 tests total, up from 29)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@pelikhan pelikhan marked this pull request as ready for review June 13, 2026 10:16
Copilot AI review requested due to automatic review settings June 13, 2026 10:16
@github-actions

github-actions Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

🧪 Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #39019 does not have the 'implementation' label and has 0 new lines of code in business logic directories (≤100 threshold). Neither enforcement condition is met.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR performs a small readability refactor in the shared JavaScript update handler factory and extends its Vitest suite with additional coverage for previously untested control-flow branches.

Changes:

  • Refactors configParts construction in createUpdateHandlerFactory from an imperative forEach + push to a declarative filter + map spread.
  • Adds new tests for staged mode, buildUpdateData(...).skipped, deferred number resolution, and itemFilter early-return behavior.
Show a summary per file
File Description
actions/setup/js/update_handler_factory.cjs Refactors configuration logging assembly (configParts) for readability.
actions/setup/js/update_handler_factory.test.cjs Adds tests covering staged/skipped/deferred/itemFilter branches.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 2

Comment on lines +810 to +814
it("should return staged result when config.staged is true", async () => {
const mockExecuteUpdate = vi.fn();

const handlerFactory = factoryModule.createUpdateHandlerFactory({
itemType: "update_test",
Comment thread actions/setup/js/update_handler_factory.test.cjs
@github-actions github-actions Bot mentioned this pull request Jun 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor Author

🧪 Test Quality Sentinel Report

Test Quality Score: 84/100 — Excellent

Analyzed 5 test(s): 5 design, 0 implementation, 0 guideline violation(s).

📊 Metrics & Test Classification (5 tests analyzed)
Metric Value
New/modified tests analyzed 5
✅ Design tests (behavioral contracts) 5 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 4 (80%)
Duplicate test clusters 0
Test inflation detected ⚠️ Yes (127 test lines / 7 prod lines ≈ 18:1 — contextually justified; see note below)
🚨 Coding-guideline violations 0

Test Classification Details

Test File Classification Notes
should return staged result when config.staged is true actions/setup/js/update_handler_factory.test.cjs:~808 ✅ Design Verifies staged-mode short-circuit contract; guards executeUpdate not called
should return skipped result when buildUpdateData returns skipped:true actions/setup/js/update_handler_factory.test.cjs:~831 ✅ Design Verifies skip-signal propagation contract; result shape + side-effect guard
should propagate deferred flag from resolveItemNumber actions/setup/js/update_handler_factory.test.cjs:~856 ✅ Design Verifies deferred error path; result.deferred, result.error + side-effect guard
should call itemFilter and skip update when filter returns a result actions/setup/js/update_handler_factory.test.cjs:~882 ✅ Design Verifies filter veto contract; observable result shape + executeUpdate suppressed
should call itemFilter and proceed when filter returns null actions/setup/js/update_handler_factory.test.cjs:~912 ✅ Design Happy-path complement to filter veto test; confirms pass-through allows execution

Language Support

Tests analyzed:

  • 🐹 Go (*_test.go): 0 tests
  • 🟨 JavaScript (*.test.cjs): 5 tests (vitest)
i️ Test Inflation Context (not flagged as a concern)

The metric shows a ~18:1 ratio (127 test lines added vs. 7 production lines added), which mechanically triggers the inflation penalty (−10 pts). However, the production change is a small jsweep refactoring — converting a forEach/push pattern to a spread + filter + map chain — while the tests add 5 new test cases for previously untested scenarios (staged mode, skipped propagation, deferred resolution, itemFilter veto, itemFilter pass-through). This is gap-filling coverage, not redundant inflation.

The 10-point deduction reflects the mechanical metric, not an actual quality concern.

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). All 5 tests verify observable behavioral contracts of createUpdateHandlerFactory.

📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

🧪 Test quality analysis by Test Quality Sentinel · 140.7 AIC · ⌖ 19.7 AIC · ⊞ 27.2K ·

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 84/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%). All 5 new tests verify behavioral contracts of createUpdateHandlerFactory.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Non-blocking observations on the 5 new tests and 1 source change. No correctness bugs found — the forEachfilter+map+spread refactor is functionally equivalent. Four test-quality gaps noted inline.

### Summary of findings

Source change (update_handler_factory.cjs):
The compact functional rewrite is clean and correct. One observation: the refactor makes it less obvious that additionalConfig values are ignored (only its keys are whitelisted against config). See inline comment.

New tests (update_handler_factory.test.cjs):
All five new tests cover real production paths that lacked coverage (staged, deferred, skipped, filter-skip, filter-proceed). The structural coverage is good; the assertion depth has gaps:

  1. Staged test: previewInfo.updateFields and hasRawBody not asserted
  2. Filter short-circuit: buildUpdateData not verified as skipped
  3. Both filter tests: itemFilter call arguments not verified — a wrong-repo or wrong-number bug would be invisible

🔎 Code quality review by PR Code Quality Reviewer · 555.7 AIC · ⌖ 13.2 AIC · ⊞ 16.7K


expect(result.success).toBe(true);
expect(result.staged).toBe(true);
expect(result.previewInfo).toMatchObject({ number: 42 });

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Staged-mode preview assertions are incomplete — future regressions in what the staged payload captures will not be detected.

💡 Suggested additions

The staged path returns previewInfo: { number, updateFields, hasRawBody }. This test only validates number. A refactor that accidentally emptied updateFields or miscaptured hasRawBody would silently pass.

Consider adding:

expect(result.previewInfo.updateFields).toEqual(["title"]);
expect(result.previewInfo.hasRawBody).toBe(false);
// optionally: formatSuccessResult not called in staged mode
expect(handlerConfig.formatSuccessResult).not.toHaveBeenCalled();

The updateFields list is the core of what staged mode is communicating — it deserves explicit coverage.

const handler = await handlerFactory({});
const result = await handler({ title: "Test" });

expect(mockItemFilter).toHaveBeenCalled();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

itemFilter short-circuit does not verify buildUpdateData is skipped — a regression leaving buildUpdateData called after filter rejection would be invisible.

When itemFilter returns a truthy result the handler returns immediately (before buildUpdateData is reached). The test should assert expect(mockBuildUpdateData).not.toHaveBeenCalled() to lock that control-flow order down. Without it, a future accidental reorder won't fail the test.

const handler = await handlerFactory({});
const result = await handler({ title: "Test" });

expect(mockItemFilter).toHaveBeenCalled();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Both itemFilter tests verify the filter was called but not what it received — a bug passing the wrong repo, item number, or config to the filter would still pass these tests.

Production code calls itemFilter(githubClient, repoResult.repoParts, itemNumber, config). Consider asserting the arguments explicitly:

expect(mockItemFilter).toHaveBeenCalledWith(
  expect.anything(),        // githubClient
  { owner: 'testowner', repo: 'testrepo' },
  42,                       // itemNumber
  expect.objectContaining({}) // config
);

Without this, a cross-repo routing bug that passes the wrong repoParts or a wrong itemNumber to the filter would be completely invisible here.

const configParts = [
`max=${maxCount}`,
`target=${updateTarget}`,
...Object.entries(additionalConfig)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

additionalConfig values are silently ignored — only its keys are used as a whitelist — the refactored form makes this harder to notice.

Both old and new code read config[key] (runtime value) for the log entry, not additionalConfig[key] (schema value). The old code made this slightly visible via the unused value destructure variable; the new compact form hides it entirely.

If a future contributor adds a value to additionalConfig expecting it to appear in the log (e.g., as a default or label), it will be silently ignored. Consider a short comment to document intent:

// additionalConfig keys act as a whitelist; values are logged from the runtime config
...Object.entries(additionalConfig)
  .filter(([key]) => config[key] !== undefined)
  .map(([key]) => `${key}=${config[key]}`),

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

@copilot run pr-finisher skill

Done. I ran the PR-finisher flow, addressed the actionable review feedback, and pushed commit 2db7e74.

Copilot AI requested a review from pelikhan June 13, 2026 10:41
@pelikhan pelikhan merged commit 0068234 into main Jun 13, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants