2024-02-06 22:58:49 +01:00
import isRoughlyEqual from 'is-roughly-equal' ;
import { ok , AssertionError } from 'assert' ;
import { DateTime } from 'luxon' ;
import * as a from 'assert' ;
import { createRequire } from 'module' ;
import { gunzipSync } from 'zlib' ;
2017-11-12 21:03:24 +01:00
2024-02-06 22:58:49 +01:00
const hour = 60 * 60 * 1000 ;
const day = 24 * hour ;
const week = 7 * day ;
2017-11-12 21:03:24 +01:00
2017-12-11 16:33:17 +01:00
// next Monday 10 am
2022-04-23 14:40:51 +02:00
const createWhen = ( timezone , locale , tMock ) => {
2024-02-06 22:58:49 +01:00
ok ( Number . isInteger ( tMock ) , 'tMock must be an integer' ) ;
2022-04-23 14:40:51 +02:00
2020-05-21 22:21:07 +02:00
const t = process . env . VCR _MODE && ! process . env . VCR _OFF
2022-04-23 14:40:51 +02:00
? tMock
2024-02-06 22:58:49 +01:00
: Date . now ( ) ;
2020-05-21 22:21:07 +02:00
return DateTime . fromMillis ( t , {
2017-12-28 22:57:22 +01:00
zone : timezone ,
locale ,
2024-02-06 22:58:49 +01:00
} )
. startOf ( 'week' )
. plus ( { weeks : 1 , hours : 10 } )
. toJSDate ( ) ;
} ;
2018-04-18 20:08:47 +02:00
2021-12-29 21:11:07 +01:00
const assertValidWhen = ( actual , expected , name , delta = day + 6 * hour ) => {
2024-02-06 22:58:49 +01:00
const ts = Number ( new Date ( actual ) ) ;
a . ok ( ! Number . isNaN ( ts ) , name + ' is not parsable by Date' ) ;
2018-04-18 20:08:47 +02:00
// the timestamps might be from long-distance trains
2024-02-06 22:58:49 +01:00
if ( ! isRoughlyEqual ( delta , Number ( expected ) , ts ) ) {
2020-05-22 16:09:25 +02:00
throw new AssertionError ( {
message : name + ' is out of range' ,
actual : ts ,
2024-02-06 22:58:49 +01:00
expected : ` ${ expected - delta } - ${ Number ( expected ) + delta } ` ,
2020-05-22 16:09:25 +02:00
operator : 'isRoughlyEqual' ,
2024-02-06 22:58:49 +01:00
} ) ;
2020-05-22 16:09:25 +02:00
}
2024-02-06 22:58:49 +01:00
} ;
2017-12-11 15:41:27 +01:00
2020-05-21 19:04:47 +02:00
// HTTP request mocking
if ( process . env . VCR _MODE && ! process . env . VCR _OFF ) {
2024-02-06 22:58:49 +01:00
const require = createRequire ( import . meta . url ) ;
2022-06-18 16:50:33 +02:00
2024-02-06 22:58:49 +01:00
const { Polly } = require ( '@pollyjs/core' ) ;
const NodeHttpAdapter = require ( '@pollyjs/adapter-node-http' ) ;
const FSPersister = require ( '@pollyjs/persister-fs' ) ;
const tap = require ( 'tap' ) ;
2022-06-18 16:50:33 +02:00
2022-11-18 13:44:27 +01:00
// monkey-patch NodeHttpAdapter to handle gzipped responses properly
// todo: submit a PR
// related: https://github.com/Netflix/pollyjs/issues/256
// related: https://github.com/Netflix/pollyjs/issues/463
// related: https://github.com/Netflix/pollyjs/issues/207
2024-02-06 22:58:49 +01:00
const _getBodyFromChunks = NodeHttpAdapter . prototype . getBodyFromChunks ;
2022-11-18 13:44:27 +01:00
NodeHttpAdapter . prototype . getBodyFromChunks = function getBodyFromChunksWithGunzip ( chunks , headers ) {
if ( headers [ 'content-encoding' ] === 'gzip' ) {
2024-02-06 22:58:49 +01:00
const concatenated = Buffer . concat ( chunks ) ;
chunks = [ gunzipSync ( concatenated ) ] ;
2022-11-18 13:44:27 +01:00
// todo: this is ugly, find a better way
2024-02-06 22:58:49 +01:00
delete headers [ 'content-encoding' ] ;
headers [ 'content-length' ] = chunks [ 0 ] . length ;
2022-11-18 13:44:27 +01:00
}
2024-02-06 22:58:49 +01:00
return _getBodyFromChunks . call ( this , chunks , headers ) ;
} ;
2022-11-18 13:44:27 +01:00
2024-02-06 22:58:49 +01:00
Polly . register ( NodeHttpAdapter ) ;
Polly . register ( FSPersister ) ;
2022-06-18 16:50:33 +02:00
2024-02-06 22:58:49 +01:00
let mode ;
if ( process . env . VCR _MODE === 'record' ) {
mode = 'record' ;
} else if ( process . env . VCR _MODE === 'playback' ) {
mode = 'replay' ;
} else {
throw new Error ( 'invalid $VCR_MODE, must be "record" or "replay"' ) ;
}
2022-06-18 16:50:33 +02:00
const polly = new Polly ( 'requests' , {
logLevel : 'warn' ,
// If a request's recording is not found, pass-through to the server and record the response.
recordIfMissing : false ,
// If false, Polly will throw when attempting to persist any failed requests. A request is considered to be a failed request when its response's status code is ≥ 400.
recordFailedRequests : true ,
// Await any unresolved requests handled by the polly instance (via flush) when stop is called.
flushRequestsOnStop : true ,
// The Polly mode. Can be one of the following:
// - replay: Replay responses from recordings.
// - record: Force Polly to record all requests. This will overwrite recordings that already exist.
// - passthrough: Passes all requests through directly to the server without recording or replaying.
mode ,
adapters : [ 'node-http' ] ,
persister : 'fs' ,
persisterOptions : {
fs : {
recordingsDir : new URL ( '../fixtures' , import . meta . url ) . pathname ,
} ,
// When disabled, requests that have not been captured by the running Polly instance will be removed from any previous recording. This ensures that a recording will only contain the requests that were made during the lifespan of the Polly instance. When enabled, new requests will be appended to the recording file.
keepUnusedRequests : true , // todo: change to false?
} ,
matchRequestsBy : {
2022-11-09 23:53:50 +01:00
order : false ,
2022-06-18 16:50:33 +02:00
headers : {
// todo: use an allow-list here?
exclude : [
// request
'user-agent' , // randomised
'connection' , // not relevant for tests
// response
'set-cookie' , // often randomised
'date' , // constantly changing
] ,
} ,
} ,
2024-02-06 22:58:49 +01:00
} ) ;
2022-06-18 16:50:33 +02:00
tap . teardown ( async ( ) => {
2024-02-06 22:58:49 +01:00
await polly . stop ( ) ;
} ) ;
2020-05-21 19:04:47 +02:00
}
2020-05-21 19:04:22 +02:00
2022-05-07 16:17:37 +02:00
export {
2020-05-21 19:04:22 +02:00
hour , createWhen , assertValidWhen ,
2024-02-06 22:58:49 +01:00
} ;