1
1
/* eslint-disable no-console */
2
2
import { getLocal } from 'mockttp' ;
3
3
import portfinder from 'portfinder' ;
4
+ import _ from 'lodash' ;
5
+
6
+ /**
7
+ * Utility function to handle direct fetch requests
8
+ * @param {string } url - The URL to fetch from
9
+ * @param {string } method - The HTTP method
10
+ * @param {Headers } headers - Request headers
11
+ * @param {Object } requestBody - The request body object
12
+ * @returns {Promise<{statusCode: number, body: string}> } Response object
13
+ */
14
+ const handleDirectFetch = async ( url , method , headers , requestBody ) => {
15
+ try {
16
+ const response = await global . fetch ( url , {
17
+ method,
18
+ headers,
19
+ body : [ 'POST' , 'PUT' , 'PATCH' ] . includes ( method ) ? requestBody : undefined ,
20
+ } ) ;
21
+
22
+ const responseBody = await response . text ( ) ;
23
+ return {
24
+ statusCode : response . status ,
25
+ body : responseBody ,
26
+ } ;
27
+ } catch ( error ) {
28
+ console . error ( 'Error forwarding request:' , url ) ;
29
+ return {
30
+ statusCode : 500 ,
31
+ body : JSON . stringify ( { error : 'Failed to forward request' } ) ,
32
+ } ;
33
+ }
34
+ } ;
35
+
4
36
/**
5
37
* Starts the mock server and sets up mock events.
6
38
*
@@ -19,53 +51,99 @@ export const startMockServer = async (events, port) => {
19
51
. forGet ( '/health-check' )
20
52
. thenReply ( 200 , 'Mock server is running' ) ;
21
53
22
- for ( const method in events ) {
23
- const methodEvents = events [ method ] ;
24
-
25
- console . log ( `Setting up mock events for ${ method } requests...` ) ;
26
-
27
- for ( const {
28
- urlEndpoint,
29
- response,
30
- requestBody,
31
- responseCode,
32
- } of methodEvents ) {
33
- console . log ( `Mocking ${ method } request to: ${ urlEndpoint } ` ) ;
34
- console . log ( `Response status: ${ responseCode } ` ) ;
35
- console . log ( 'Response:' , response ) ;
36
- if ( requestBody ) {
37
- console . log ( `POST request body:` , requestBody ) ;
38
- }
54
+ // Handle all /proxy requests
55
+ await mockServer
56
+ . forAnyRequest ( )
57
+ . matching ( ( request ) => request . path . startsWith ( '/proxy' ) )
58
+ . thenCallback ( async ( request ) => {
59
+ const urlEndpoint = new URL ( request . url ) . searchParams . get ( 'url' ) ;
60
+ const method = request . method ;
39
61
40
- if ( method === 'GET' ) {
41
- await mockServer
42
- . forGet ( '/proxy' )
43
- . withQuery ( { url : urlEndpoint } )
44
- . thenReply ( responseCode , JSON . stringify ( response ) ) ;
45
- }
62
+ // Find matching mock event
63
+ const methodEvents = events [ method ] || [ ] ;
64
+ const matchingEvent = methodEvents . find (
65
+ ( event ) => event . urlEndpoint === urlEndpoint ,
66
+ ) ;
67
+
68
+ if ( matchingEvent ) {
69
+ console . log ( `Mocking ${ method } request to: ${ urlEndpoint } ` ) ;
70
+ console . log ( `Response status: ${ matchingEvent . responseCode } ` ) ;
71
+ console . log ( 'Response:' , matchingEvent . response ) ;
72
+
73
+ // For POST requests, verify the request body if specified
74
+ if ( method === 'POST' && matchingEvent . requestBody ) {
75
+ const requestBodyJson = await request . body . getJson ( ) ;
76
+
77
+ // Ensure both objects exist before comparison
78
+ if ( ! requestBodyJson || ! matchingEvent . requestBody ) {
79
+ console . log ( 'Request body validation failed: Missing request body' ) ;
80
+ return {
81
+ statusCode : 400 ,
82
+ body : JSON . stringify ( { error : 'Missing request body' } ) ,
83
+ } ;
84
+ }
85
+
86
+ // We don't use _.isEqual because we want to ignore extra fields in the request body
87
+ const matches = _ . isMatch ( requestBodyJson , matchingEvent . requestBody ) ;
46
88
47
- if ( method === 'POST' ) {
48
- await mockServer
49
- . forPost ( '/proxy' )
50
- . withQuery ( { url : urlEndpoint } )
51
- . withJsonBodyIncluding ( requestBody || { } )
52
- . thenReply ( responseCode , JSON . stringify ( response ) ) ;
89
+ if ( ! matches ) {
90
+ console . log ( 'Request body validation failed:' ) ;
91
+ console . log (
92
+ 'Expected:' ,
93
+ JSON . stringify ( matchingEvent . requestBody , null , 2 ) ,
94
+ ) ;
95
+ console . log ( 'Received:' , JSON . stringify ( requestBodyJson , null , 2 ) ) ;
96
+ console . log (
97
+ 'Differences:' ,
98
+ JSON . stringify (
99
+ _ . differenceWith (
100
+ [ requestBodyJson ] ,
101
+ [ matchingEvent . requestBody ] ,
102
+ _ . isEqual ,
103
+ ) ,
104
+ null ,
105
+ 2 ,
106
+ ) ,
107
+ ) ;
108
+
109
+ return {
110
+ statusCode : 404 ,
111
+ body : JSON . stringify ( {
112
+ error : 'Request body validation failed' ,
113
+ expected : matchingEvent . requestBody ,
114
+ received : requestBodyJson ,
115
+ } ) ,
116
+ } ;
117
+ }
118
+ }
119
+
120
+ return {
121
+ statusCode : matchingEvent . responseCode ,
122
+ body : JSON . stringify ( matchingEvent . response ) ,
123
+ } ;
53
124
}
54
- }
55
- }
56
125
57
- await mockServer . forUnmatchedRequest ( ) . thenPassThrough ( {
58
- beforeRequest : async ( { url, method } ) => {
59
- const returnUrl = new URL ( url ) . searchParams . get ( 'url' ) || url ;
126
+ // If no matching mock found, pass through to actual endpoint
60
127
const updatedUrl =
61
128
device . getPlatform ( ) === 'android'
62
- ? returnUrl . replace ( 'localhost' , '127.0.0.1' )
63
- : returnUrl ;
64
- console . log ( `Mock proxy forwarding request to: ${ updatedUrl } ` ) ;
65
- return { url : updatedUrl } ;
66
- } ,
67
- } ) ;
129
+ ? urlEndpoint . replace ( 'localhost' , '127.0.0.1' )
130
+ : urlEndpoint ;
131
+
132
+ return handleDirectFetch (
133
+ updatedUrl ,
134
+ method ,
135
+ request . headers ,
136
+ method === 'POST' ? await request . body . getText ( ) : undefined
137
+ ) ;
138
+ } ) ;
68
139
140
+ // In case any other requests are made, pass them through to the actual endpoint
141
+ await mockServer . forUnmatchedRequest ( ) . thenCallback ( async ( request ) => handleDirectFetch (
142
+ request . url ,
143
+ request . method ,
144
+ request . headers ,
145
+ await request . body . getText ( )
146
+ ) ) ;
69
147
70
148
return mockServer ;
71
149
} ;
0 commit comments