Skip to content

chore(coverage): 🔧 improve coverage tooling and test suite#57

Merged
jorben merged 5 commits into
masterfrom
chore/coverage-high-standard-20260303
Mar 4, 2026
Merged

chore(coverage): 🔧 improve coverage tooling and test suite#57
jorben merged 5 commits into
masterfrom
chore/coverage-high-standard-20260303

Conversation

@jorben

@jorben jorben commented Mar 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Expand coverage-focused tests across cloud/auth flows in main and renderer
  • Add merged coverage workflow with dedicated scripts and merge report generation
  • Update CI to run renderer tests and merged coverage reporting
  • Add local agent artifact ignore rules to prevent accidental commits

Test Plan

  • Run npm run test:unit
  • Run npm run test:renderer
  • Run npm run test:coverage:all

🤖 Generated with Codex Cli

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Summary

This PR substantially expands test coverage across main/renderer cloud/auth/update flows and adds a merged coverage reporting pipeline wired into CI. The direction is good and mostly aligns with the project’s architecture, but there is one CI-level correctness issue that will likely break the pipeline as currently ordered. I would not merge until that is fixed.

Critical

  • .github/workflows/ci.yml:84Coverage step likely fails due to missing test setup dependencies/build artifacts: Running npm run test:renderer and npm run test:coverage:all before npm run build can fail if renderer/main tests rely on generated artifacts or type-transpiled ESM paths (*.js imports in TS tests/modules are common in this repo). Since the workflow previously only ran unit tests and then build, introducing these steps without ensuring prerequisite generation/transpile can cause deterministic CI failures.
    Suggestion: Move Generate merged coverage report after build (or add explicit prerequisite steps before tests, e.g., npm run generate and any required compile/prep step), and ensure the same ordering used locally for test:coverage:all.

Important

  • package.json:43Coverage merge script assumes JSON reporter output path stability: test:coverage:merge hardcodes coverage/main/coverage-final.json and coverage/renderer/coverage-final.json. If Vitest/coverage provider output naming changes or reporter config drifts, the merge step will fail hard.
    Suggestion: Either (a) set explicit coverage.reportOnFailure/stable output config in Vitest files and document this contract, or (b) make coverage-merge.mjs discover coverage-final.json dynamically within each directory and provide clearer remediation in error output.

Praise

  • scripts/coverage-merge.mjs:1Pragmatic merged coverage utility: Nice use of istanbul-lib-coverage with scoped summaries (src vs business files) and actionable “top uncovered” output; this is useful for prioritizing real coverage work.
  • tests/setup.renderer.ts:1Much more complete renderer API mocking: Expanding window.api mocks for cloud/auth/updater/events reduces brittle test setup and improves reliability for component/context tests.
  • src/main/ipc/handlers/__tests__/cloud.handler.test.ts:1Strong IPC contract validation: Good breadth on input validation, delegated call behavior, and error wrapping across many channels—this directly protects main↔renderer API stability.

@github-actions

github-actions Bot commented Mar 4, 2026

Copy link
Copy Markdown

AI Code Review Summary

PR: #57 (chore(coverage): 🔧 improve coverage tooling and test suite)
Preferred language: English

Overall Assessment

Detected 12 actionable findings, prioritize CRITICAL/HIGH before merge.

Major Findings by Severity

  • MEDIUM (10)
    • src/core/infrastructure/services/tests/CloudService.test.ts:41 - Shared singleton state is not fully reset between CloudService tests
    • src/core/infrastructure/services/tests/CloudService.test.ts:43 - Missing explicit coverage for token acquisition rejection path
    • src/core/infrastructure/services/tests/CloudService.test.ts:125 - No assertion of request payload/query construction for pagination/filter methods
    • src/core/infrastructure/services/tests/CloudSSEManager.test.ts:167 - No coverage for SSE stream read exceptions and abort/cancel path
    • src/core/infrastructure/services/tests/CloudSSEManager.test.ts:212 - SSE reconnect/backoff tests do not verify cleanup or duplicate timer prevention
    • src/main/ipc/handlers/tests/cloud.handler.test.ts:121 - Missing boundary test at exact file-size limit for cloud:convert
    • src/main/ipc/handlers/tests/cloud.handler.test.ts:83 - Potentially incorrect mock shape for Node built-in fs default import in ESM tests
    • src/main/services/tests/UpdateService.test.ts:20 - Missing error-path coverage for updater check failures
  • LOW (2)
    • src/core/infrastructure/services/tests/AuthManager.test.ts:257 - No assertion that browser-open failure avoids entering polling state
    • vitest.config.ts:19 - Coverage config change lacks guard test for include/exclude correctness

Actionable Suggestions

  • Refactor fs mock in cloud.handler.test.ts to match the exact import contract used by cloud.handler.ts (default vs namespace vs named).
  • Reduce brittleness by asserting structured error categories/codes where possible instead of full message text.
  • Split cloud.handler.test.ts into smaller focused suites to improve readability and long-term maintenance.
  • Add exact-threshold tests (== 100MB) for cloud:convert content and path validation.
  • In AuthManager browser-open failure test, assert no polling side effects (no token poll fetch/timer).
  • Optionally add one test for non-Error thrown values in handlers to verify stable error serialization.
  • Keep extending negative tests around IPC trust boundaries (malformed args, unexpected types, oversized payloads).
  • Ensure production handlers enforce authz/session checks consistently; add explicit tests once implementation is in scope.
  • Add regression tests for sensitive-data handling in errors/logs across auth and cloud services.
  • In CloudService tests, isolate module state per test (vi.resetModules() in beforeEach) before dynamic import.
  • Consider exposing a non-production resetForTests() on CloudService if singleton pattern must remain.
  • Keep the CloudSSEManager-style explicit state reset pattern as a model for other singleton service tests.

Potential Risks

  • False confidence from mismatched module mocks.
  • Test brittleness due to exact string matching.
  • Maintenance overhead from one very large multi-responsibility test file.
  • Boundary bugs in upload size checks.
  • Hidden background side effects after early login failure.
  • Inconsistent error formatting when thrown value is not an Error instance.
  • If production IPC handlers rely only on renderer honesty, missing authz checks could still exist outside this diff.
  • Path-based convert flows may be exposed to traversal/symlink edge cases unless canonicalization is enforced in implementation.
  • Error wrapping that returns raw error messages can unintentionally leak internal details if not sanitized in production.
  • Intermittent test failures when test execution order changes.
  • False confidence from tests passing due to prior-test side effects.
  • API request construction bugs may pass current response-only assertions.

Test Suggestions

  • Run tests with changed import style experiment (temporarily switch handler fs import form) to confirm mocks fail loudly when shape mismatches.
  • Add one contract test per IPC channel ensuring handler registration list stays synchronized with implementation.
  • Add negative tests for non-Error throws in handler wrappers.
  • Run tests in watch with fake timers for AuthManager polling-related suites to detect leaked timers.
  • Add targeted regression tests for edge numeric inputs (0, 1, max, NaN) across cloud checkout/history params.
  • Security test: fuzz IPC payloads for all auth/cloud channels with invalid schemas and large nested objects.
  • Security test: verify no channel accepts dangerous prototypes (proto/constructor pollution payloads).
  • Security test: validate cloud:downloadPdf handles malicious file names safely (reserved names, traversal tokens, unicode tricks).
  • Run tests with random order/seed (if available) to detect hidden coupling.
  • Run vitest --runInBand and parallel modes to compare stability.
  • Add CI step to re-run this suite multiple times to catch flakes.
  • CloudService: verify fetch call arguments for getTasks/getTaskPages/getCreditHistory/getCheckoutStatus.

File-Level Coverage Notes

  • src/core/infrastructure/services/tests/AuthManager.test.ts: Security-neutral to positive. Added tests strengthen auth-flow robustness (duplicate login prevention, browser-open failure handling, device token polling state transitions, and timeout/abort error behavior). (This is test code; no production auth logic changes are shown in this diff.)
  • src/main/ipc/handlers/tests/auth.handler.test.ts: Security-neutral to positive. Tests validate IPC handler registration integrity and error wrapping behavior for auth channels. (No direct production handler implementation changes are included here.)
  • src/main/ipc/handlers/tests/cloud.handler.test.ts: Security-positive test coverage expansion. Includes validation-path tests (size checks, parameter validation), file save cancellation handling, and broad delegated error wrapping checks. (Given this is test-only code, there is no new exploit surface in runtime behavior from this diff alone.)
  • src/core/infrastructure/services/tests/CloudService.test.ts: Comprehensive API-surface coverage with strong negative-case testing, but still output-centric; request-shape and auth-exception boundary checks are missing. (Good breadth overall; improving input-to-request assertions would significantly harden these tests.)
  • src/core/infrastructure/services/tests/CloudSSEManager.test.ts: Solid baseline for SSE parsing and reconnect behavior, with notable gaps in abort/error lifecycle and timer cleanup guarantees. (Given singleton mutable state, lifecycle assertions are especially important to avoid cross-test and production nondeterminism.)
  • src/core/infrastructure/adapters/split/tests/FileWaitUtil.test.ts: Good retry/timer diagnostics coverage and clear failure diagnostics assertions. (This test file is in good shape for core behavior.)
  • src/renderer/utils/tests/cloudTaskMapper.test.ts: Strong unit coverage for happy-path and several boundary mappings (zero pages, overflow clamp, status override, type/model fallbacks). (Overall quality is good; mostly stabilization improvements needed.)
  • src/renderer/utils/cloudTaskMapper.ts: Clamp addition is a solid defensive fix and now covered by overflow test. (Code change itself is small and appropriate.)
  • tests/setup.renderer.ts: Expanded mocks improve renderer test compatibility and reduce missing-API failures. (Useful infra expansion; maintainability guardrails would help.)
  • vitest.config.renderer.ts: Coverage include/exclude is clearer and should improve signal for renderer/shared source files. (Reasonable config hardening.)
  • vitest.config.ts: Backend coverage scoping is improved and more explicit. (Good direction; add guardrails for long-term stability.)
  • src/main/services/tests/UpdateService.test.ts: Comprehensive core-path testing (dev skip, one-time init, concurrency guard, event forwarding, error truncation, quit/install). (High-value suite; a couple of resilience cases remain.)

Inline Downgraded Items (processed but not inline)

  • None

Coverage Status

  • Target files: 12
  • Covered files: 12
  • Uncovered files: 0
  • No-patch/binary covered as file-level: 0
  • Findings with unknown confidence (N/A): 0

Uncovered list:

  • None

No-patch covered list:

  • None

Runtime/Budget

  • Rounds used: 1/4
  • Planned batches: 3
  • Executed batches: 3
  • Sub-agent runs: 7
  • Planner calls: 1
  • Reviewer calls: 7
  • Model calls: 8/64
  • Structured-output summary-only degradation: NO

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Automated PR review completed.

  • Findings kept: 14
  • Inline comments attempted: 14
  • Target files: 11
  • Covered files: 11
  • Uncovered files: 0
    See the summary comment for detailed analysis and coverage details.

expect(mockWindowManager.sendToRenderer).not.toHaveBeenCalled()
})

it('readStream parses CRLF chunks and reconnects when stream ends naturally', async () => {

This comment was marked as outdated.

}))

describe('FileWaitUtil', () => {
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})

This comment was marked as outdated.

expect(warnSpy).not.toHaveBeenCalled()
})

it('retries until file becomes available', async () => {

This comment was marked as outdated.

authManager: mockAuthManager,
}))

const makeJsonResponse = (status: number, body: any) => ({

This comment was marked as outdated.

vi.useRealTimers()

const { cloudSSEManager } = await import('../CloudSSEManager.js')
const manager = cloudSSEManager as any

This comment was marked as outdated.

expect(mockSendToRenderer).toHaveBeenCalledWith('updater:status', { status: 'downloaded', version: '1.2.3' })
})

it('truncates error message before sending to renderer', async () => {

This comment was marked as outdated.

vi.clearAllMocks()
handlers.clear()

const mod = await import('../auth.handler.js')

This comment was marked as outdated.

success: true,
data: { buffer, fileName: 'demo.pdf' },
})
mockDialog.showSaveDialog.mockResolvedValueOnce({ canceled: false, filePath: '/downloads/demo.pdf' })

This comment was marked as outdated.

await second
})

it('forwards updater events to renderer', async () => {

This comment was marked as outdated.

}

describe('cloudTaskMapper', () => {
it('maps cloud task to local task shape', () => {

This comment was marked as outdated.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Automated PR review completed.

  • Findings kept: 12
  • Findings with unknown confidence: 0
  • Inline comments attempted: 12
  • Target files: 12
  • Covered files: 12
  • Uncovered files: 0
    See the summary comment for detailed analysis and coverage details.

})

describe('CloudService', () => {
beforeEach(() => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] Shared singleton state is not fully reset between CloudService tests

Tests rely on a shared imported singleton without explicit state reset, which can cause inter-test coupling/regressions if CloudService gains or already has mutable internal fields.

Suggestion: Use module isolation per test (vi.resetModules() before import) or add/reset a dedicated CloudService test hook/state reset in beforeEach. Prefer constructing a fresh service instance where possible instead of importing a shared default singleton.

Risk: Order-dependent failures and hard-to-debug flakes as implementation evolves (especially around cached tokens/options/retry flags).

Confidence: 0.91

[From SubAgent: general]

describe('CloudService', () => {
beforeEach(() => {
vi.clearAllMocks()
mockAuthManager.getAccessToken.mockResolvedValue('token-1')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] Missing explicit coverage for token acquisition rejection path

Tests cover empty token handling but not the case where getAccessToken rejects with an exception, leaving an unverified error-normalization path.

Suggestion: Add tests where mockAuthManager.getAccessToken.mockRejectedValueOnce(new Error('auth down')) for key methods (at least convert, getTasks, and one mutating method) and assert consistent { success:false, error:'auth down' } behavior.

Risk: Unhandled auth exceptions could surface as unnormalized errors or crash paths in production without being caught by current tests.

Confidence: 0.90

[From SubAgent: testing]

}
mockAuthManager.fetchWithAuth.mockResolvedValueOnce(makeJsonResponse(200, response))

const result = await cloudService.getTasks(1, 10)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] No assertion of request payload/query construction for pagination/filter methods

Many tests assert outputs but not whether input params are serialized correctly into request URL/body/options, which can hide regressions in API contract formation.

Suggestion: For getTasks/getTaskPages/getCreditHistory/getPaymentHistory/getCheckoutStatus, assert fetchWithAuth.mock.calls[n] contains expected endpoint, query params (page, page_size, filters, wait_seconds), and method.

Risk: A regression in request construction could pass tests while breaking real API behavior.

Confidence: 0.85

[From SubAgent: testing]

expect(mockWindowManager.sendToRenderer).not.toHaveBeenCalled()
})

it('readStream parses CRLF chunks and reconnects when stream ends naturally', async () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] No coverage for SSE stream read exceptions and abort/cancel path

Current SSE tests focus on parse correctness and reconnect on normal end/error status, but not failures during read or explicit cancellation flow.

Suggestion: Add tests simulating read() rejection and disconnect() during active stream; assert graceful handling, no renderer spam, and reconnect policy consistency.

Risk: Runtime stream errors may leave manager in inconsistent connected state or trigger unexpected reconnect behavior.

Confidence: 0.82

[From SubAgent: testing]

expect(reconnectSpy).toHaveBeenCalled()
})

it('reconnect applies exponential backoff and caps delay', async () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] SSE reconnect/backoff tests do not verify cleanup or duplicate timer prevention

Timer-heavy tests validate scheduling but not that reconnect timer lifecycle avoids stacking or stale timers, leaving potential flaky/redundant reconnect behavior untested.

Suggestion: Add assertions that reconnect clears/overwrites previous timers and that cleanup()/disconnect() nulls timers/aborts streams. Verify only one active reconnect timer after repeated failures.

Risk: Duplicate reconnect timers can cause intermittent reconnect storms and nondeterministic behavior that unit tests may currently miss.

Confidence: 0.78

[From SubAgent: testing]

on: vi.fn((event: string, cb: (...args: any[]) => void) => {
handlers[event] = cb
}),
checkForUpdates: vi.fn().mockResolvedValue(undefined),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] Missing error-path coverage for updater check failures

The suite does not verify behavior when autoUpdater.checkForUpdates() fails, leaving resilience and observability of that path unvalidated.

Suggestion: Add a test where mockAutoUpdater.checkForUpdates.mockRejectedValueOnce(new Error('network')) and assert expected renderer notification/logging plus reset of in-flight guard state for subsequent calls.

Risk: Unhandled promise rejection or stuck internal state could break update checks in production without test detection.

Confidence: 0.91

[From SubAgent: testing]

})

it('uses current timestamp fallback when no created_at and no started_at', () => {
const nowMin = Date.now() - 1000

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] Time-based fallback test can be flaky due to real clock usage

The test relies on wall-clock time and a 1s window, which can intermittently fail under slow CI, timer skew, or execution pauses.

Suggestion: Use vi.useFakeTimers() + vi.setSystemTime(...) and assert exact equality for sortTimestamp, then restore real timers in cleanup.

Risk: Intermittent failures reduce CI reliability and can mask real regressions.

Confidence: 0.93

[From SubAgent: testing]

Comment thread tests/setup.renderer.ts
},
})

if (!window.URL.createObjectURL) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] Conditional URL API mocking can cause test-order dependent behavior

Global URL methods are mocked only when absent, which can lead to non-deterministic test behavior depending on runtime environment and library versions.

Suggestion: Always stub these globals in setup (or vi.spyOn + mockImplementation) and restore/reset in hooks for deterministic behavior. Example: assign window.URL.createObjectURL = vi.fn(() => 'blob:mock-url') unconditionally in setup and clear in beforeEach.

Risk: Flaky tests and hidden regressions due to differing global behavior across environments or after jsdom upgrades.

Confidence: 0.82

[From SubAgent: general]

expect(global.fetch).toHaveBeenCalledTimes(1);
});

it('should return error when browser cannot be opened', async () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[LOW] No assertion that browser-open failure avoids entering polling state

The test checks error state but does not assert that polling/timers were not started after browser open failure.

Suggestion: Also assert no subsequent polling attempt (e.g., no token-endpoint fetch calls or timer scheduling, depending on implementation hooks) after shell.openExternal rejection.

Risk: A hidden side effect could keep background polling alive despite immediate failure, causing flaky state transitions or resource leaks.

Confidence: 0.86

[From SubAgent: testing]

Comment thread vitest.config.ts
coverage: {
provider: "v8",
reporter: ["text", "json", "html"],
include: [

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[LOW] Coverage config change lacks guard test for include/exclude correctness

Coverage scope has materially changed, but there is no validation that key source areas are still measured and that test/config files remain excluded as intended.

Suggestion: Add a lightweight CI assertion (script or snapshot of coverage summary paths) to catch accidental glob regressions, especially across monorepo-like src segments.

Risk: Coverage metrics may drift silently, creating false confidence in test completeness.

Confidence: 0.79

[From SubAgent: testing]

@jorben
jorben merged commit b89cd75 into master Mar 4, 2026
3 checks passed
@jorben
jorben deleted the chore/coverage-high-standard-20260303 branch March 4, 2026 05:35
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