Skip to content

🐛 Address Copilot review comments from PRs #4129, #4130, #4134#4147

Merged
clubanderson merged 1 commit intomainfrom
fix/copilot-review-batch2
Apr 1, 2026
Merged

🐛 Address Copilot review comments from PRs #4129, #4130, #4134#4147
clubanderson merged 1 commit intomainfrom
fix/copilot-review-batch2

Conversation

@clubanderson
Copy link
Copy Markdown
Collaborator

Summary

Addresses all actionable Copilot review comments from three recently merged PRs:

PR #4129 — GPU health & persistence:

  • Include ClusterRole name (gpuHealthClusterRole) in error messages for easier operational debugging
  • Add machine-readable errorCode: "SYNC_NOT_IMPLEMENTED" to the 501 sync endpoint response so clients can handle it without string matching

PR #4130 — Feedback test hygiene:

  • Replace payload, _ := json.Marshal(...) with require.NoError via a requireMarshalJSON helper to catch unexpected marshal failures
  • Replace body, _ := io.ReadAll(resp.Body) with require.NoError via a readBody helper that also closes the body
  • Add defer resp.Body.Close() to all tests that read response bodies
  • Explicitly discard mac.Write() return values (_, _ =) to satisfy strict linters

PR #4134 — Accessibility, HTTP client reuse, body limit scoping:

  • Add onFocus/onBlur handlers and aria-describedby to InfoTooltip so keyboard users and screen readers can access tooltip content
  • Replace per-request http.Client creation in uploadScreenshotToGitHub with context.WithTimeout + h.httpClient to reuse Transport (connection pooling, proxy settings)
  • Scope the elevated 20 MB body limit to the feedback creation endpoint only via a per-route middleware guard, keeping a 1 MB default for all other API routes

Test plan

  • go build ./... passes
  • go test ./pkg/api/handlers/... -run "TestFeedback|TestWebhook" — all 13 tests pass
  • npx tsc --noEmit passes (frontend)

- Include ClusterRole name in error messages for easier debugging (client.go)
- Add machine-readable errorCode to sync endpoint 501 response (console_persistence.go)
- Fix test hygiene: check json.Marshal/io.ReadAll errors, close response
  bodies, explicitly discard hash.Write returns (feedback_test.go)
- Add onFocus/onBlur and aria-describedby to InfoTooltip for keyboard
  and screen-reader accessibility (CardWrapper.tsx)
- Reuse h.httpClient with context.WithTimeout instead of creating a new
  http.Client per screenshot upload (feedback.go)
- Scope elevated 20 MB body limit to feedback creation route only via
  per-route middleware guard (server.go)

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Copilot AI review requested due to automatic review settings April 1, 2026 13:24
@kubestellar-prow kubestellar-prow bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Apr 1, 2026
@kubestellar-prow
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign clubanderson for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@netlify
Copy link
Copy Markdown

netlify bot commented Apr 1, 2026

Deploy Preview for kubestellarconsole ready!

Name Link
🔨 Latest commit b84603e
🔍 Latest deploy log https://app.netlify.com/projects/kubestellarconsole/deploys/69cd1c8e8ad2f3000813d40a
😎 Deploy Preview https://deploy-preview-4147.console-deploy-preview.kubestellar.io
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@clubanderson clubanderson merged commit ec08577 into main Apr 1, 2026
19 of 20 checks passed
@kubestellar-prow kubestellar-prow bot deleted the fix/copilot-review-batch2 branch April 1, 2026 13:24
@kubestellar-prow kubestellar-prow bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Apr 1, 2026
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

Addresses actionable Copilot review comments from prior PRs by improving accessibility, operational debug-ability, API error contracts, HTTP client usage, request body-size protections, and Go test hygiene.

Changes:

  • Improves tooltip accessibility (keyboard + screen reader support) and adds stable tooltip identifiers.
  • Enhances API/server behavior: clearer K8s error messages, machine-readable 501 error codes, HTTP client reuse with timeouts, and body-size limiting logic.
  • Hardens tests by asserting on JSON marshaling/read errors and ensuring response bodies are closed.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
web/src/components/cards/CardWrapper.tsx Adds focus/blur handling and ARIA wiring for InfoTooltip.
pkg/k8s/client.go Includes ClusterRole name in relevant error messages.
pkg/api/server.go Adds API body-size guard logic and updates comments/config around body limits.
pkg/api/handlers/feedback_test.go Improves test robustness via helper functions and proper body closing.
pkg/api/handlers/feedback.go Reuses handler HTTP client with per-request timeout via request context.
pkg/api/handlers/console_persistence.go Expands 501 response with machine-readable errorCode.

const [position, setPosition] = useState<{ top: number; left: number } | null>(null)
const triggerRef = useRef<HTMLButtonElement>(null)
const tooltipRef = useRef<HTMLDivElement>(null)
const tooltipId = useRef(`info-tooltip-${Math.random().toString(36).slice(2, 9)}`).current
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

Two related accessibility issues: (1) generating tooltipId via Math.random() can cause unstable IDs across renders (and can lead to hydration mismatches if this component is ever SSR-rendered); prefer React 18's useId() or a deterministic ID source. (2) aria-describedby is only set when isVisible is true, and the tooltip content is only mounted when visible; on keyboard focus, assistive tech may query the description before state updates mount the tooltip. A more robust pattern is to keep a stable aria-describedby pointing to an element that remains in the DOM (visually hidden when inactive) and toggle visibility via CSS/aria-hidden.

Copilot uses AI. Check for mistakes.
Comment on lines +309 to 318
aria-describedby={isVisible ? tooltipId : undefined}
>
<Info className="w-3.5 h-3.5" />
</button>
{isVisible && position && createPortal(
<div
ref={tooltipRef}
id={tooltipId}
role="tooltip"
className="fixed z-[100] max-w-xs px-3 py-2.5 text-xs leading-relaxed rounded-lg bg-background border border-border text-foreground shadow-xl animate-fade-in"
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

Two related accessibility issues: (1) generating tooltipId via Math.random() can cause unstable IDs across renders (and can lead to hydration mismatches if this component is ever SSR-rendered); prefer React 18's useId() or a deterministic ID source. (2) aria-describedby is only set when isVisible is true, and the tooltip content is only mounted when visible; on keyboard focus, assistive tech may query the description before state updates mount the tooltip. A more robust pattern is to keep a stable aria-describedby pointing to an element that remains in the DOM (visually hidden when inactive) and toggle visibility via CSS/aria-hidden.

Suggested change
aria-describedby={isVisible ? tooltipId : undefined}
>
<Info className="w-3.5 h-3.5" />
</button>
{isVisible && position && createPortal(
<div
ref={tooltipRef}
id={tooltipId}
role="tooltip"
className="fixed z-[100] max-w-xs px-3 py-2.5 text-xs leading-relaxed rounded-lg bg-background border border-border text-foreground shadow-xl animate-fade-in"
aria-describedby={tooltipId}
>
<Info className="w-3.5 h-3.5" />
</button>
{position && createPortal(
<div
ref={tooltipRef}
id={tooltipId}
role="tooltip"
className={cn(
"fixed z-[100] max-w-xs px-3 py-2.5 text-xs leading-relaxed rounded-lg bg-background border border-border text-foreground shadow-xl animate-fade-in",
!isVisible && "opacity-0 pointer-events-none"
)}

Copilot uses AI. Check for mistakes.
{isVisible && position && createPortal(
<div
ref={tooltipRef}
id={tooltipId}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

Two related accessibility issues: (1) generating tooltipId via Math.random() can cause unstable IDs across renders (and can lead to hydration mismatches if this component is ever SSR-rendered); prefer React 18's useId() or a deterministic ID source. (2) aria-describedby is only set when isVisible is true, and the tooltip content is only mounted when visible; on keyboard focus, assistive tech may query the description before state updates mount the tooltip. A more robust pattern is to keep a stable aria-describedby pointing to an element that remains in the DOM (visually hidden when inactive) and toggle visibility via CSS/aria-hidden.

Copilot uses AI. Check for mistakes.
Comment on lines +197 to +201
// feedbackBodyLimit is elevated globally to 20 MB to support base64-encoded
// screenshot uploads in the POST /api/feedback/requests endpoint. Non-feedback
// POST routes are protected by a tighter per-route body-size middleware
// (apiDefaultBodyLimit) to limit memory pressure from oversized requests.
const feedbackBodyLimit = 20 * 1024 * 1024 // 20 MB — base64 screenshot uploads
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The per-route bodyGuard currently calls c.Body() to measure size. With a global BodyLimit of 20MB, this still allows up to ~20MB to be read/buffered into memory for any /api request before being rejected at 1MB, which undermines the stated goal of limiting memory pressure (and can worsen DoS behavior under load). Consider enforcing the smaller limit before buffering by checking Content-Length (when present) and/or restructuring to keep Fiber's global BodyLimit at 1MB and only allow 20MB on the feedback creation endpoint (e.g., via mounting a sub-app with a higher BodyLimit for /api/feedback/requests). This also appears inconsistent with the PR description claim of keeping a 1MB default for all other API routes while the app config sets 20MB globally.

Copilot uses AI. Check for mistakes.
ErrorHandler: customErrorHandler,
ReadBufferSize: 16384,
WriteBufferSize: 16384,
BodyLimit: feedbackBodyLimit,
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The per-route bodyGuard currently calls c.Body() to measure size. With a global BodyLimit of 20MB, this still allows up to ~20MB to be read/buffered into memory for any /api request before being rejected at 1MB, which undermines the stated goal of limiting memory pressure (and can worsen DoS behavior under load). Consider enforcing the smaller limit before buffering by checking Content-Length (when present) and/or restructuring to keep Fiber's global BodyLimit at 1MB and only allow 20MB on the feedback creation endpoint (e.g., via mounting a sub-app with a higher BodyLimit for /api/feedback/requests). This also appears inconsistent with the PR description claim of keeping a 1MB default for all other API routes while the app config sets 20MB globally.

Copilot uses AI. Check for mistakes.
Comment on lines +602 to +616
// Body-size guard: enforce a 1 MB limit on all API routes except the
// feedback creation endpoint which accepts large base64 screenshot payloads
// (up to the global 20 MB Fiber BodyLimit).
const apiDefaultBodyLimit = 1 * 1024 * 1024 // 1 MB — sufficient for JSON API requests
bodyGuard := func(c *fiber.Ctx) error {
if c.Method() == fiber.MethodPost && c.Path() == "/api/feedback/requests" {
// Allow the elevated feedbackBodyLimit (checked by Fiber global config)
return c.Next()
}
if len(c.Body()) > apiDefaultBodyLimit {
return fiber.ErrRequestEntityTooLarge
}
return c.Next()
}
api := s.app.Group("/api", apiLimiter, bodyGuard, middleware.JWTAuth(s.config.JWTSecret))
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The per-route bodyGuard currently calls c.Body() to measure size. With a global BodyLimit of 20MB, this still allows up to ~20MB to be read/buffered into memory for any /api request before being rejected at 1MB, which undermines the stated goal of limiting memory pressure (and can worsen DoS behavior under load). Consider enforcing the smaller limit before buffering by checking Content-Length (when present) and/or restructuring to keep Fiber's global BodyLimit at 1MB and only allow 20MB on the feedback creation endpoint (e.g., via mounting a sub-app with a higher BodyLimit for /api/feedback/requests). This also appears inconsistent with the PR description claim of keeping a 1MB default for all other API routes while the app config sets 20MB globally.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

Thank you for your contribution! Your PR has been merged.

Check out what's new:

Stay connected: Slack #kubestellar-dev | Multi-Cluster Survey

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

👋 Hey @clubanderson — thanks for opening this PR!

🤖 This project is developed exclusively using AI coding assistants.

Please do not attempt to code anything for this project manually.
All contributions should be authored using an AI coding tool such as:

This ensures consistency in code style, architecture patterns, test coverage,
and commit quality across the entire codebase.


This is an automated message.

@clubanderson
Copy link
Copy Markdown
Collaborator Author

🔄 Auto-Applying Copilot Code Review

Copilot code review found 1 code suggestion(s) and 5 general comment(s).

@copilot Please apply all of the following code review suggestions:

  • web/src/components/cards/CardWrapper.tsx (line 318): aria-describedby={tooltipId} > <Info className="w-3.5 h-3.5" /> ...

Also address these general comments:

  • web/src/components/cards/CardWrapper.tsx (line 226): Two related accessibility issues: (1) generating tooltipId via Math.random() can cause unstable IDs across renders (
  • web/src/components/cards/CardWrapper.tsx (line 316): Two related accessibility issues: (1) generating tooltipId via Math.random() can cause unstable IDs across renders (
  • pkg/api/server.go (line 201): The per-route bodyGuard currently calls c.Body() to measure size. With a global BodyLimit of 20MB, this still allo
  • pkg/api/server.go (line 206): The per-route bodyGuard currently calls c.Body() to measure size. With a global BodyLimit of 20MB, this still allo
  • pkg/api/server.go (line 616): The per-route bodyGuard currently calls c.Body() to measure size. With a global BodyLimit of 20MB, this still allo

Push all fixes in a single commit. Run cd web && npm run build && npm run lint before committing.


Auto-generated by copilot-review-apply workflow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dco-signoff: yes Indicates the PR's author has signed the DCO. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants