diff --git a/packages/verified-fetch/package.json b/packages/verified-fetch/package.json index 8945b835..03bd4686 100644 --- a/packages/verified-fetch/package.json +++ b/packages/verified-fetch/package.json @@ -161,12 +161,16 @@ "progress-events": "^1.0.0" }, "devDependencies": { + "@helia/utils": "^0.0.1", "@libp2p/logger": "^4.0.5", "@libp2p/peer-id-factory": "^4.0.5", "@sgtpooki/file-type": "^1.0.1", "@types/sinon": "^17.0.3", "aegir": "^42.2.2", + "blockstore-core": "^4.4.0", + "datastore-core": "^9.2.8", "helia": "^4.0.1", + "it-last": "^3.0.4", "magic-bytes.js": "^1.8.0", "sinon": "^17.0.1", "sinon-ts": "^2.0.0", diff --git a/packages/verified-fetch/src/verified-fetch.ts b/packages/verified-fetch/src/verified-fetch.ts index e513f46a..0719a6e8 100644 --- a/packages/verified-fetch/src/verified-fetch.ts +++ b/packages/verified-fetch/src/verified-fetch.ts @@ -9,6 +9,7 @@ import { code as dagJsonCode } from '@ipld/dag-json' import { code as dagPbCode } from '@ipld/dag-pb' import { code as jsonCode } from 'multiformats/codecs/json' import { decode, code as rawCode } from 'multiformats/codecs/raw' +import { identity } from 'multiformats/hashes/identity' import { CustomProgressEvent } from 'progress-events' import { getStreamFromAsyncIterable } from './utils/get-stream-from-async-iterable.js' import { parseResource } from './utils/parse-resource.js' @@ -62,6 +63,20 @@ function convertOptions (options?: VerifiedFetchOptions): (Omit { - const response = new Response('vnd.ipfs.ipns-record support is not implemented', { status: 501 }) + const response = notSupportedResponse('vnd.ipfs.ipns-record support is not implemented') response.headers.set('X-Content-Type-Options', 'nosniff') // see https://specs.ipfs.tech/http-gateways/path-gateway/#x-content-type-options-response-header return response } // handle vnd.ipld.car private async handleIPLDCar ({ cid, path, options }: FetchHandlerFunctionArg): Promise { - const response = new Response('vnd.ipld.car support is not implemented', { status: 501 }) + const response = notSupportedResponse('vnd.ipld.car support is not implemented') response.headers.set('X-Content-Type-Options', 'nosniff') // see https://specs.ipfs.tech/http-gateways/path-gateway/#x-content-type-options-response-header return response } @@ -113,7 +128,7 @@ export class VerifiedFetch { onProgress: options?.onProgress }) options?.onProgress?.(new CustomProgressEvent('verified-fetch:request:end', { cid, path })) - const response = new Response(JSON.stringify(result), { status: 200 }) + const response = okResponse(JSON.stringify(result)) response.headers.set('content-type', 'application/json') return response } @@ -126,7 +141,7 @@ export class VerifiedFetch { onProgress: options?.onProgress }) options?.onProgress?.(new CustomProgressEvent('verified-fetch:request:end', { cid, path })) - const response = new Response(JSON.stringify(result), { status: 200 }) + const response = okResponse(JSON.stringify(result)) response.headers.set('content-type', 'application/json') return response } @@ -139,7 +154,7 @@ export class VerifiedFetch { onProgress: options?.onProgress }) options?.onProgress?.(new CustomProgressEvent('verified-fetch:request:end', { cid, path })) - const response = new Response(result, { status: 200 }) + const response = okResponse(JSON.stringify(result)) await this.setContentType(result, path, response) return response } @@ -166,7 +181,7 @@ export class VerifiedFetch { // terminalElement = stat } catch (err: any) { this.log('error loading path %c/%s', dirCid, rootFilePath, err) - return new Response('Unable to find index.html for directory at given path. Support for directories with implicit root is not implemented', { status: 501 }) + return notSupportedResponse('Unable to find index.html for directory at given path. Support for directories with implicit root is not implemented') } finally { options?.onProgress?.(new CustomProgressEvent('verified-fetch:request:end', { cid: dirCid, path: rootFilePath })) } @@ -183,7 +198,7 @@ export class VerifiedFetch { const { stream, firstChunk } = await getStreamFromAsyncIterable(asyncIter, path ?? '', this.helia.logger, { onProgress: options?.onProgress }) - const response = new Response(stream, { status: 200 }) + const response = okResponse(stream) await this.setContentType(firstChunk, path, response) return response @@ -194,7 +209,7 @@ export class VerifiedFetch { options?.onProgress?.(new CustomProgressEvent('verified-fetch:request:start', { cid, path })) const result = await this.helia.blockstore.get(cid) options?.onProgress?.(new CustomProgressEvent('verified-fetch:request:end', { cid, path })) - const response = new Response(decode(result), { status: 200 }) + const response = okResponse(decode(result)) await this.setContentType(result, path, response) return response } @@ -261,14 +276,14 @@ export class VerifiedFetch { * These format handlers should adjust the response headers as specified in https://specs.ipfs.tech/http-gateways/path-gateway/#response-headers */ private readonly formatHandlers: Record = { - raw: async () => new Response('application/vnd.ipld.raw support is not implemented', { status: 501 }), + raw: async () => notSupportedResponse('application/vnd.ipld.raw support is not implemented'), car: this.handleIPLDCar, 'ipns-record': this.handleIPNSRecord, - tar: async () => new Response('application/x-tar support is not implemented', { status: 501 }), - 'dag-json': async () => new Response('application/vnd.ipld.dag-json support is not implemented', { status: 501 }), - 'dag-cbor': async () => new Response('application/vnd.ipld.dag-cbor support is not implemented', { status: 501 }), - json: async () => new Response('application/json support is not implemented', { status: 501 }), - cbor: async () => new Response('application/cbor support is not implemented', { status: 501 }) + tar: async () => notSupportedResponse('application/x-tar support is not implemented'), + 'dag-json': async () => notSupportedResponse('application/vnd.ipld.dag-json support is not implemented'), + 'dag-cbor': async () => notSupportedResponse('application/vnd.ipld.dag-cbor support is not implemented'), + json: async () => notSupportedResponse('application/json support is not implemented'), + cbor: async () => notSupportedResponse('application/cbor support is not implemented') } private readonly codecHandlers: Record = { @@ -276,7 +291,8 @@ export class VerifiedFetch { [dagPbCode]: this.handleDagPb, [jsonCode]: this.handleJson, [dagCborCode]: this.handleDagCbor, - [rawCode]: this.handleRaw + [rawCode]: this.handleRaw, + [identity.code]: this.handleRaw } async fetch (resource: Resource, opts?: VerifiedFetchOptions): Promise { @@ -318,7 +334,7 @@ export class VerifiedFetch { if (codecHandler != null) { response = await codecHandler.call(this, { cid, path, options, terminalElement }) } else { - return new Response(`Support for codec with code ${cid.code} is not yet implemented. Please open an issue at https://github.com/ipfs/helia/issues/new`, { status: 501 }) + return notSupportedResponse(`Support for codec with code ${cid.code} is not yet implemented. Please open an issue at https://github.com/ipfs/helia/issues/new`) } } diff --git a/packages/verified-fetch/test/fixtures/create-offline-helia.ts b/packages/verified-fetch/test/fixtures/create-offline-helia.ts new file mode 100644 index 00000000..2c6ab4af --- /dev/null +++ b/packages/verified-fetch/test/fixtures/create-offline-helia.ts @@ -0,0 +1,20 @@ +import { Helia as HeliaClass } from '@helia/utils' +import { MemoryBlockstore } from 'blockstore-core' +import { MemoryDatastore } from 'datastore-core' +import type { Helia } from '@helia/interface' + +export async function createHelia (): Promise { + const datastore = new MemoryDatastore() + const blockstore = new MemoryBlockstore() + + const helia = new HeliaClass({ + datastore, + blockstore, + blockBrokers: [], + routers: [] + }) + + await helia.start() + + return helia +} diff --git a/packages/verified-fetch/test/verified-fetch.spec.ts b/packages/verified-fetch/test/verified-fetch.spec.ts index b36cfc48..77a4b256 100644 --- a/packages/verified-fetch/test/verified-fetch.spec.ts +++ b/packages/verified-fetch/test/verified-fetch.spec.ts @@ -1,43 +1,56 @@ /* eslint-env mocha */ -import { type DAGCBOR } from '@helia/dag-cbor' -import { type DAGJSON } from '@helia/dag-json' +import { dagCbor } from '@helia/dag-cbor' +import { dagJson } from '@helia/dag-json' import { type IPNS } from '@helia/ipns' -import { type JSON as HeliaJSON } from '@helia/json' -import { type UnixFS } from '@helia/unixfs' +import { json } from '@helia/json' +import { unixfs, type UnixFS } from '@helia/unixfs' +import { stop } from '@libp2p/interface' import { defaultLogger } from '@libp2p/logger' import { expect } from 'aegir/chai' +import last from 'it-last' import { CID } from 'multiformats/cid' -import { encode } from 'multiformats/codecs/raw' -import sinon, { type SinonStub } from 'sinon' +import * as raw from 'multiformats/codecs/raw' +import { identity } from 'multiformats/hashes/identity' +import { sha256 } from 'multiformats/hashes/sha2' +import Sinon from 'sinon' import { stubInterface } from 'sinon-ts' +import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { VerifiedFetch } from '../src/verified-fetch.js' -import type { PathWalkerFn } from '../src/utils/walk-path' -import type { Blocks, Helia } from '@helia/interface' +import { createHelia } from './fixtures/create-offline-helia.js' +import type { Helia } from '@helia/interface' import type { Logger, ComponentLogger } from '@libp2p/interface' -import type { UnixFSDirectory, UnixFSEntry } from 'ipfs-unixfs-exporter' const testCID = CID.parse('QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr') -const anyOnProgressMatcher = sinon.match.any as unknown as () => void describe('@helia/verifed-fetch', () => { + let helia: Helia + + beforeEach(async () => { + helia = await createHelia() + }) + + afterEach(async () => { + await stop(helia) + }) + it('starts and stops the helia node', async () => { - const stopStub = sinon.stub() - const startStub = sinon.stub() + const helia = stubInterface({ + logger: defaultLogger() + }) const verifiedFetch = new VerifiedFetch({ - helia: stubInterface({ - start: startStub, - stop: stopStub, - logger: defaultLogger() - }) + helia }) - expect(stopStub.withArgs().callCount).to.equal(0) - expect(startStub.withArgs().callCount).to.equal(0) + + expect(helia.stop.callCount).to.equal(0) + expect(helia.start.callCount).to.equal(0) + await verifiedFetch.start() - expect(stopStub.withArgs().callCount).to.equal(0) - expect(startStub.withArgs().callCount).to.equal(1) + expect(helia.stop.callCount).to.equal(0) + expect(helia.start.callCount).to.equal(1) + await verifiedFetch.stop() - expect(stopStub.withArgs().callCount).to.equal(1) - expect(startStub.withArgs().callCount).to.equal(1) + expect(helia.stop.callCount).to.equal(1) + expect(helia.start.callCount).to.equal(1) }) describe('format not implemented', () => { @@ -97,43 +110,10 @@ describe('@helia/verifed-fetch', () => { describe('implicit format', () => { let verifiedFetch: VerifiedFetch - let unixfsStub: ReturnType> - let dagJsonStub: ReturnType> - let jsonStub: ReturnType> - let dagCborStub: ReturnType> - let pathWalkerStub: SinonStub, ReturnType> - let blockstoreStub: ReturnType> beforeEach(async () => { - blockstoreStub = stubInterface() - unixfsStub = stubInterface({ - cat: sinon.stub(), - stat: sinon.stub() - }) - dagJsonStub = stubInterface({ - // @ts-expect-error - stub errors - get: sinon.stub() - }) - jsonStub = stubInterface({ - // @ts-expect-error - stub errors - get: sinon.stub() - }) - dagCborStub = stubInterface({ - // @ts-expect-error - stub errors - get: sinon.stub() - }) - pathWalkerStub = sinon.stub, ReturnType>() verifiedFetch = new VerifiedFetch({ - helia: stubInterface({ - blockstore: blockstoreStub, - logger: defaultLogger() - }), - ipns: stubInterface(), - unixfs: unixfsStub, - dagJson: dagJsonStub, - json: jsonStub, - dagCbor: dagCborStub, - pathWalker: pathWalkerStub + helia }) }) @@ -143,256 +123,144 @@ describe('@helia/verifed-fetch', () => { it('should return raw data', async () => { const finalRootFileContent = new Uint8Array([0x01, 0x02, 0x03]) - pathWalkerStub.returns(Promise.resolve({ - ipfsRoots: [testCID], - terminalElement: { - cid: testCID, - size: BigInt(3), - depth: 1, - content: async function * () { yield finalRootFileContent }, - name: 'index.html', - path: '', - type: 'raw', - node: finalRootFileContent - } - })) - unixfsStub.cat.returns({ - [Symbol.asyncIterator]: async function * () { - yield finalRootFileContent - } - }) - const resp = await verifiedFetch.fetch(testCID) - expect(pathWalkerStub.callCount).to.equal(1) - expect(unixfsStub.cat.callCount).to.equal(1) + const cid = CID.createV1(raw.code, await sha256.digest(finalRootFileContent)) + await helia.blockstore.put(cid, finalRootFileContent) + + const resp = await verifiedFetch.fetch(`ipfs://${cid}`) expect(resp).to.be.ok() expect(resp.status).to.equal(200) + expect(resp.statusText).to.equal('OK') const data = await resp.arrayBuffer() - expect(new Uint8Array(data)).to.deep.equal(finalRootFileContent) + expect(new Uint8Array(data)).to.equalBytes(finalRootFileContent) }) - it('should look for root files when directory is returned', async () => { + it('should report progress during fetch', async () => { const finalRootFileContent = new Uint8Array([0x01, 0x02, 0x03]) - const signal = sinon.match.any as unknown as AbortSignal - const onProgress = sinon.spy() - // @ts-expect-error - stubbed type is incorrect - pathWalkerStub.onCall(0).returns(Promise.resolve({ - ipfsRoots: [testCID], - terminalElement: { - cid: testCID, - size: BigInt(3), - depth: 1, - // @ts-expect-error - stubbed type is incorrect - content: sinon.stub() as unknown as AsyncGenerator, - // @ts-expect-error - stubbed type is incorrect - unixfs: {} as unknown as UnixFS, - name: 'dirName', - path: '', - type: 'directory', - // @ts-expect-error - stubbed type is incorrect - node: {} - } satisfies UnixFSDirectory - })) - unixfsStub.stat.withArgs(testCID, { path: 'index.html', signal, onProgress: anyOnProgressMatcher }).onCall(0) - .returns(Promise.resolve({ - cid: CID.parse('Qmc3zqKcwzbbvw3MQm3hXdg8BQoFjGdZiGdAfXAyAGGdLi'), - size: 3, - type: 'raw', - fileSize: BigInt(3), - dagSize: BigInt(1), - localFileSize: BigInt(3), - localDagSize: BigInt(1), - blocks: 1 - })) - unixfsStub.cat.returns({ - [Symbol.asyncIterator]: async function * () { - yield finalRootFileContent - } - }) - unixfsStub.cat.returns({ - [Symbol.asyncIterator]: async function * () { - yield finalRootFileContent - } + const cid = CID.createV1(raw.code, await sha256.digest(finalRootFileContent)) + await helia.blockstore.put(cid, finalRootFileContent) + + const onProgress = Sinon.spy() + + await verifiedFetch.fetch(`ipfs://${cid}`, { + onProgress }) - const resp = await verifiedFetch.fetch(testCID, { onProgress }) - expect(unixfsStub.stat.callCount).to.equal(1) - expect(pathWalkerStub.callCount).to.equal(1) - expect(pathWalkerStub.getCall(0).args[1]).to.equal(`${testCID.toString()}/`) - expect(unixfsStub.cat.callCount).to.equal(1) - expect(unixfsStub.cat.withArgs(testCID).callCount).to.equal(0) - expect(unixfsStub.cat.withArgs(CID.parse('Qmc3zqKcwzbbvw3MQm3hXdg8BQoFjGdZiGdAfXAyAGGdLi'), sinon.match.any).callCount).to.equal(1) - expect(onProgress.callCount).to.equal(5) + expect(onProgress.callCount).to.equal(3) const onProgressEvents = onProgress.getCalls().map(call => call.args[0]) - expect(onProgressEvents[0]).to.include({ type: 'verified-fetch:request:start' }).and.to.have.property('detail').that.deep.equals({ - cid: testCID, - path: 'index.html' - }) - expect(onProgressEvents[1]).to.include({ type: 'verified-fetch:request:end' }).and.to.have.property('detail').that.deep.equals({ - cid: testCID, - path: 'index.html' + expect(onProgressEvents[0]).to.include({ type: 'blocks:get:blockstore:get' }).and.to.have.property('detail').that.deep.equals(cid) + expect(onProgressEvents[1]).to.include({ type: 'verified-fetch:request:start' }).and.to.have.property('detail').that.deep.equals({ + cid, + path: '' }) - expect(onProgressEvents[3]).to.include({ type: 'verified-fetch:request:end' }).and.to.have.property('detail').that.deep.equals({ - cid: CID.parse('Qmc3zqKcwzbbvw3MQm3hXdg8BQoFjGdZiGdAfXAyAGGdLi'), + expect(onProgressEvents[2]).to.include({ type: 'verified-fetch:request:end' }).and.to.have.property('detail').that.deep.equals({ + cid, path: '' }) - expect(onProgressEvents[4]).to.include({ type: 'verified-fetch:request:progress:chunk' }).and.to.have.property('detail').that.is.undefined() + }) + + it('should look for index files when directory is returned', async () => { + const finalRootFileContent = new Uint8Array([0x01, 0x02, 0x03]) + + const fs = unixfs(helia) + const res = await last(fs.addAll([{ + path: 'index.html', + content: finalRootFileContent + }], { + wrapWithDirectory: true + })) + + if (res == null) { + throw new Error('Import failed') + } + + const stat = await fs.stat(res.cid) + expect(stat.type).to.equal('directory') + + const resp = await verifiedFetch.fetch(res.cid) expect(resp).to.be.ok() expect(resp.status).to.equal(200) + expect(resp.statusText).to.equal('OK') const data = await resp.arrayBuffer() - expect(new Uint8Array(data)).to.deep.equal(finalRootFileContent) + expect(new Uint8Array(data)).to.equalBytes(finalRootFileContent) }) - it('should not call unixfs.cat if root file is not found', async () => { - const signal = sinon.match.any as unknown as AbortSignal - const onProgress = sinon.spy() - // @ts-expect-error - stubbed type is incorrect - pathWalkerStub.onCall(0).returns(Promise.resolve({ - ipfsRoots: [testCID], - terminalElement: { - cid: testCID, - size: BigInt(3), - depth: 1, - // @ts-expect-error - stubbed type is incorrect - content: sinon.stub() as unknown as AsyncGenerator, - // @ts-expect-error - stubbed type is incorrect - unixfs: {} as unknown as UnixFS, - name: 'dirName', - path: '', - type: 'directory', - // @ts-expect-error - stubbed type is incorrect - node: {} - } satisfies UnixFSDirectory + it('should return 501 if index file is not found', async () => { + const finalRootFileContent = new Uint8Array([0x01, 0x02, 0x03]) + + const fs = unixfs(helia) + const res = await last(fs.addAll([{ + path: 'not_an_index.html', + content: finalRootFileContent + }], { + wrapWithDirectory: true })) - unixfsStub.stat.withArgs(testCID, { path: 'index.html', signal, onProgress: anyOnProgressMatcher }).onCall(0).throws(new Error('not found')) - const resp = await verifiedFetch.fetch(testCID) + if (res == null) { + throw new Error('Import failed') + } - expect(unixfsStub.stat.withArgs(testCID, { path: 'index.html', signal, onProgress: anyOnProgressMatcher }).callCount).to.equal(1) - expect(unixfsStub.cat.withArgs(testCID).callCount).to.equal(0) - expect(onProgress.callCount).to.equal(0) + const stat = await fs.stat(res.cid) + expect(stat.type).to.equal('directory') + + const resp = await verifiedFetch.fetch(res.cid) expect(resp).to.be.ok() expect(resp.status).to.equal(501) + expect(resp.statusText).to.equal('Not Implemented') }) - it('should return dag-json encoded CID', async () => { - const abortSignal = new AbortController().signal - const onProgress = sinon.spy() - const cid = CID.parse('baguqeerasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea') - dagJsonStub.get.withArgs(cid).returns(Promise.resolve({ + it('should handle dag-json block', async () => { + const obj = { hello: 'world' - })) - const resp = await verifiedFetch.fetch(cid, { - signal: abortSignal, - onProgress - }) - expect(unixfsStub.stat.withArgs(cid).callCount).to.equal(0) - expect(unixfsStub.cat.withArgs(cid).callCount).to.equal(0) - expect(dagJsonStub.get.withArgs(cid).callCount).to.equal(1) - expect(onProgress.callCount).to.equal(2) - const onProgressEvents = onProgress.getCalls().map(call => call.args[0]) - expect(onProgressEvents[0]).to.have.property('type', 'verified-fetch:request:start') - expect(onProgressEvents[0]).to.have.property('detail').that.deep.equals({ - cid, - path: '' - }) - expect(onProgressEvents[1]).to.have.property('type', 'verified-fetch:request:end') - expect(onProgressEvents[1]).to.have.property('detail').that.deep.equals({ - cid, - path: '' - }) + } + const j = dagJson(helia) + const cid = await j.add(obj) + + const resp = await verifiedFetch.fetch(cid) expect(resp).to.be.ok() expect(resp.status).to.equal(200) - const data = await resp.json() - expect(data).to.deep.equal({ - hello: 'world' - }) + expect(resp.statusText).to.equal('OK') + await expect(resp.json()).to.eventually.deep.equal(obj) }) - it('should return dag-cbor encoded CID', async () => { - const abortSignal = new AbortController().signal - const onProgress = sinon.spy() - const cid = CID.parse('bafyreidykglsfhoixmivffc5uwhcgshx4j465xwqntbmu43nb2dzqwfvae') - dagCborStub.get.withArgs(cid).returns(Promise.resolve(JSON.stringify({ + it('should handle dag-cbor block', async () => { + const obj = { hello: 'world' - }))) - const resp = await verifiedFetch.fetch(cid, { - signal: abortSignal, - onProgress - }) + } + const c = dagCbor(helia) + const cid = await c.add(obj) + + const resp = await verifiedFetch.fetch(cid) expect(resp).to.be.ok() expect(resp.status).to.equal(200) - expect(unixfsStub.stat.withArgs(cid).callCount).to.equal(0) - expect(unixfsStub.cat.withArgs(cid).callCount).to.equal(0) - expect(dagCborStub.get.withArgs(cid).callCount).to.equal(1) - expect(onProgress.callCount).to.equal(2) - const onProgressEvents = onProgress.getCalls().map(call => call.args[0]) - expect(onProgressEvents[0]).to.have.property('type', 'verified-fetch:request:start') - expect(onProgressEvents[0]).to.have.property('detail').that.deep.equals({ - cid, - path: '' - }) - expect(onProgressEvents[1]).to.have.property('type', 'verified-fetch:request:end') - expect(onProgressEvents[1]).to.have.property('detail').that.deep.equals({ - cid, - path: '' - }) - const data = await resp.json() - expect(data).to.deep.equal({ - hello: 'world' - }) + expect(resp.statusText).to.equal('OK') + await expect(resp.json()).to.eventually.deep.equal(obj) }) - it('should return json encoded CID', async () => { - const abortSignal = new AbortController().signal - const onProgress = sinon.spy() - const cid = CID.parse('bagaaifcavabu6fzheerrmtxbbwv7jjhc3kaldmm7lbnvfopyrthcvod4m6ygpj3unrcggkzhvcwv5wnhc5ufkgzlsji7agnmofovc2g4a3ui7ja') - jsonStub.get.withArgs(cid).returns(Promise.resolve({ + it('should handle json block', async () => { + const obj = { hello: 'world' - })) - const resp = await verifiedFetch.fetch(cid, { - signal: abortSignal, - onProgress - }) - expect(unixfsStub.stat.withArgs(cid).callCount).to.equal(0) - expect(unixfsStub.cat.withArgs(cid).callCount).to.equal(0) - expect(dagJsonStub.get.withArgs(cid).callCount).to.equal(0) - expect(jsonStub.get.withArgs(cid).callCount).to.equal(1) - const onProgressEvents = onProgress.getCalls().map(call => call.args[0]) - expect(onProgressEvents[0]).to.have.property('type', 'verified-fetch:request:start') - expect(onProgressEvents[0]).to.have.property('detail').that.deep.equals({ - cid, - path: '' - }) - expect(onProgressEvents[1]).to.have.property('type', 'verified-fetch:request:end') - expect(onProgressEvents[1]).to.have.property('detail').that.deep.equals({ - cid, - path: '' - }) + } + const j = json(helia) + const cid = await j.add(obj) + + const resp = await verifiedFetch.fetch(cid) expect(resp).to.be.ok() expect(resp.status).to.equal(200) - const data = await resp.json() - expect(data).to.deep.equal({ - hello: 'world' - }) + expect(resp.statusText).to.equal('OK') + await expect(resp.json()).to.eventually.deep.equal(obj) }) - it('should handle raw identity CID', async () => { - const abortSignal = new AbortController().signal - const onProgress = sinon.spy() - const cid = CID.parse('bafkqac3imvwgy3zao5xxe3de') - const textEncoder = new TextEncoder() - blockstoreStub.get.withArgs(cid).returns(Promise.resolve(encode(textEncoder.encode('hello world')))) - const resp = await verifiedFetch.fetch(cid, { - signal: abortSignal, - onProgress - }) + it('should handle identity CID', async () => { + const data = uint8ArrayFromString('hello world') + const cid = CID.createV1(identity.code, identity.digest(data)) + + const resp = await verifiedFetch.fetch(cid) expect(resp).to.be.ok() - // expect(resp.statusText).to.equal('OK') expect(resp.status).to.equal(200) - const data = await resp.text() - expect(data).to.equal('hello world') + expect(resp.statusText).to.equal('OK') + await expect(resp.text()).to.eventually.equal('hello world') }) }) })