Skip to content

[jsweep] Clean upload_artifact.cjs#35635

Merged
pelikhan merged 2 commits into
mainfrom
signed/jsweep/upload-artifact-20260529-754493b51ddbd311
May 29, 2026
Merged

[jsweep] Clean upload_artifact.cjs#35635
pelikhan merged 2 commits into
mainfrom
signed/jsweep/upload-artifact-20260529-754493b51ddbd311

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented May 29, 2026

PR Description

Summary

Refactors upload_artifact.cjs for clarity and correctness, and expands the test suite with new coverage for size enforcement and future test scaffolding.


Changes

actions/setup/js/upload_artifact.cjs (modified)

  • Replaced the mutual-exclusivity check for path/filters fields with a cleaner boolean equality expression
  • Simplified the file-filter predicate to a concise single-expression arrow function

actions/setup/js/upload_artifact.test.cjs (modified)

  • Added test cases for max-size-bytes enforcement:
    • Validates failure when artifact exceeds the size limit
    • Validates success when artifact is exactly at the size limit
  • Scaffolded describe blocks for:
    • Artifact name derivation
    • Slot index outputs
    • Auto-copy behaviour

Impact

  • Breaking change: No
  • Risk: Low — logic simplifications with equivalent semantics, backed by new test coverage

Generated by PR Description Updater for issue #35635 · sonnet46 871.8K ·

github-actions Bot and others added 2 commits May 29, 2026 05:11
- Fix JSDoc `/** @type {string[]} candidateRelPaths */` inline comment to correct `/** @type {string[]} */` declaration form
- Fix JSDoc type casts for `include`/`exclude` to wrap full expressions with parentheses
- Simplify verbose mutual-exclusion check using `hasPath === hasFilters`
- Simplify filter expression with single-line logical operators instead of if-return chain
- Add 8 new tests: max-size-bytes enforcement, artifact name derivation (custom, sanitised, basename, slot-index fallback), slot index outputs (increment, file_count/size_bytes)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@pelikhan pelikhan marked this pull request as ready for review May 29, 2026 05:14
Copilot AI review requested due to automatic review settings May 29, 2026 05:14
@github-actions
Copy link
Copy Markdown
Contributor Author

github-actions Bot commented May 29, 2026

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

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

@github-actions
Copy link
Copy Markdown
Contributor Author

github-actions Bot commented May 29, 2026

🧪 Test Quality Sentinel completed test quality analysis.

@github-actions
Copy link
Copy Markdown
Contributor Author

github-actions Bot commented May 29, 2026

PR Code Quality Reviewer completed the code quality review.

@github-actions
Copy link
Copy Markdown
Contributor Author

github-actions Bot commented May 29, 2026

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions github-actions Bot mentioned this pull request May 29, 2026
@github-actions
Copy link
Copy Markdown
Contributor Author

🧪 Test Quality Sentinel Report

Test Quality Score: 82/100 — Excellent

Analyzed 8 JavaScript test(s): 8 design tests (behavioral contracts), 0 implementation tests, 0 coding-guideline violations.

📊 Metrics & Test Classification (8 tests analyzed)
Metric Value
New/modified tests analyzed 8
✅ Design tests (behavioral contracts) 8 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 6 (75%)
Duplicate test clusters 0
Test inflation detected ⚠️ Yes (92 test lines added vs 5 prod lines; ratio ≈ 18:1) — but contextually expected: this is a cleanup PR adding coverage for previously untested behavior
🚨 Coding-guideline violations 0

Test Classification Details

Test File Classification Notes
fails when total file size exceeds max-size-bytes upload_artifact.test.cjs ✅ Design Error path covered
succeeds when total file size is exactly at the max-size-bytes limit upload_artifact.test.cjs ✅ Design Boundary condition covered
uses custom name from message when provided upload_artifact.test.cjs ✅ Design Asserts observable uploadArtifact call arg
sanitises special characters in custom name upload_artifact.test.cjs ✅ Design Edge case: invalid chars in name
falls back to basename of path when no name given upload_artifact.test.cjs ✅ Design Fallback behavior
falls back to slot index name when path basename is empty upload_artifact.test.cjs ✅ Design Edge case: empty path basename
increments slot index across multiple successful uploads upload_artifact.test.cjs ✅ Design Multi-upload output contract
sets slot file_count and size_bytes outputs upload_artifact.test.cjs ✅ Design Output shape contract

Language Support

Tests analyzed:

  • 🐹 Go (*_test.go): 0 tests
  • 🟨 JavaScript (*.test.cjs): 8 tests (vitest)

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). All 8 new tests verify observable behavioral contracts. The high test-to-production-line ratio (18:1) is appropriate here — this is a sweep PR that adds test coverage for existing behavior that was previously untested.

📖 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 · sonnet46 1M ·

Copy link
Copy Markdown
Contributor Author

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 82/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%). All 8 new tests verify behavioral contracts with good error/edge-case coverage.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Cleanup of actions/setup/js/upload_artifact.cjs (a safe-output handler for uploading artifacts via @actions/artifact) for clarity, plus expanded test coverage in the colocated Vitest file.

Changes:

  • Refactor resolveFiles mutual-exclusion check (hasPath === hasFilters) and inline the include/exclude filter into a single boolean expression; fix a non-standard JSDoc @type annotation.
  • Add 8 new tests covering max-size-bytes enforcement at and over the limit, artifact-name derivation (custom name, sanitisation, basename fallback, slot-index fallback), and slot index/count outputs across multiple uploads.
Show a summary per file
File Description
actions/setup/js/upload_artifact.cjs Minor readability refactors in resolveFiles (mutual-exclusion guard, filter predicate, JSDoc type annotation).
actions/setup/js/upload_artifact.test.cjs Adds 3 new describe blocks (8 tests) for size limits, name derivation, and slot index outputs.

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: 1

}

/** @type {string[]} candidateRelPaths */
/** @type {string[]} */
Copy link
Copy Markdown
Contributor Author

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /zoom-out and /tdd — no blocking issues, one readability suggestion.

📋 Key Themes & Highlights

Key Themes

  • Correctness: Both simplifications are semantically equivalent to the originals. hasPath === hasFilters is true iff both are absent or both are present — matching the original count ≠ 1 guard exactly. ✅
  • Readability trade-off: The filter lambda collapse is logically fine but produces a 158-char line; see inline comment for a split that preserves the one-liner intent without hitting line-length limits.
  • PR description vs diff: The description claims JSDoc casts for include/exclude were fixed to wrap the full || expression, but those lines appear unchanged in the diff. Not a blocker, but worth aligning the description with what actually changed.

Positive Highlights

  • hasPath === hasFilters is a genuine clarity win over the numeric-sum approach.
  • ✅ 8 new tests cover meaningful boundary conditions (exact-limit size, special-char sanitisation, slot-index increments) — good /tdd hygiene.
  • ✅ JSDoc variable-name-in-type-comment fix (/** @type {string[]} candidateRelPaths *//** @type {string[]} */) is correct.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 1.2M

if (exclude.length > 0 && matchesAnyPattern(f, exclude)) return false;
return true;
});
candidateRelPaths = allFiles.filter(f => (include.length === 0 || matchesAnyPattern(f, include)) && (exclude.length === 0 || !matchesAnyPattern(f, exclude)));
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.

[/zoom-out] The simplified one-liner is correct, but at 158 characters it trades the original multiline readability for compactness.

💡 Suggested split
candidateRelPaths = allFiles.filter(
  f =>
    (include.length === 0 || matchesAnyPattern(f, include)) &&
    (exclude.length === 0 || !matchesAnyPattern(f, exclude))
);

This keeps the two guard conditions on separate lines, making the logic easier to scan.

Copy link
Copy Markdown
Contributor Author

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

The production logic changes are correct — hasPath === hasFilters is a valid boolean equality guard and the filter one-liner is logically equivalent to the removed multi-line form. Two test coverage gaps worth addressing before merge:

### Findings
  1. No regression test for the hasPath === hasFilters guard (main production change): both-present and neither-present paths are untested — see inline comment.
  2. Sanitization test asserts absence of 3 chars but not structural validity of the result — an empty or all-hyphen name would pass silently — see inline comment.

All other new tests (max-size-bytes boundary, slot index outputs, name derivation fallbacks) look correct.

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 1.7M

expect(name).toBe("my-custom-name");
});

it("sanitises special characters in custom name", async () => {
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.

Sanitization test only checks absence of three chars — the result could be empty or invalid.

💡 Suggested fix

The test asserts name does not contain , (, or !, but does not verify the result is non-empty or structurally valid. If the sanitizer collapses everything to hyphens or returns "", the test still passes and the upstream artifact upload would fail silently.

const [name] = mockArtifactClient.uploadArtifact.mock.calls[0];
expect(name.length).toBeGreaterThan(0);
expect(name).toMatch(/^[a-zA-Z0-9._-]+$/);
expect(name).not.toContain(" ");
expect(name).not.toContain("(");
expect(name).not.toContain("!");

This fully pins the sanitized output contract rather than only checking a handful of rejected characters.

@@ -507,6 +507,98 @@ describe("upload_artifact.cjs", () => {
});
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.

No regression test for the hasPath === hasFilters guard — the main production change in this PR.

💡 Suggested fix

The refactoring of the mutual-exclusivity check (hasMutuallyExclusive !== 1hasPath === hasFilters) is the only production code change, but there are no tests exercising the error branches it guards: (1) both path and filters provided, and (2) neither provided. A future refactor could silently regress the guard.

describe("resolveFiles mutual-exclusivity guard", () => {
  it("fails when both path and filters are provided", async () => {
    writeStaging("file.json");
    const results = await runHandler(buildConfig(), [
      { type: "upload_artifact", path: "file.json", filters: { include: ["**"] } },
    ]);
    expect(results[0].success).toBe(false);
    expect(results[0].error).toContain("exactly one of 'path' or 'filters'");
  });

  it("fails when neither path nor filters are provided", async () => {
    const results = await runHandler(buildConfig(), [
      { type: "upload_artifact" },
    ]);
    expect(results[0].success).toBe(false);
    expect(results[0].error).toContain("exactly one of 'path' or 'filters'");
  });
});

@github-actions
Copy link
Copy Markdown
Contributor Author

@copilot refresh the branch and rerun checks, then summarize any remaining blockers.

Generated by 👨‍🍳 PR Sous Chef · gpt54 14.1M ·

1 similar comment
@github-actions
Copy link
Copy Markdown
Contributor Author

@copilot refresh the branch and rerun checks, then summarize any remaining blockers.

Generated by 👨‍🍳 PR Sous Chef · gpt54 14.1M ·

@pelikhan pelikhan merged commit 67e1de9 into main May 29, 2026
55 of 57 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.

2 participants