Skip to content

Migrate SDK HTTP tests to MSW and preserve FormData payloads - #145

Open
base44-os-gremlins[bot] wants to merge 10 commits into
mainfrom
feat/msw-test-infrastructure
Open

Migrate SDK HTTP tests to MSW and preserve FormData payloads#145
base44-os-gremlins[bot] wants to merge 10 commits into
mainfrom
feat/msw-test-infrastructure

Conversation

@base44-os-gremlins

@base44-os-gremlins base44-os-gremlins Bot commented Mar 16, 2026

Copy link
Copy Markdown

The SDK’s HTTP tests now run through MSW against the actual Axios/fetch clients. The migration preserves the current-main suite, enforces request/body/header/query expectations, and fails unmatched traffic even when SDK code catches the network error. The earlier migration omitted a Nock test file and weakened several body assertions; this update covers the current test surface and removes transport response stubs.

Stronger multipart assertions exposed a production bug: functions.invoke(FormData) rebuilt an empty form and lost fields. A separate fix preserves caller-supplied FormData, including repeated keys, empty values and binary files. The old implementation fails the new wire assertions; the fixed implementation passes.

Validation:

  • Current-main baseline: 301 unit tests. Updated suite: 307 passing tests across 22 files, plus API type checks in npm test.
  • Node 20 test run, TypeScript build and source ESLint pass locally. ESLint’s existing scope is src, not the test files.
  • Six deliberate negative controls fail for incorrect bodies, headers, queries, missing calls, resolver inspection errors and swallowed unhandled requests.
  • Source coverage and behavior comparison, before/after regression output, authoring documentation and exact-commit evidence are available in goal #873.
  • Live E2E is an explicit, separate command; no live platform credentials or mutations were used for this verification. Hosted checks remain separately visible on this PR.

tests/README.md documents adding handlers, request assertions, strict teardown, multipart inspection and the unit/live-E2E boundary. Goal c9/c10 evidence is prepared for human acceptance; this PR has not been merged.

Review scope

The actual GitHub comparison has been verified against current main (4ebcc76) and assessed head (1d574ef): 27 files, with matching file statuses and line counts. The earlier 92-file view used a stale March PR base; refreshing the base removed 65 unrelated paths without changing code.

  • 17 unit test files: MSW migration and coverage additions.
  • 4 fixture/setup files and 2 Vitest configs.
  • 2 package/dependency files and the handler guide.
  • One production file, src/modules/functions.ts: preserve direct FormData (5 added/4 removed lines).

The goal review package includes the declared scope, machine-checked PR diff, and review map. Human acceptance remains pending.

Migrate all unit tests from nock-based HTTP interception to a proper
MSW (Mock Service Worker) mock server using msw/node + @mswjs/interceptors.

Changes:
- Add msw@2 as devDependency, remove nock
- Create tests/mocks/server.ts: MSW server with documentation on adding handlers
- Update tests/setup.js: start/stop/reset MSW server in beforeAll/afterEach/afterAll
- Port all 7 nock-based test files to per-test server.use() handlers:
  entities, auth, functions, connectors, integrations, custom-integrations, client
- Replace vi.stubGlobal("fetch", ...) in functions.test.ts with MSW handlers
- Add request capture pattern for asserting headers (Authorization, Base44-State)
- Use RegExp patterns for MSW handlers where operationId contains URL-unsafe chars

All 116 unit tests pass (npm run test:unit exits 0).

Goal: https://github.com/base44-dev/gremlins/issues/873

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Mar 16, 2026

Copy link
Copy Markdown

🚀 Package Preview Available!


Install this PR's preview build with npm:

npm i @base44-preview/sdk@0.8.48-pr.145.e1cc9fe

Prefer not to change any import paths? Install using npm alias so your code still imports @base44/sdk:

npm i "@base44/sdk@npm:@base44-preview/sdk@0.8.48-pr.145.e1cc9fe"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "@base44/sdk": "npm:@base44-preview/sdk@0.8.48-pr.145.e1cc9fe"
  }
}

Preview published to npm registry — try new features instantly!

Copilot AI 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.

Pull request overview

This PR migrates the unit test suite from nock-based HTTP interception to an MSW (msw/node, v2) mock server, centralizing request mocking and enabling per-test handlers via server.use().

Changes:

  • Added a shared MSW Node server (tests/mocks/server.ts) and wired its lifecycle into the Vitest global setup (tests/setup.js).
  • Ported multiple unit test files from nock to MSW handlers (http.* + HttpResponse), including request capture for header assertions.
  • Updated dependencies to introduce msw (and intended to remove nock).

Reviewed changes

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

Show a summary per file
File Description
tests/unit/integrations.test.js Migrates integration endpoint tests from nock to MSW handlers.
tests/unit/functions.test.ts Replaces nock + fetch stubbing with MSW handlers; adds header capture assertions.
tests/unit/entities.test.ts Migrates entities module tests to MSW with query/body validation in handlers.
tests/unit/custom-integrations.test.ts Migrates custom integrations tests to MSW; adds URL matching helper for encoded operation IDs.
tests/unit/connectors.test.ts Migrates connector token retrieval tests from nock to MSW.
tests/unit/client.test.js Updates client tests to use MSW patterns and ensures cleanup is called.
tests/unit/auth.test.js Migrates auth tests from nock to MSW; uses MSW to validate auth header behavior in some flows.
tests/setup.js Starts/stops the MSW server for the suite and resets handlers after each test.
tests/mocks/server.ts Introduces the shared MSW Node server and handler authoring documentation.
package.json Adds msw devDependency (and removes nock).
package-lock.json Adds MSW dependency entries, but still includes nock in root devDependencies.

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

Comment thread tests/setup.js Outdated
// Global beforeAll and afterAll hooks
// MSW server lifecycle
beforeAll(() => {
server.listen({ onUnhandledRequest: 'warn' });

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

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

server.listen({ onUnhandledRequest: 'warn' }) will warn and then passthrough unmatched requests to the real network in MSW v2. For unit tests, this can make the suite flaky and can accidentally hit real endpoints. Prefer onUnhandledRequest: 'error' (or a custom handler) so unmocked requests fail fast.

Suggested change
server.listen({ onUnhandledRequest: 'warn' });
server.listen({ onUnhandledRequest: 'error' });

Copilot uses AI. Check for mistakes.
Comment thread tests/unit/client.test.js Outdated
import { createClient, createClientFromRequest } from '../../src/index.ts';
import { describe, test, expect, beforeEach, afterEach } from 'vitest';
import nock from 'nock';
import { describe, test, expect, afterEach } from 'vitest';

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

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

Unused import: afterEach is imported from vitest but never used in this file. Remove it to keep the test file clean and avoid lint warnings.

Suggested change
import { describe, test, expect, afterEach } from 'vitest';
import { describe, test, expect } from 'vitest';

Copilot uses AI. Check for mistakes.
Comment thread tests/unit/custom-integrations.test.ts Outdated
Comment on lines +12 to +17
// The SDK URL-encodes only curly braces in operationIds (not : or /)
function sdkOperationUrl(slug: string, operationId: string): string {
const encoded = operationId.replace(/{/g, '%7B').replace(/}/g, '%7D');
return `${customBase}/${slug}/${encoded}`;
}

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

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

sdkOperationUrl() is declared but never used. Consider removing it (or using it in the tests) to avoid dead code and keep the helper section focused.

Suggested change
// The SDK URL-encodes only curly braces in operationIds (not : or /)
function sdkOperationUrl(slug: string, operationId: string): string {
const encoded = operationId.replace(/{/g, '%7B').replace(/}/g, '%7D');
return `${customBase}/${slug}/${encoded}`;
}

Copilot uses AI. Check for mistakes.
Comment thread tests/unit/auth.test.js Outdated
Comment on lines 129 to 143
test('should use appBaseUrl for login redirect when provided', () => {
const customAppBaseUrl = 'https://custom-app.example.com';
const clientWithCustomUrl = createClient({
serverUrl,
appId,
appBaseUrl: customAppBaseUrl,
});
const clientWithCustomUrl = createClient({ serverUrl, appId, appBaseUrl: customAppBaseUrl });

// Mock window.location
const originalWindow = global.window;
const mockLocation = { href: '' };
global.window = {
location: mockLocation
};
global.window = { location: mockLocation };

const nextUrl = 'https://example.com/dashboard';
clientWithCustomUrl.auth.redirectToLogin(nextUrl);
clientWithCustomUrl.auth.redirectToLogin('https://example.com/dashboard');

// Verify the redirect URL uses the custom appBaseUrl
expect(mockLocation.href).toBe(
`${customAppBaseUrl}/login?from_url=${encodeURIComponent(nextUrl)}`
`${customAppBaseUrl}/login?from_url=${encodeURIComponent('https://example.com/dashboard')}`
);

// Restore window
global.window = originalWindow;

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

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

This test creates an additional client (clientWithCustomUrl) but never calls cleanup(). Since createClient().cleanup() disconnects sockets/analytics, leaving it uncalled can leak resources across tests. Add a try/finally (or explicit cleanup at the end) to ensure the client is always cleaned up.

Copilot uses AI. Check for mistakes.
Comment thread tests/unit/auth.test.js Outdated
Comment on lines 146 to 160
test('should use relative URL for login redirect when appBaseUrl is not provided', () => {
// Create a client without appBaseUrl
const clientWithoutAppBaseUrl = createClient({
serverUrl,
appId,
});
const clientWithoutAppBaseUrl = createClient({ serverUrl, appId });

// Mock window.location
const originalWindow = global.window;
const mockLocation = { href: '', origin: 'https://current-app.com' };
global.window = {
location: mockLocation
};
global.window = { location: mockLocation };

const nextUrl = 'https://example.com/dashboard';
clientWithoutAppBaseUrl.auth.redirectToLogin(nextUrl);
clientWithoutAppBaseUrl.auth.redirectToLogin('https://example.com/dashboard');

// Verify the redirect URL uses a relative path (no appBaseUrl prefix)
expect(mockLocation.href).toBe(
`/login?from_url=${encodeURIComponent(nextUrl)}`
`/login?from_url=${encodeURIComponent('https://example.com/dashboard')}`
);

// Restore window
global.window = originalWindow;
});

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

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

This test creates an additional client (clientWithoutAppBaseUrl) but never calls cleanup(). Add explicit cleanup (ideally in a finally) to avoid leaking sockets/analytics state across the test suite.

Copilot uses AI. Check for mistakes.
Comment thread package.json
"eslint": "^9.39.2",
"eslint-plugin-import": "^2.32.0",
"nock": "^13.4.0",
"msw": "^2.12.11",

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

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

nock was removed from devDependencies, but the repo still contains at least one unit test that imports/uses nock (e.g. tests/unit/integrations.test.ts). This will break fresh installs/CI runs. Either migrate the remaining nock-based tests to MSW as well, or keep nock as a devDependency until the migration is complete.

Suggested change
"msw": "^2.12.11",
"msw": "^2.12.11",
"nock": "^13.5.0",

Copilot uses AI. Check for mistakes.
@netanelgilad netanelgilad changed the title feat(tests): replace nock with MSW mock server infrastructure Migrate SDK HTTP tests to MSW and preserve FormData payloads Sep 8, 2026
@github-actions github-actions Bot added the docs-draft PR has auto-drafted documentation suggestions label Sep 8, 2026
@base44-os-gremlins

Copy link
Copy Markdown
Author

Decision summary

This PR replaces Nock-style per-test responses with one reusable, stateful MSW
Base44 platform. Tests arrange scoped domain facts, call the real SDK, and assert
SDK results, state transitions, and meaningful request details. Test files do
not define HTTP handlers, response bodies, statuses, or proxy envelopes.

Representative flow:

platform.given.app(appId).entities.records("Todo", []);
const created = await base44.entities.Todo.create({ title: "New task" });
await expect(base44.entities.Todo.get(created.id)).resolves.toEqual(created);
await expect(base44.entities.Todo.list()).resolves.toContainEqual(created);

Central handlers cover entities, auth/reset, functions, agents, integrations,
connectors, analytics, actors, app settings, and retained legacy SDK surfaces.
State is scoped according to the pinned backend evidence and reset before/after
each test. Named faults own error serialization and one-shot recovery. The
existing FormData production fix is preserved, including repeated fields and
binary file bytes.

Validation

  • Exact head: a5d60853f24330c51ae53ec0e9f20f2b2880337f.
  • npm test: API types plus 23 files / 332 tests passed.
  • Node 20.20.2: 23 files / 332 tests passed.
  • Build, source-only ESLint, strict mock-platform TypeScript, changed-file
    Prettier, coverage, and git diff --check passed.
  • Production-source coverage remains above main@4ebcc76 on statements,
    branches, and functions.
  • The actual GitHub PR-diff verifier passed against base 4ebcc76, head
    a5d6085, and all 40 declared paths with matching statuses and line counts.

The complete code/proof package and explicit backend-fidelity limits are in
Goal #873.

Scope and limitations

The diff is limited to the MSW dependency/setup, centralized platform and
migrated SDK tests, E2E isolation/docs, and the 5-addition/4-deletion FormData
fix in src/modules/functions.ts. Full production policy/schema validation,
live model execution, and explicitly labeled legacy SDK endpoints are outside
the in-memory platform's fidelity claim.

Hosted test, lint, dependency audit, and package preview checks pass. The two
Claude automation checks reject the authorized publishing bot before analysis;
their annotations report the actor allowlist, not a source/test failure.

Human review and acceptance remain outstanding. Do not merge automatically.

@base44-os-gremlins

Copy link
Copy Markdown
Author

Review remediation update for exact published head a5d60853f24330c51ae53ec0e9f20f2b2880337f:

The previous ready recommendation is retracted. I audited all six open March 26 Copilot conversations individually. Each underlying finding is addressed in the current head, but I have not dismissed or resolved any conversation and do not infer acceptance:

Fresh focused proof at this head passes 5 files/92 tests (client, auth, custom integrations, core integrations and architecture guard); the published full proof remains 23 files/332 tests on default and Node 20. The scoped bridge does not expose GitHub's GraphQL-only thread-resolution mutation, so a maintainer must verify these links and click Resolve conversation for each genuinely addressed finding.

The two red Claude jobs are policy failures before analysis, not source failures. Both reject bot actor base44-os-gremlins. The minimal repository-policy action is to add allowed_bots: base44-os-gremlins to the existing with: block in both .github/workflows/claude-code-review.yml and .github/workflows/claude-docs-drafter.yml, then rerun both jobs at this SHA. I did not widen this PR's predeclared 40-path scope to include workflow/security policy, and I am not proposing *.

No merge, review dismissal or human approval is claimed. The PR remains needs-work until all six conversations are resolved and both Claude checks are green.

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

Labels

docs-draft PR has auto-drafted documentation suggestions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants