Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 203 additions & 0 deletions packages/lib/src/__tests__/request-handling.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,209 @@ describe('Request Handling', () => {
});
});

describe('Query parameters handling', () => {
it('should parse single query parameter', async () => {
const handler = vi.fn((req) => `Got param: ${req.query.name}`);
workerify.get('/test', handler);

await workerify.listen();

const consumerId = mockChannel.getLastConsumerId();
mockChannel.simulateMessage({
type: 'workerify:handle',
id: 'req-1',
consumerId,
request: {
url: 'http://localhost:3000/test?name=John',
method: 'GET',
headers: {},
body: null,
},
});

await new Promise((resolve) => setTimeout(resolve, 10));

expect(handler).toHaveBeenCalledWith(
expect.objectContaining({
url: 'http://localhost:3000/test?name=John',
method: 'GET',
query: { name: 'John' },
}),
expect.objectContaining({
status: 200,
statusText: 'OK',
}),
);

const responses = mockChannel.lastMessages.filter(
(msg) => msg.type === 'workerify:response',
);
expect(responses[0].body).toBe('Got param: John');
});

it('should parse multiple query parameters', async () => {
const handler = vi.fn((req) => ({
name: req.query.name,
age: req.query.age,
city: req.query.city,
}));
workerify.get('/user', handler);

await workerify.listen();

const consumerId = mockChannel.getLastConsumerId();
mockChannel.simulateMessage({
type: 'workerify:handle',
id: 'req-1',
consumerId,
request: {
url: 'http://localhost:3000/user?name=John&age=30&city=NYC',
method: 'GET',
headers: {},
body: null,
},
});

await new Promise((resolve) => setTimeout(resolve, 10));

expect(handler).toHaveBeenCalledWith(
expect.objectContaining({
query: { name: 'John', age: '30', city: 'NYC' },
}),
expect.objectContaining({
status: 200,
statusText: 'OK',
}),
);

const responses = mockChannel.lastMessages.filter(
(msg) => msg.type === 'workerify:response',
);
expect(responses[0].body).toEqual({
name: 'John',
age: '30',
city: 'NYC',
});
});

it('should handle requests without query parameters', async () => {
const handler = vi.fn(
(req) => `Has params: ${Object.keys(req.query).length > 0}`,
);
workerify.get('/test', handler);

await workerify.listen();

const consumerId = mockChannel.getLastConsumerId();
mockChannel.simulateMessage({
type: 'workerify:handle',
id: 'req-1',
consumerId,
request: {
url: 'http://localhost:3000/test',
method: 'GET',
headers: {},
body: null,
},
});

await new Promise((resolve) => setTimeout(resolve, 10));

expect(handler).toHaveBeenCalledWith(
expect.objectContaining({
query: {},
}),
expect.objectContaining({
status: 200,
statusText: 'OK',
}),
);

const responses = mockChannel.lastMessages.filter(
(msg) => msg.type === 'workerify:response',
);
expect(responses[0].body).toBe('Has params: false');
});

it('should handle URL-encoded query parameters', async () => {
const handler = vi.fn((req) => req.query);
workerify.get('/search', handler);

await workerify.listen();

const consumerId = mockChannel.getLastConsumerId();
mockChannel.simulateMessage({
type: 'workerify:handle',
id: 'req-1',
consumerId,
request: {
url: 'http://localhost:3000/search?q=hello%20world&filter=active%26verified',
method: 'GET',
headers: {},
body: null,
},
});

await new Promise((resolve) => setTimeout(resolve, 10));

expect(handler).toHaveBeenCalledWith(
expect.objectContaining({
query: { q: 'hello world', filter: 'active&verified' },
}),
expect.objectContaining({
status: 200,
statusText: 'OK',
}),
);
});

it('should handle query parameters with route parameters', async () => {
const handler = vi.fn((req) => ({
userId: req.params.id,
filter: req.query.filter,
sort: req.query.sort,
}));
workerify.get('/users/:id', handler);

await workerify.listen();

const consumerId = mockChannel.getLastConsumerId();
mockChannel.simulateMessage({
type: 'workerify:handle',
id: 'req-1',
consumerId,
request: {
url: 'http://localhost:3000/users/123?filter=active&sort=desc',
method: 'GET',
headers: {},
body: null,
},
});

await new Promise((resolve) => setTimeout(resolve, 10));

expect(handler).toHaveBeenCalledWith(
expect.objectContaining({
params: { id: '123' },
query: { filter: 'active', sort: 'desc' },
}),
expect.objectContaining({
status: 200,
statusText: 'OK',
}),
);

const responses = mockChannel.lastMessages.filter(
(msg) => msg.type === 'workerify:response',
);
expect(responses[0].body).toEqual({
userId: '123',
filter: 'active',
sort: 'desc',
});
});
});

describe('Form data handling', () => {
it('should parse form-encoded data in POST requests', async () => {
const handler = vi.fn((req) => req.body);
Expand Down
1 change: 1 addition & 0 deletions packages/lib/src/__tests__/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export function createMockRequest(
},
body: body || null,
params: {},
query: {},
};
}

Expand Down
8 changes: 8 additions & 0 deletions packages/lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ export class Workerify {
// Add params to request
request.params = params || {};

// Parse query parameters
const urlObj = new URL(request.url);
const query: Record<string, string> = {};
for (const [key, value] of urlObj.searchParams.entries()) {
query[key] = value;
}
request.query = query;

// Parse form-encoded data for POST requests
if (
request.method === 'POST' &&
Expand Down
1 change: 1 addition & 0 deletions packages/lib/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface WorkerifyRequest {
headers: Record<string, string>;
body?: WorkerifyBody;
params: Record<string, string>;
query: Record<string, string>;
}

export interface WorkerifyReply {
Expand Down
Loading