diff --git a/src/client.js b/src/client.js index d503685..b59e8b9 100644 --- a/src/client.js +++ b/src/client.js @@ -261,9 +261,10 @@ export class Saturn { throw new Error(`All attempts to fetch content have failed. Last error: ${lastError.message}`) } - const fetchContent = async function * () { + const fetchContent = async function * (options) { let byteCount = 0 - const byteChunks = await this.fetchContent(cidPath, opts) + const fetchOptions = Object.assign(opts, options) + const byteChunks = await this.fetchContent(cidPath, fetchOptions) for await (const chunk of byteChunks) { // avoid sending duplicate chunks if (byteCount < byteCountCheckpoint) { @@ -327,7 +328,7 @@ export class Saturn { if (originUrl) { opts.nodes = Array({ url: originUrl }) try { - yield * fetchContent() + yield * fetchContent({ flatFile: true }) return } catch (err) { lastError = err @@ -343,6 +344,7 @@ export class Saturn { * @param {object} [opts={}] * @param {('car'|'raw')} [opts.format] * @param {boolean} [opts.raceNodes] + * @param {boolean} [opts.flatFile] * @param {number} [opts.connectTimeout=5000] * @param {number} [opts.downloadTimeout=0] * @returns {Promise>} @@ -367,7 +369,11 @@ export class Saturn { try { const itr = metricsIterable(asAsyncIterable(res.body)) - yield * extractVerifiedContent(cidPath, itr) + if (opts.flatFile) { + yield * itr + } else { + yield * extractVerifiedContent(cidPath, itr) + } } catch (err) { log.error = err.message controller.abort() diff --git a/test/fallback.spec.js b/test/fallback.spec.js index 6b8a1a5..3c4a23a 100644 --- a/test/fallback.spec.js +++ b/test/fallback.spec.js @@ -3,7 +3,7 @@ import assert from 'node:assert/strict' import { describe, mock, test } from 'node:test' import { Saturn } from '#src/index.js' -import { concatChunks, generateNodes, getMockServer, HTTP_STATUS_GONE, HTTP_STATUS_TIMEOUT, mockJWT, mockNodesHandlers, mockOrchHandler, mockOriginHandler, MSW_SERVER_OPTS } from './test-utils.js' +import { concatChunks, generateNodes, getMockServer, HTTP_STATUS_GONE, HTTP_STATUS_TIMEOUT, mockFlatFileOriginHandler, mockJWT, mockNodesHandlers, mockOrchHandler, mockOriginHandler, MSW_SERVER_OPTS } from './test-utils.js' const TEST_DEFAULT_ORCH = 'https://orchestrator.strn.pl.test/nodes' const TEST_NODES_LIST_KEY = 'saturn-nodes' @@ -280,7 +280,7 @@ describe('Client Fallback', () => { mockOrchHandler(numNodes, TEST_DEFAULT_ORCH, TEST_ORIGIN_DOMAIN), mockJWT(TEST_AUTH), mockOriginHandler(TEST_ORIGIN_DOMAIN, 0, true), - mockOriginHandler(TEST_CUSTOMER_ORIGIN, 0, false), + mockFlatFileOriginHandler(TEST_CUSTOMER_ORIGIN, 0, false), ...mockNodesHandlers(numNodes, TEST_ORIGIN_DOMAIN, numNodes, HTTP_STATUS_TIMEOUT) ] @@ -303,9 +303,10 @@ describe('Client Fallback', () => { const buffer = await concatChunks(cid) const actualContent = String.fromCharCode(...buffer) - const expectedContent = 'hello world\n' + const jsonContent = JSON.parse(actualContent) + const expectedContent = JSON.parse('{ "hello": "world" }') - assert.strictEqual(actualContent, expectedContent) + assert.deepEqual(jsonContent, expectedContent) mock.reset() server.close() }) diff --git a/test/fixtures/hello.json b/test/fixtures/hello.json new file mode 100644 index 0000000..d02fab0 --- /dev/null +++ b/test/fixtures/hello.json @@ -0,0 +1,3 @@ +{ + "hello": "world" +} \ No newline at end of file diff --git a/test/test-utils.js b/test/test-utils.js index d77ce6f..3840d5f 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -68,6 +68,31 @@ export function mockOriginHandler (originUrl, delay = 0, error = false) { }) } +/** + * Generates a mock handler to mimick an origin that serves flat files. + * + * @param {string} originUrl - originUrl + * @param {number} delay - request delay in ms + * @param {boolean} error + * @returns {RestHandler} + */ +export function mockFlatFileOriginHandler (originUrl, delay = 0, error = false) { + originUrl = addHttpPrefix(originUrl) + originUrl = `${originUrl}/ipfs/:cid` + return rest.get(originUrl, (req, res, ctx) => { + if (error) { + throw Error('Simulated Error') + } + const filepath = getFixturePath('hello.json') + const fileContents = fs.readFileSync(filepath) + return res( + ctx.delay(delay), + ctx.status(HTTP_STATUS_OK), + ctx.body(fileContents) + ) + }) +} + /** * Generates a mock handler to mimick Saturn's orchestrator /nodes endpoint. *