Skip to content

Commit baf42dc

Browse files
committed
Drive-by: Ask Claude to stop mocking FormData.
I noticed the use of `MockFormData` and `vi.spyOn` in the test seemed gratuitous, so I coaxed Claude into simplifying.
1 parent 7d3fdde commit baf42dc

File tree

1 file changed

+62
-137
lines changed

1 file changed

+62
-137
lines changed

__tests__/oauth-provider.test.ts

Lines changed: 62 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -91,62 +91,6 @@ class MockExecutionContext implements ExecutionContext {
9191
}
9292
}
9393

94-
// Mock FormData to enable testing token endpoint
95-
class MockFormData implements FormData {
96-
private data: Map<string, string> = new Map();
97-
98-
append(name: string, value: string | Blob, filename?: string): void {
99-
this.data.set(name, value.toString());
100-
}
101-
102-
delete(name: string): void {
103-
this.data.delete(name);
104-
}
105-
106-
get(name: string): FormDataEntryValue | null {
107-
return this.data.get(name) || null;
108-
}
109-
110-
getAll(name: string): FormDataEntryValue[] {
111-
const value = this.data.get(name);
112-
return value ? [value] : [];
113-
}
114-
115-
has(name: string): boolean {
116-
return this.data.has(name);
117-
}
118-
119-
set(name: string, value: string | Blob, filename?: string): void {
120-
this.data.set(name, value.toString());
121-
}
122-
123-
forEach(callbackfn: (value: FormDataEntryValue, key: string, parent: FormData) => void): void {
124-
this.data.forEach((value, key) => callbackfn(value, key, this));
125-
}
126-
127-
*entries(): IterableIterator<[string, FormDataEntryValue]> {
128-
for (const [key, value] of this.data.entries()) {
129-
yield [key, value];
130-
}
131-
}
132-
133-
*keys(): IterableIterator<string> {
134-
for (const key of this.data.keys()) {
135-
yield key;
136-
}
137-
}
138-
139-
*values(): IterableIterator<FormDataEntryValue> {
140-
for (const value of this.data.values()) {
141-
yield value;
142-
}
143-
}
144-
145-
[Symbol.iterator](): IterableIterator<[string, FormDataEntryValue]> {
146-
return this.entries();
147-
}
148-
}
149-
15094
// Simple API handler for testing
15195
class TestApiHandler extends WorkerEntrypoint {
15296
fetch(request: Request) {
@@ -606,21 +550,20 @@ describe('OAuthProvider', () => {
606550
const code = url.searchParams.get('code')!;
607551

608552
// Now exchange the code for tokens
609-
const formData = new MockFormData();
610-
formData.append('grant_type', 'authorization_code');
611-
formData.append('code', code);
612-
formData.append('redirect_uri', redirectUri);
613-
formData.append('client_id', clientId);
614-
formData.append('client_secret', clientSecret);
615-
616-
// Mock FormData in request
617-
vi.spyOn(Request.prototype, 'formData').mockResolvedValue(formData as unknown as FormData);
618-
553+
// Use URLSearchParams which is proper for application/x-www-form-urlencoded
554+
const params = new URLSearchParams();
555+
params.append('grant_type', 'authorization_code');
556+
params.append('code', code);
557+
params.append('redirect_uri', redirectUri);
558+
params.append('client_id', clientId);
559+
params.append('client_secret', clientSecret);
560+
561+
// Use the URLSearchParams object as the body - correctly encoded for Content-Type: application/x-www-form-urlencoded
619562
const tokenRequest = createMockRequest(
620563
'https://example.com/oauth/token',
621564
'POST',
622565
{ 'Content-Type': 'application/x-www-form-urlencoded' },
623-
formData as unknown as FormData
566+
params.toString()
624567
);
625568

626569
const tokenResponse = await oauthProvider.fetch(tokenRequest, mockEnv, mockCtx);
@@ -660,21 +603,18 @@ describe('OAuthProvider', () => {
660603
const code = url.searchParams.get('code')!;
661604

662605
// Now exchange the code without providing redirect_uri
663-
const formData = new MockFormData();
664-
formData.append('grant_type', 'authorization_code');
665-
formData.append('code', code);
606+
const params = new URLSearchParams();
607+
params.append('grant_type', 'authorization_code');
608+
params.append('code', code);
666609
// redirect_uri intentionally omitted
667-
formData.append('client_id', clientId);
668-
formData.append('client_secret', clientSecret);
669-
670-
// Mock FormData in request
671-
vi.spyOn(Request.prototype, 'formData').mockResolvedValue(formData as unknown as FormData);
610+
params.append('client_id', clientId);
611+
params.append('client_secret', clientSecret);
672612

673613
const tokenRequest = createMockRequest(
674614
'https://example.com/oauth/token',
675615
'POST',
676616
{ 'Content-Type': 'application/x-www-form-urlencoded' },
677-
formData as unknown as FormData
617+
params.toString()
678618
);
679619

680620
const tokenResponse = await oauthProvider.fetch(tokenRequest, mockEnv, mockCtx);
@@ -729,22 +669,19 @@ describe('OAuthProvider', () => {
729669
const code = url.searchParams.get('code')!;
730670

731671
// Now exchange the code without providing redirect_uri
732-
const formData = new MockFormData();
733-
formData.append('grant_type', 'authorization_code');
734-
formData.append('code', code);
672+
const params = new URLSearchParams();
673+
params.append('grant_type', 'authorization_code');
674+
params.append('code', code);
735675
// redirect_uri intentionally omitted
736-
formData.append('client_id', clientId);
737-
formData.append('client_secret', clientSecret);
738-
formData.append('code_verifier', codeVerifier);
739-
740-
// Mock FormData in request
741-
vi.spyOn(Request.prototype, 'formData').mockResolvedValue(formData as unknown as FormData);
676+
params.append('client_id', clientId);
677+
params.append('client_secret', clientSecret);
678+
params.append('code_verifier', codeVerifier);
742679

743680
const tokenRequest = createMockRequest(
744681
'https://example.com/oauth/token',
745682
'POST',
746683
{ 'Content-Type': 'application/x-www-form-urlencoded' },
747-
formData as unknown as FormData
684+
params.toString()
748685
);
749686

750687
const tokenResponse = await oauthProvider.fetch(tokenRequest, mockEnv, mockCtx);
@@ -772,20 +709,18 @@ describe('OAuthProvider', () => {
772709
const code = new URL(location).searchParams.get('code')!;
773710

774711
// Exchange for tokens
775-
const formData = new MockFormData();
776-
formData.append('grant_type', 'authorization_code');
777-
formData.append('code', code);
778-
formData.append('redirect_uri', redirectUri);
779-
formData.append('client_id', clientId);
780-
formData.append('client_secret', clientSecret);
781-
782-
vi.spyOn(Request.prototype, 'formData').mockResolvedValue(formData as unknown as FormData);
712+
const params = new URLSearchParams();
713+
params.append('grant_type', 'authorization_code');
714+
params.append('code', code);
715+
params.append('redirect_uri', redirectUri);
716+
params.append('client_id', clientId);
717+
params.append('client_secret', clientSecret);
783718

784719
const tokenRequest = createMockRequest(
785720
'https://example.com/oauth/token',
786721
'POST',
787722
{ 'Content-Type': 'application/x-www-form-urlencoded' },
788-
formData as unknown as FormData
723+
params.toString()
789724
);
790725

791726
const tokenResponse = await oauthProvider.fetch(tokenRequest, mockEnv, mockCtx);
@@ -847,20 +782,18 @@ describe('OAuthProvider', () => {
847782
const code = new URL(location).searchParams.get('code')!;
848783

849784
// Exchange for tokens
850-
const formData = new MockFormData();
851-
formData.append('grant_type', 'authorization_code');
852-
formData.append('code', code);
853-
formData.append('redirect_uri', redirectUri);
854-
formData.append('client_id', clientId);
855-
formData.append('client_secret', clientSecret);
856-
857-
vi.spyOn(Request.prototype, 'formData').mockResolvedValue(formData as unknown as FormData);
785+
const params = new URLSearchParams();
786+
params.append('grant_type', 'authorization_code');
787+
params.append('code', code);
788+
params.append('redirect_uri', redirectUri);
789+
params.append('client_id', clientId);
790+
params.append('client_secret', clientSecret);
858791

859792
const tokenRequest = createMockRequest(
860793
'https://example.com/oauth/token',
861794
'POST',
862795
{ 'Content-Type': 'application/x-www-form-urlencoded' },
863-
formData as unknown as FormData
796+
params.toString()
864797
);
865798

866799
const tokenResponse = await oauthProvider.fetch(tokenRequest, mockEnv, mockCtx);
@@ -874,19 +807,17 @@ describe('OAuthProvider', () => {
874807

875808
it('should issue new tokens with refresh token', async () => {
876809
// Use the refresh token to get a new access token
877-
const formData = new MockFormData();
878-
formData.append('grant_type', 'refresh_token');
879-
formData.append('refresh_token', refreshToken);
880-
formData.append('client_id', clientId);
881-
formData.append('client_secret', clientSecret);
882-
883-
vi.spyOn(Request.prototype, 'formData').mockResolvedValue(formData as unknown as FormData);
810+
const params = new URLSearchParams();
811+
params.append('grant_type', 'refresh_token');
812+
params.append('refresh_token', refreshToken);
813+
params.append('client_id', clientId);
814+
params.append('client_secret', clientSecret);
884815

885816
const refreshRequest = createMockRequest(
886817
'https://example.com/oauth/token',
887818
'POST',
888819
{ 'Content-Type': 'application/x-www-form-urlencoded' },
889-
formData as unknown as FormData
820+
params.toString()
890821
);
891822

892823
const refreshResponse = await oauthProvider.fetch(refreshRequest, mockEnv, mockCtx);
@@ -913,39 +844,35 @@ describe('OAuthProvider', () => {
913844

914845
it('should allow using the previous refresh token once', async () => {
915846
// Use the refresh token to get a new access token (first refresh)
916-
const formData1 = new MockFormData();
917-
formData1.append('grant_type', 'refresh_token');
918-
formData1.append('refresh_token', refreshToken);
919-
formData1.append('client_id', clientId);
920-
formData1.append('client_secret', clientSecret);
921-
922-
vi.spyOn(Request.prototype, 'formData').mockResolvedValueOnce(formData1 as unknown as FormData);
847+
const params1 = new URLSearchParams();
848+
params1.append('grant_type', 'refresh_token');
849+
params1.append('refresh_token', refreshToken);
850+
params1.append('client_id', clientId);
851+
params1.append('client_secret', clientSecret);
923852

924853
const refreshRequest1 = createMockRequest(
925854
'https://example.com/oauth/token',
926855
'POST',
927856
{ 'Content-Type': 'application/x-www-form-urlencoded' },
928-
formData1 as unknown as FormData
857+
params1.toString()
929858
);
930859

931860
const refreshResponse1 = await oauthProvider.fetch(refreshRequest1, mockEnv, mockCtx);
932861
const newTokens1 = await refreshResponse1.json();
933862
const newRefreshToken = newTokens1.refresh_token;
934863

935864
// Now try to use the original refresh token again (simulating a retry after failure)
936-
const formData2 = new MockFormData();
937-
formData2.append('grant_type', 'refresh_token');
938-
formData2.append('refresh_token', refreshToken); // Original token
939-
formData2.append('client_id', clientId);
940-
formData2.append('client_secret', clientSecret);
941-
942-
vi.spyOn(Request.prototype, 'formData').mockResolvedValueOnce(formData2 as unknown as FormData);
865+
const params2 = new URLSearchParams();
866+
params2.append('grant_type', 'refresh_token');
867+
params2.append('refresh_token', refreshToken); // Original token
868+
params2.append('client_id', clientId);
869+
params2.append('client_secret', clientSecret);
943870

944871
const refreshRequest2 = createMockRequest(
945872
'https://example.com/oauth/token',
946873
'POST',
947874
{ 'Content-Type': 'application/x-www-form-urlencoded' },
948-
formData2 as unknown as FormData
875+
params2.toString()
949876
);
950877

951878
const refreshResponse2 = await oauthProvider.fetch(refreshRequest2, mockEnv, mockCtx);
@@ -1005,20 +932,18 @@ describe('OAuthProvider', () => {
1005932
const code = new URL(location).searchParams.get('code')!;
1006933

1007934
// Exchange for tokens
1008-
const formData = new MockFormData();
1009-
formData.append('grant_type', 'authorization_code');
1010-
formData.append('code', code);
1011-
formData.append('redirect_uri', redirectUri);
1012-
formData.append('client_id', clientId);
1013-
formData.append('client_secret', clientSecret);
1014-
1015-
vi.spyOn(Request.prototype, 'formData').mockResolvedValue(formData as unknown as FormData);
935+
const params = new URLSearchParams();
936+
params.append('grant_type', 'authorization_code');
937+
params.append('code', code);
938+
params.append('redirect_uri', redirectUri);
939+
params.append('client_id', clientId);
940+
params.append('client_secret', clientSecret);
1016941

1017942
const tokenRequest = createMockRequest(
1018943
'https://example.com/oauth/token',
1019944
'POST',
1020945
{ 'Content-Type': 'application/x-www-form-urlencoded' },
1021-
formData as unknown as FormData
946+
params.toString()
1022947
);
1023948

1024949
const tokenResponse = await oauthProvider.fetch(tokenRequest, mockEnv, mockCtx);

0 commit comments

Comments
 (0)