@@ -25,10 +25,13 @@ import {
2525 httpRequest ,
2626 httpText ,
2727 parseChecksums ,
28+ readIncomingResponse ,
2829} from '@socketsecurity/lib/http-request'
2930import type {
3031 HttpHookRequestInfo ,
3132 HttpHookResponseInfo ,
33+ IncomingRequest ,
34+ IncomingResponse ,
3235} from '@socketsecurity/lib/http-request'
3336import { Logger } from '@socketsecurity/lib/logger'
3437import { afterAll , beforeAll , describe , expect , it } from 'vitest'
@@ -216,6 +219,12 @@ afterAll(async () => {
216219 } )
217220} )
218221
222+ function makeRawRequest ( url : string ) : Promise < http . IncomingMessage > {
223+ return new Promise ( ( resolve , reject ) => {
224+ http . get ( url , resolve ) . on ( 'error' , reject )
225+ } )
226+ }
227+
219228describe ( 'http-request' , ( ) => {
220229 describe ( 'httpRequest' , ( ) => {
221230 it ( 'should make a simple GET request' , async ( ) => {
@@ -2117,4 +2126,138 @@ abc123def456789012345678901234567890123456789012345678901234abcd
21172126 }
21182127 } )
21192128 } )
2129+
2130+ describe ( 'type aliases' , ( ) => {
2131+ it ( 'IncomingResponse should be assignable from http.IncomingMessage' , ( ) => {
2132+ const msg : http . IncomingMessage = { } as http . IncomingMessage
2133+ const response : IncomingResponse = msg
2134+ expect ( response ) . toBe ( msg )
2135+ } )
2136+
2137+ it ( 'IncomingRequest should be assignable from http.IncomingMessage' , ( ) => {
2138+ const msg : http . IncomingMessage = { } as http . IncomingMessage
2139+ const request : IncomingRequest = msg
2140+ expect ( request ) . toBe ( msg )
2141+ } )
2142+ } )
2143+
2144+ describe ( 'readIncomingResponse' , ( ) => {
2145+ it ( 'should read a 200 JSON response' , async ( ) => {
2146+ const msg = await makeRawRequest ( `${ httpBaseUrl } /json` )
2147+ const response = await readIncomingResponse ( msg )
2148+
2149+ expect ( response . ok ) . toBe ( true )
2150+ expect ( response . status ) . toBe ( 200 )
2151+ expect ( response . statusText ) . toBe ( 'OK' )
2152+ expect ( response . json ( ) ) . toEqual ( { message : 'Hello, World!' , status : 'success' } )
2153+ expect ( response . headers [ 'content-type' ] ) . toBe ( 'application/json' )
2154+ expect ( response . rawResponse ) . toBe ( msg )
2155+ } )
2156+
2157+ it ( 'should read a plain text response' , async ( ) => {
2158+ const msg = await makeRawRequest ( `${ httpBaseUrl } /text` )
2159+ const response = await readIncomingResponse ( msg )
2160+
2161+ expect ( response . ok ) . toBe ( true )
2162+ expect ( response . status ) . toBe ( 200 )
2163+ expect ( response . text ( ) ) . toBe ( 'Plain text response' )
2164+ } )
2165+
2166+ it ( 'should handle 404 responses' , async ( ) => {
2167+ const msg = await makeRawRequest ( `${ httpBaseUrl } /not-found` )
2168+ const response = await readIncomingResponse ( msg )
2169+
2170+ expect ( response . ok ) . toBe ( false )
2171+ expect ( response . status ) . toBe ( 404 )
2172+ expect ( response . statusText ) . toBe ( 'Not Found' )
2173+ expect ( response . text ( ) ) . toBe ( 'Not Found' )
2174+ } )
2175+
2176+ it ( 'should handle 500 server errors' , async ( ) => {
2177+ const msg = await makeRawRequest ( `${ httpBaseUrl } /server-error` )
2178+ const response = await readIncomingResponse ( msg )
2179+
2180+ expect ( response . ok ) . toBe ( false )
2181+ expect ( response . status ) . toBe ( 500 )
2182+ expect ( response . text ( ) ) . toBe ( 'Internal Server Error' )
2183+ } )
2184+
2185+ it ( 'should provide arrayBuffer from body' , async ( ) => {
2186+ const msg = await makeRawRequest ( `${ httpBaseUrl } /text` )
2187+ const response = await readIncomingResponse ( msg )
2188+ const ab = response . arrayBuffer ( )
2189+
2190+ expect ( ab ) . toBeInstanceOf ( ArrayBuffer )
2191+ expect ( ab . byteLength ) . toBeGreaterThan ( 0 )
2192+ expect ( Buffer . from ( ab ) . toString ( 'utf8' ) ) . toBe ( 'Plain text response' )
2193+ } )
2194+
2195+ it ( 'should handle binary response data' , async ( ) => {
2196+ const msg = await makeRawRequest ( `${ httpBaseUrl } /binary` )
2197+ const response = await readIncomingResponse ( msg )
2198+
2199+ expect ( response . ok ) . toBe ( true )
2200+ expect ( response . body . length ) . toBe ( 7 )
2201+ expect ( response . body [ 0 ] ) . toBe ( 0x00 )
2202+ expect ( response . body [ 1 ] ) . toBe ( 0x01 )
2203+ expect ( response . body [ 6 ] ) . toBe ( 0xfd )
2204+ } )
2205+
2206+ it ( 'should produce same result as httpRequest for same endpoint' , async ( ) => {
2207+ const msg = await makeRawRequest ( `${ httpBaseUrl } /json` )
2208+ const fromRaw = await readIncomingResponse ( msg )
2209+ const fromLib = await httpRequest ( `${ httpBaseUrl } /json` )
2210+
2211+ expect ( fromRaw . ok ) . toBe ( fromLib . ok )
2212+ expect ( fromRaw . status ) . toBe ( fromLib . status )
2213+ expect ( fromRaw . json ( ) ) . toEqual ( fromLib . json ( ) )
2214+ expect ( fromRaw . text ( ) ) . toBe ( fromLib . text ( ) )
2215+ } )
2216+
2217+ it ( 'should handle large response bodies' , async ( ) => {
2218+ const msg = await makeRawRequest ( `${ httpBaseUrl } /large-body` )
2219+ const response = await readIncomingResponse ( msg )
2220+
2221+ expect ( response . ok ) . toBe ( true )
2222+ expect ( response . text ( ) ) . toBe ( 'X' . repeat ( 10_000 ) )
2223+ expect ( response . body . length ) . toBe ( 10_000 )
2224+ } )
2225+
2226+ it ( 'should default status to 0 when statusCode is undefined' , async ( ) => {
2227+ const { Readable } = await import ( 'node:stream' )
2228+ const fakeMsg = new Readable ( {
2229+ read ( ) {
2230+ this . push ( 'body' )
2231+ this . push ( null )
2232+ } ,
2233+ } ) as unknown as IncomingResponse
2234+ Object . assign ( fakeMsg , {
2235+ headers : { } ,
2236+ statusCode : undefined ,
2237+ statusMessage : undefined ,
2238+ } )
2239+
2240+ const response = await readIncomingResponse ( fakeMsg )
2241+
2242+ expect ( response . status ) . toBe ( 0 )
2243+ expect ( response . statusText ) . toBe ( '' )
2244+ expect ( response . ok ) . toBe ( false )
2245+ expect ( response . text ( ) ) . toBe ( 'body' )
2246+ } )
2247+
2248+ it ( 'should preserve response headers' , async ( ) => {
2249+ const msg = await makeRawRequest ( `${ httpBaseUrl } /json` )
2250+ const response = await readIncomingResponse ( msg )
2251+
2252+ expect ( response . headers [ 'content-type' ] ) . toBe ( 'application/json' )
2253+ expect ( response . headers ) . toBeDefined ( )
2254+ } )
2255+
2256+ it ( 'should throw on invalid JSON from json()' , async ( ) => {
2257+ const msg = await makeRawRequest ( `${ httpBaseUrl } /text` )
2258+ const response = await readIncomingResponse ( msg )
2259+
2260+ expect ( ( ) => response . json ( ) ) . toThrow ( )
2261+ } )
2262+ } )
21202263} )
0 commit comments