Conversation
50ba2f1 to
4729211
Compare
There was a problem hiding this comment.
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 newtest: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
setTimeoutwaits (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 (andjestis aliased tovi), consider switching back to fake timers (jest/vi.useFakeTimers,advanceTimersByTime, and/orsetSystemTime) so the TTL behavior is tested deterministically and quickly.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 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('|') |
There was a problem hiding this comment.
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.
| 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('|') |
There was a problem hiding this comment.
these tests aren't testing the right thing but this is simple move, will address it separately
Coverage Report for CI Build 24215850057Coverage increased (+1.6%) to 82.434%Details
Uncovered Changes
Coverage Regressions2 previously-covered lines in 1 file lost coverage.
Coverage Stats💛 - Coveralls |
4729211 to
5a83690
Compare
2c815e2 to
be518d4
Compare
There was a problem hiding this comment.
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 usingvi.useFakeTimers()+ advancing timers, and (if the underlying TTL cache usesperformance.now) stubperformance.nowsimilarly to the previous Jest approach to keep TTL calculations deterministic.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
be518d4 to
cf93909
Compare
There was a problem hiding this comment.
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 usesperformance.now()for age calculations, this means time never actually advances from the cache’s perspective, so the test won’t reliably validate thatdispose()cancels the internal TTL timer. Use the existingadvance()helper here (or incrementnowalongside 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.
cf93909 to
434cd89
Compare
Signed-off-by: ferhat elmas <elmas.ferhat@gmail.com>
434cd89 to
e13bf9f
Compare
There was a problem hiding this comment.
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(usingJSON.stringify) instead of importing and exercising the productionmemoizePromisefrom./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.
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.