Skip to content

refactor: split remote handler + add unit tests#16064

Draft
elliott-with-the-longest-name-on-github wants to merge 1 commit into
version-3from
elliott/remote-handler-refactor
Draft

refactor: split remote handler + add unit tests#16064
elliott-with-the-longest-name-on-github wants to merge 1 commit into
version-3from
elliott/remote-handler-refactor

Conversation

@elliott-with-the-longest-name-on-github

Copy link
Copy Markdown
Contributor

I'm on a slow-and-steady mission to start being able to replace some of our integration tests with unit tests. Our integration tests often contort themselves into all sorts of horrible shapes to try to assert that some internal-to-sveltekit or side-effect-y thing is happening. Part of this effort is going to be busting up some of these big "orchestrator" files into abstractions that are easier to test. The remote function server handling hadn't gotten too bad yet, but it was definitely on its way there. I split it up some:

  • Split remote.js into more-focused remote/ modules. I think most of the code is clearer now.
  • Added shared server mock factories in test/mocks/server.js -- we had a couple of places where we were partially mock-and-cast-ing our request state stuff, this just kinda standardizes it
  • Unit tests for the bits that previously needed a browser: keep-alive timing (fake timers), dispatch per type, the collect_remote_data race/redirect/private-fn edge cases, keyed form.for() (incl. slash keys), requested() limits, and OTEL span emission
  • Tagged some unit tests with UNIT-MIGRATION-CANDIDATE for later; when I've had time to do this to more of the codebase and we're still stable for a while, we can sweep through and remove them

@changeset-bot

changeset-bot Bot commented Jun 17, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 274d6c0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@svelte-docs-bot

Copy link
Copy Markdown

}
})
);
import { create_mock_event } from '../../../test/mocks/server.js';

@Rich-Harris Rich-Harris Jun 17, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can we create an internal import for these like #test/mocks/server?


vi.mock(import('@sveltejs/kit/internal/server'), async (actualPromise) => {
const actual = await actualPromise();
const { create_mock_event, create_mock_state } = await import('../../../test/mocks/server.js');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why do we need a static import and a dynamic one?

Comment on lines +18 to +19
// @ts-expect-error
globalThis.__SVELTEKIT_DEV__ = false;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

depending on how often we find ourselves doing this it might be worth creating a helper for it

Comment on lines +5 to +13
import {
create_mock_event,
create_mock_internals,
create_mock_manifest,
create_mock_options,
create_mock_remote,
create_mock_request,
create_mock_state
} from '../../../../test/mocks/server.js';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

might feel nicer like this:

Suggested change
import {
create_mock_event,
create_mock_internals,
create_mock_manifest,
create_mock_options,
create_mock_remote,
create_mock_request,
create_mock_state
} from '../../../../test/mocks/server.js';
import * as mocks from '#test/mocks/server';

followed by

const internals = mocks.create_internals(...);

import { record_span } from '../../telemetry/record_span.js';
import { handle_error_and_jsonify } from '../utils.js';
import { collect_remote_data } from './collect.js';
import { run_command, run_form, run_prerender, run_query, run_query_batch } from './dispatch.js';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

what's the argument for separating call.js and dispatch.js? wouldn't it be easier to put the contents of dispatch.js in here?

@Rich-Harris

Rich-Harris commented Jun 17, 2026

Copy link
Copy Markdown
Member

this is probably going to cause some merge conflict misery — it might be better to wait until more of the PR queue gets merged before going much further. any PRs targeting main that touch remote functions are going to get annoying to untangle

@Rich-Harris

Copy link
Copy Markdown
Member

Overall I'm happy that we're moving in the direction of faster tests, but after a quick first pass through I worry about a couple of things (besides the aforementioned merge conflicts):

  • we're testing a lot of internal implementation details that don't matter. as those implementation details evolve, we will need to update all our tests (or worse, we will be discouraged from improving the implementation because of the resulting headache)
  • some of the tests are truly just noise — we absolutely don't need unit tests for things like assert_form_content_type or get_payload for example
  • we're using mocks a lot, and mocks have always felt like a bit of a code smell to me. unavoidable at times but not a reliable substitute for reality

Obviously what we have now is a PITA and I'm not defending it. But I think the sweet spot is much more coarse-grained than this — for example it would be nice to test something like 'given an app with the following manifest, assert that if I call server.respond(...) then the following query results are inlined into the response' without involving Playwright. Perhaps replacing the existing server.test.js files would be an ideal place to start

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.

2 participants