Skip to content

feat: intro vitest & split units#1000

Open
ferhatelmas wants to merge 1 commit intomasterfrom
ferhat/vitest-unit
Open

feat: intro vitest & split units#1000
ferhatelmas wants to merge 1 commit intomasterfrom
ferhat/vitest-unit

Conversation

@ferhatelmas
Copy link
Copy Markdown
Member

@ferhatelmas ferhatelmas commented Apr 9, 2026

What kind of change does this PR introduce?

feat

What is the current behavior?

Tests are run in one block sequentially by jest.

What is the new behavior?

Introduce vitest. Move some obvious unit tests next to their implementations.
Run them in CI separately. Merge coverage via parallel runs.
Unit tests won't run twice, there is no difference for db.

Additional context

This is a direction to split tests for better organization and run speed.

@ferhatelmas ferhatelmas requested a review from a team as a code owner April 9, 2026 20:43
Copilot AI review requested due to automatic review settings April 9, 2026 20:43
Copy link
Copy Markdown

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

This PR introduces Vitest as a dedicated runner for colocated unit tests (under src/**), while scoping Jest to the existing integration-style test suite under src/test. It also updates CI to run unit tests separately and upload coverage in parallel.

Changes:

  • Add vitest.config.ts + src/test/vitest-setup.ts, and wire new test:unit* scripts.
  • Move/split several tests to live alongside implementations (and adjust imports accordingly).
  • Update CI workflow and Jest config to run unit vs integration tests separately and upload coverage.

Reviewed changes

Copilot reviewed 17 out of 23 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
vitest.config.ts Adds Vitest configuration, aliases, coverage, and test include/exclude rules.
src/test/vitest-setup.ts Bridges existing Jest-style globals/setup into Vitest.
src/storage/validators/x-robots-tag.test.ts Removes unrelated plugin tests; keeps validator-focused unit tests.
src/http/plugins/header-validator.test.ts Restores/moves header-validator plugin tests into the correct module area.
src/storage/protocols/s3/signature-v4-stream.test.ts Adds new unit tests for the streaming signature parser.
src/internal/streams/ndjson.test.ts Adds unit tests for NdJsonTransform.
src/internal/streams/hash-stream.test.ts Updates import path for shared test utility.
src/internal/errors/render.test.ts Switches to local imports to support colocated unit tests.
src/internal/database/migrations/memoize.ts Extracts a shared memoizePromise helper.
src/internal/database/migrations/files.ts Uses memoizePromise to cache migration file loading.
src/internal/database/migrations/memoize.test.ts Adds tests intended for memoizePromise.
src/internal/concurrency/async-abort-controller.test.ts Adds unit tests for AsyncAbortController.
src/internal/cache/ttl.test.ts Adjusts TTL test timing approach for the new unit-runner context.
src/internal/cache/lru.test.ts Adds unit tests for LRU cache wrapper behavior.
src/internal/auth/crypto.test.ts Updates imports for colocated test location.
src/http/routes/s3/router.test.ts Updates imports for colocated test location.
src/http/routes-helper.test.ts Updates imports for colocated test location.
src/http/plugins/xml.test.ts Updates imports for colocated test location.
src/http/plugins/log-request.test.ts Updates imports for colocated test location.
jest.config.cjs Scopes Jest to src/test and updates match settings.
package.json Adds Vitest + coverage deps and unit-test scripts.
package-lock.json Lockfile updates for Vitest and new transitive dependencies.
.github/workflows/ci.yml Adds a unit-test job and parallel Coveralls upload configuration.
Comments suppressed due to low confidence (1)

src/internal/cache/ttl.test.ts:115

  • This test now uses real setTimeout waits (20ms + 30ms). That slows the suite and makes the test dependent on wall-clock scheduling. Since the repo is standardizing on Vitest for unit tests (and jest is aliased to vi), consider switching back to fake timers (jest/vi.useFakeTimers, advanceTimersByTime, and/or setSystemTime) so the TTL behavior is tested deterministically and quickly.

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

Comment on lines +10 to +18
function generateKey(args: Args): string {
return args
.map((arg) => {
if (typeof arg === 'object' && arg !== null) {
return Object.entries(arg).sort().toString()
}
return String(arg)
})
.join('|')
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

generateKey() uses Object.entries(arg).sort().toString() for object arguments, which is lossy and collision-prone (nested objects become "[object Object]", commas are ambiguous with .toString(), and key order/serialization edge cases can collide). Either constrain this helper to primitive arguments (and throw for objects), or switch to a stable serialization/hash function that can uniquely represent supported argument types.

Suggested change
function generateKey(args: Args): string {
return args
.map((arg) => {
if (typeof arg === 'object' && arg !== null) {
return Object.entries(arg).sort().toString()
}
return String(arg)
})
.join('|')
function serializeValue(
value: unknown,
seen: WeakSet<object> = new WeakSet()
): string {
if (value === null) {
return 'null'
}
if (value === undefined) {
return 'undefined'
}
if (typeof value === 'string') {
return `string:${JSON.stringify(value)}`
}
if (typeof value === 'number') {
if (Number.isNaN(value)) {
return 'number:NaN'
}
if (Object.is(value, -0)) {
return 'number:-0'
}
return `number:${value}`
}
if (typeof value === 'boolean') {
return `boolean:${value}`
}
if (typeof value === 'bigint') {
return `bigint:${value.toString()}`
}
if (typeof value === 'symbol') {
return `symbol:${String(value)}`
}
if (typeof value === 'function') {
return `function:${value.name || 'anonymous'}`
}
if (seen.has(value)) {
throw new Error('Cannot memoize arguments with circular references')
}
seen.add(value)
if (Array.isArray(value)) {
return `array:[${value.map((item) => serializeValue(item, seen)).join(',')}]`
}
if (value instanceof Date) {
return `date:${value.toISOString()}`
}
if (value instanceof RegExp) {
return `regexp:${value.toString()}`
}
const entries = Object.keys(value as Record<string, unknown>)
.sort()
.map(
(key) =>
`${JSON.stringify(key)}:${serializeValue(
(value as Record<string, unknown>)[key],
seen
)}`
)
return `object:{${entries.join(',')}}`
}
function generateKey(args: Args): string {
return args.map((arg) => serializeValue(arg)).join('|')

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

these tests aren't testing the right thing but this is simple move, will address it separately

@coveralls
Copy link
Copy Markdown

coveralls commented Apr 9, 2026

Coverage Report for CI Build 24215850057

Coverage increased (+1.6%) to 82.434%

Details

  • Coverage increased (+1.6%) from the base build.
  • Patch coverage: 1 uncovered change across 1 file (30 of 31 lines covered, 96.77%).
  • 2 coverage regressions across 1 file.

Uncovered Changes

File Changed Covered %
src/internal/database/migrations/memoize.ts 30 29 96.67%

Coverage Regressions

2 previously-covered lines in 1 file lost coverage.

File Lines Losing Coverage Coverage
src/http/error-handler.ts 2 84.97%

Coverage Stats

Coverage Status
Relevant Lines: 33538
Covered Lines: 30023
Line Coverage: 89.52%
Relevant Branches: 7143
Covered Branches: 3512
Branch Coverage: 49.17%
Branches in Coverage %: Yes
Coverage Strength: 347.78 hits per line

💛 - Coveralls

@ferhatelmas ferhatelmas requested a review from Copilot April 9, 2026 20:57
@ferhatelmas ferhatelmas force-pushed the ferhat/vitest-unit branch 2 times, most recently from 2c815e2 to be518d4 Compare April 9, 2026 21:02
Copy link
Copy Markdown

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

Copilot reviewed 18 out of 22 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

src/internal/cache/ttl.test.ts:116

  • These TTL tests now rely on real time (setTimeout) to advance TTL. That makes the suite slower and can be flaky under CI load/timer throttling. Prefer using vi.useFakeTimers() + advancing timers, and (if the underlying TTL cache uses performance.now) stub performance.now similarly to the previous Jest approach to keep TTL calculations deterministic.

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

Copy link
Copy Markdown

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

Copilot reviewed 18 out of 22 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

src/internal/cache/ttl.test.ts:152

  • In this dispose test, timers are advanced but the mocked performance.now() value (now) is not incremented. If the underlying TTL logic uses performance.now() for age calculations, this means time never actually advances from the cache’s perspective, so the test won’t reliably validate that dispose() cancels the internal TTL timer. Use the existing advance() helper here (or increment now alongside advancing timers) so the entry would become stale if the timer were still active.

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

Signed-off-by: ferhat elmas <elmas.ferhat@gmail.com>
Copy link
Copy Markdown

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

Copilot reviewed 20 out of 22 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

src/internal/database/migrations/memoize.test.ts:5

  • These tests currently re-implement a local memoizePromise (using JSON.stringify) instead of importing and exercising the production memoizePromise from ./memoize. As a result, the suite can pass even if the real helper breaks or behaves differently. Update the tests to import the actual helper and assert its real keying/caching semantics (including behavior on rejection) rather than testing a stubbed implementation.

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

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.

3 participants