@@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach } from "vitest";
22import { makeClient } from "./client-http" ;
33import { makeAuthHeaders } from "@madatdata/base-client" ;
44import { setupMswServerTestHooks } from "@madatdata/test-helpers/msw-server-hooks" ;
5+ import { setupMemo } from "@madatdata/test-helpers/setup-memo" ;
56import { shouldSkipIntegrationTests } from "@madatdata/test-helpers/env-config" ;
67import { rest } from "msw" ;
78
@@ -10,8 +11,11 @@ import type { HTTPStrategies } from "./strategies/types";
1011import {
1112 skipParsingFieldsFromResponse ,
1213 parseFieldsFromResponseBodyJSONFieldsKey ,
14+ skipTransformFetchOptions ,
1315} from "." ;
1416
17+ import type { IsomorphicRequest } from "@mswjs/interceptors" ;
18+
1519// NOTE: Previously, the default http-client was hardcoded for Splitgraph, which
1620// is why all the tests reflect its shape. But we don't want this package to
1721// depend on db-splitgraph, so we copy the strategies from DbSplitgraph.makeHTTPClient
@@ -45,41 +49,42 @@ const splitgraphClientOptions = {
4549 } ,
4650 parseFieldsFromResponse : skipParsingFieldsFromResponse ,
4751 parseFieldsFromResponseBodyJSON : parseFieldsFromResponseBodyJSONFieldsKey ,
52+ transformFetchOptions : skipTransformFetchOptions ,
4853 } as HTTPStrategies ,
4954} ;
5055
56+ const minSuccessfulJSON = {
57+ success : true ,
58+ command : "SELECT" ,
59+ rowCount : 1 ,
60+ rows : [
61+ {
62+ "?column?" : 1 ,
63+ } ,
64+ ] ,
65+ fields : [
66+ {
67+ name : "?column?" ,
68+ tableID : 0 ,
69+ columnID : 0 ,
70+ dataTypeID : 23 ,
71+ dataTypeSize : 4 ,
72+ dataTypeModifier : - 1 ,
73+ format : "text" ,
74+ formattedType : "INT4" ,
75+ } ,
76+ ] ,
77+ executionTime : "128ms" ,
78+ executionTimeHighRes : "0s 128.383115ms" ,
79+ } ;
80+
5181describe ( "makeClient creates client which" , ( ) => {
5282 setupMswServerTestHooks ( ) ;
5383
5484 beforeEach ( ( { mswServer } ) => {
5585 mswServer ?. use (
5686 rest . post ( defaultHost . baseUrls . sql + "/ddn" , ( _req , res , ctx ) => {
57- return res (
58- ctx . json ( {
59- success : true ,
60- command : "SELECT" ,
61- rowCount : 1 ,
62- rows : [
63- {
64- "?column?" : 1 ,
65- } ,
66- ] ,
67- fields : [
68- {
69- name : "?column?" ,
70- tableID : 0 ,
71- columnID : 0 ,
72- dataTypeID : 23 ,
73- dataTypeSize : 4 ,
74- dataTypeModifier : - 1 ,
75- format : "text" ,
76- formattedType : "INT4" ,
77- } ,
78- ] ,
79- executionTime : "128ms" ,
80- executionTimeHighRes : "0s 128.383115ms" ,
81- } )
82- ) ;
87+ return res ( ctx . json ( minSuccessfulJSON ) ) ;
8388 } )
8489 ) ;
8590 } ) ;
@@ -125,22 +130,125 @@ describe("makeClient creates client which", () => {
125130 } ) ;
126131} ) ;
127132
128- const makeStubClient = ( ) => {
133+ const stubStrategies : HTTPStrategies = {
134+ makeFetchOptions : ( ) => ( {
135+ method : "POST" ,
136+ } ) ,
137+ makeQueryURL : async ( { host, database } ) => {
138+ return Promise . resolve ( host . baseUrls . sql + "/" + database . dbname ) ;
139+ } ,
140+ parseFieldsFromResponse : skipParsingFieldsFromResponse ,
141+ parseFieldsFromResponseBodyJSON : parseFieldsFromResponseBodyJSONFieldsKey ,
142+ transformFetchOptions : skipTransformFetchOptions ,
143+ } ;
144+
145+ const makeStubClient = ( opts ?: { strategies ?: Partial < HTTPStrategies > } ) => {
129146 return makeClient ( {
130147 credential : null ,
131148 strategies : {
132- makeFetchOptions : ( ) => ( {
133- method : "POST" ,
134- } ) ,
135- makeQueryURL : async ( { host, database } ) => {
136- return Promise . resolve ( host . baseUrls . sql + "/" + database . dbname ) ;
137- } ,
138- parseFieldsFromResponse : skipParsingFieldsFromResponse ,
139- parseFieldsFromResponseBodyJSON : parseFieldsFromResponseBodyJSONFieldsKey ,
149+ ...stubStrategies ,
150+ ...opts ?. strategies ,
140151 } ,
141152 } ) ;
142153} ;
143154
155+ describe ( "client implements strategies" , ( ) => {
156+ setupMswServerTestHooks ( ) ;
157+ setupMemo ( ) ;
158+
159+ beforeEach ( ( testCtx ) => {
160+ const { mswServer, useTestMemo } = testCtx ;
161+
162+ const reqMemo = useTestMemo ! < string , IsomorphicRequest > ( ) ;
163+
164+ mswServer ?. use (
165+ rest . post ( "http://localhost/default/q/fingerprint" , ( req , res , ctx ) => {
166+ reqMemo . set ( testCtx . meta . id , req ) ;
167+ return res ( ctx . status ( 200 ) , ctx . json ( minSuccessfulJSON ) ) ;
168+ } ) ,
169+ rest . post ( "http://localhost/transformed" , ( req , res , ctx ) => {
170+ reqMemo . set ( testCtx . meta . id , req ) ;
171+ return res ( ctx . status ( 200 ) , ctx . json ( minSuccessfulJSON ) ) ;
172+ } )
173+ ) ;
174+ } ) ;
175+
176+ it ( "transforms request headers" , async ( { useTestMemo, meta } ) => {
177+ const client = makeStubClient ( {
178+ strategies : {
179+ makeQueryURL : ( ) =>
180+ Promise . resolve ( "http://localhost/default/q/fingerprint" ) ,
181+ makeFetchOptions : ( ) => {
182+ return {
183+ method : "POST" ,
184+ headers : {
185+ "initial-header" : "stays" ,
186+ "override-header" : "will-not-be-set-to-this" ,
187+ } ,
188+ } ;
189+ } ,
190+ transformFetchOptions ( { input, init } ) {
191+ return {
192+ input,
193+ init : {
194+ ...init ,
195+ headers : {
196+ ...init ?. headers ,
197+ "new-header" : "was-not-in-make-fetch-options" ,
198+ "override-header" : "is-different-from-fetch-options" ,
199+ } ,
200+ } ,
201+ } ;
202+ } ,
203+ } ,
204+ } ) ;
205+
206+ const { error } = await client . execute < { } > ( "SELECT 1;" ) ;
207+
208+ const reqMemo = useTestMemo ! ( ) . get ( meta . id ) as IsomorphicRequest ;
209+
210+ expect ( error ) . toBeNull ( ) ;
211+
212+ expect ( reqMemo [ "headers" ] ) . toMatchInlineSnapshot ( `
213+ HeadersPolyfill {
214+ Symbol(normalizedHeaders): {
215+ "initial-header": "stays",
216+ "new-header": "was-not-in-make-fetch-options",
217+ "override-header": "is-different-from-fetch-options",
218+ },
219+ Symbol(rawHeaderNames): Map {
220+ "initial-header" => "initial-header",
221+ "new-header" => "new-header",
222+ "override-header" => "override-header",
223+ },
224+ }
225+ ` ) ;
226+ } ) ;
227+
228+ it ( "transforms request URL" , async ( { useTestMemo, meta } ) => {
229+ const client = makeStubClient ( {
230+ strategies : {
231+ makeQueryURL : ( ) =>
232+ Promise . resolve ( "http://localhost/default/q/fingerprint" ) ,
233+ transformFetchOptions ( { init } ) {
234+ return {
235+ input : "http://localhost/transformed" ,
236+ init,
237+ } ;
238+ } ,
239+ } ,
240+ } ) ;
241+
242+ const { error } = await client . execute < { } > ( "SELECT 1;" ) ;
243+
244+ const reqMemo = useTestMemo ! ( ) . get ( meta . id ) as IsomorphicRequest ;
245+
246+ expect ( error ) . toBeNull ( ) ;
247+
248+ expect ( reqMemo [ "url" ] . toString ( ) ) . toBe ( "http://localhost/transformed" ) ;
249+ } ) ;
250+ } ) ;
251+
144252describe ( "client handles errors correctly because it" , ( ) => {
145253 setupMswServerTestHooks ( ) ;
146254
@@ -217,6 +325,7 @@ const makeUnconnectableClient = () => {
217325 } ,
218326 parseFieldsFromResponse : skipParsingFieldsFromResponse ,
219327 parseFieldsFromResponseBodyJSON : parseFieldsFromResponseBodyJSONFieldsKey ,
328+ transformFetchOptions : skipTransformFetchOptions ,
220329 } ,
221330 } ) ;
222331} ;
0 commit comments