From a5e0035d746fecb084b51250252dc19be446d469 Mon Sep 17 00:00:00 2001 From: dOrgJelli Date: Wed, 26 Apr 2023 13:01:50 -0500 Subject: [PATCH 1/4] fix: remove PackageToWrapperResolver from default resolver --- .../src/ClientConfigBuilder.ts | 6 +- .../__tests__/core/error-structure.spec.ts | 3 +- .../src/__tests__/core/uri-resolution.spec.ts | 2 +- .../src/UriResolverExtensionFileReader.ts | 73 +++++++++++++------ ...plugin-extension-error-with-subinvoke.json | 8 +- 5 files changed, 57 insertions(+), 35 deletions(-) diff --git a/packages/client-config-builder/src/ClientConfigBuilder.ts b/packages/client-config-builder/src/ClientConfigBuilder.ts index 9ceb41f01..192df5e0a 100644 --- a/packages/client-config-builder/src/ClientConfigBuilder.ts +++ b/packages/client-config-builder/src/ClientConfigBuilder.ts @@ -17,7 +17,6 @@ import { StaticResolver, ResolutionResultCache, ResolutionResultCacheResolver, - PackageToWrapperResolver, RequestSynchronizerResolver, } from "@polywrap/uri-resolvers-js"; import { ExtendableUriResolver } from "@polywrap/uri-resolver-extensions-js"; @@ -49,8 +48,7 @@ export class ClientConfigBuilder extends BaseClientConfigBuilder { resolver ?? RecursiveResolver.from( RequestSynchronizerResolver.from( - ResolutionResultCacheResolver.from( - PackageToWrapperResolver.from([ + ResolutionResultCacheResolver.from([ StaticResolver.from([ ...this.buildRedirects(), ...this.buildWrappers(), @@ -58,7 +56,7 @@ export class ClientConfigBuilder extends BaseClientConfigBuilder { ]), ...this._config.resolvers, new ExtendableUriResolver(), - ]), + ], resolutionResultCache ?? new ResolutionResultCache() ) ) diff --git a/packages/client/src/__tests__/core/error-structure.spec.ts b/packages/client/src/__tests__/core/error-structure.spec.ts index fe366cd8f..682e5713a 100644 --- a/packages/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/client/src/__tests__/core/error-structure.spec.ts @@ -256,9 +256,8 @@ describe("Error structure", () => { if (result.ok) throw Error("should never happen"); expect(result.error?.name).toEqual("WrapError"); - expect(result.error?.code).toEqual(WrapErrorCode.URI_RESOLVER_ERROR); + expect(result.error?.code).toEqual(WrapErrorCode.CLIENT_LOAD_WRAPPER_ERROR); expect(result.error?.uri.endsWith("tmp")).toBeTruthy(); - expect(result.error?.resolutionStack).toBeDefined(); expect(`${result.error?.cause}`).toContain( `Unrecognized WrapManifest schema version "0.0.0.5"` ); diff --git a/packages/client/src/__tests__/core/uri-resolution.spec.ts b/packages/client/src/__tests__/core/uri-resolution.spec.ts index 4e233f5bc..7adc14e1a 100644 --- a/packages/client/src/__tests__/core/uri-resolution.spec.ts +++ b/packages/client/src/__tests__/core/uri-resolution.spec.ts @@ -41,7 +41,7 @@ describe("URI resolution", () => { ); if (expectResult.ok) { - expectResult.value.type = "wrapper"; + expectResult.value.type = "package"; } await expectResolutionResult( diff --git a/packages/uri-resolver-extensions/src/UriResolverExtensionFileReader.ts b/packages/uri-resolver-extensions/src/UriResolverExtensionFileReader.ts index 88895da8b..fc0eb146b 100644 --- a/packages/uri-resolver-extensions/src/UriResolverExtensionFileReader.ts +++ b/packages/uri-resolver-extensions/src/UriResolverExtensionFileReader.ts @@ -13,6 +13,12 @@ import { Result, ResultErr } from "@polywrap/result"; // $start: UriResolverExtensionFileReader /** An IFileReader that reads files by invoking URI Resolver Extension wrappers */ export class UriResolverExtensionFileReader implements IFileReader /* $ */ { + + private _fileCache: Map< + string, + Promise> + > = new Map(); + // $start: UriResolverExtensionFileReader-constructor /** * Construct a UriResolverExtensionFileReader @@ -37,30 +43,49 @@ export class UriResolverExtensionFileReader implements IFileReader /* $ */ { * */ async readFile(filePath: string): Promise> /* $ */ { const path = combinePaths(this._wrapperUri.path, filePath); - const result = await UriResolverInterface.module.getFile( - { - invoke: ( - options: InvokeOptions - ): Promise> => this._client.invoke(options), - invokeWrapper: ( - options: InvokeOptions & { wrapper: Wrapper } - ): Promise> => - this._client.invokeWrapper(options), - }, - this._resolverExtensionUri, - path - ); - if (!result.ok) return result; - if (!result.value) { - return ResultErr( - new Error( - `File not found at ${path} using resolver ${this._resolverExtensionUri.uri}` - ) - ); + + // If the file has already been requested + const existingFile = this._fileCache.get(path); + + if (existingFile) { + return existingFile; } - return { - value: result.value, - ok: true, - }; + + // else, create a new read file request + const getFileRequest = new Promise>( + async (resolve) => { + const result = await UriResolverInterface.module.getFile( + { + invoke: ( + options: InvokeOptions + ): Promise> => this._client.invoke(options), + invokeWrapper: ( + options: InvokeOptions & { wrapper: Wrapper } + ): Promise> => + this._client.invokeWrapper(options), + }, + this._resolverExtensionUri, + path + ); + if (!result.ok) { + resolve(result); + } else if (!result.value) { + resolve(ResultErr( + new Error( + `File not found at ${path} using resolver ${this._resolverExtensionUri.uri}` + ) + )); + } else { + resolve({ + value: result.value, + ok: true, + }); + } + } + ); + + this._fileCache.set(path, getFileRequest); + + return getFileRequest; } } diff --git a/packages/uri-resolver-extensions/src/__tests__/histories/shows-plugin-extension-error-with-subinvoke.json b/packages/uri-resolver-extensions/src/__tests__/histories/shows-plugin-extension-error-with-subinvoke.json index 2e1d6583b..514e8f46d 100644 --- a/packages/uri-resolver-extensions/src/__tests__/histories/shows-plugin-extension-error-with-subinvoke.json +++ b/packages/uri-resolver-extensions/src/__tests__/histories/shows-plugin-extension-error-with-subinvoke.json @@ -1,10 +1,10 @@ [ - "wrap://test/error => UriResolverAggregator => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 177, col: 15 })", + "wrap://test/error => UriResolverAggregator => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 201, col: 15 })", [ "wrap://test/error => StaticResolver - Miss", - "wrap://test/error => ExtendableUriResolver => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 177, col: 15 })", + "wrap://test/error => ExtendableUriResolver => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 201, col: 15 })", [ - "wrap://test/error => ResolverExtension (wrap://package/subinvoke-resolver) => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 177, col: 15 })", + "wrap://test/error => ResolverExtension (wrap://package/subinvoke-resolver) => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 201, col: 15 })", [ "wrap://package/subinvoke-resolver => Client.loadWrapper => wrapper (wrap://package/subinvoke-resolver)", [ @@ -13,7 +13,7 @@ "wrap://package/subinvoke-resolver => StaticResolver - Package (wrap://package/subinvoke-resolver) => package (wrap://package/subinvoke-resolver)" ] ], - "wrap://package/subinvoke-resolver => Client.invokeWrapper => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 177, col: 15 })", + "wrap://package/subinvoke-resolver => Client.invokeWrapper => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 201, col: 15 })", [ "wrap://package/test-resolver => Client.loadWrapper => wrapper (wrap://package/test-resolver)", [ From 8b1c4a4b27a6ebcf3e9a9adda5e067674249c9ba Mon Sep 17 00:00:00 2001 From: dOrgJelli Date: Wed, 26 Apr 2023 16:41:09 -0500 Subject: [PATCH 2/4] chore: add tests + fix caching --- .../src/UriResolverExtensionFileReader.ts | 6 + ...uri-resolver-extension-file-reader.spec.ts | 136 ++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 packages/uri-resolver-extensions/src/__tests__/uri-resolver-extension-file-reader.spec.ts diff --git a/packages/uri-resolver-extensions/src/UriResolverExtensionFileReader.ts b/packages/uri-resolver-extensions/src/UriResolverExtensionFileReader.ts index fc0eb146b..d9476afec 100644 --- a/packages/uri-resolver-extensions/src/UriResolverExtensionFileReader.ts +++ b/packages/uri-resolver-extensions/src/UriResolverExtensionFileReader.ts @@ -67,15 +67,21 @@ export class UriResolverExtensionFileReader implements IFileReader /* $ */ { this._resolverExtensionUri, path ); + if (!result.ok) { + // The UriResolver has encountered an error, + // return the error & reset the file cache (enabling retries). + this._fileCache.delete(path); resolve(result); } else if (!result.value) { + // The UriResolver did not find the file @ the provided URI. resolve(ResultErr( new Error( `File not found at ${path} using resolver ${this._resolverExtensionUri.uri}` ) )); } else { + // The file has been found. resolve({ value: result.value, ok: true, diff --git a/packages/uri-resolver-extensions/src/__tests__/uri-resolver-extension-file-reader.spec.ts b/packages/uri-resolver-extensions/src/__tests__/uri-resolver-extension-file-reader.spec.ts new file mode 100644 index 000000000..419eb392b --- /dev/null +++ b/packages/uri-resolver-extensions/src/__tests__/uri-resolver-extension-file-reader.spec.ts @@ -0,0 +1,136 @@ +import { UriResolverExtensionFileReader } from "../UriResolverExtensionFileReader"; + +import { + PolywrapClient, + ClientConfigBuilder, + Uri +} from "@polywrap/client-js"; +import { + PluginModule, + PluginWrapper +} from "@polywrap/plugin-js"; + +const mockUriResolverExtUri = "wrap://mock/uri-resolver-ext"; +const mockFile = Uint8Array.from([0, 1, 2]); + +class MockUriResolverExt extends PluginModule<{}, {}> { + callCount: number = 0; + + getCallCount() { + return this.callCount; + } + + getFile(args: { path: string }) { + ++this.callCount; + if (args.path.includes("throw/now")) { + throw Error("failed during read file"); + } else if (args.path.includes("not/found")) { + return null; + } + return mockFile; + } +} + +function createMockClient(): PolywrapClient { + const config = new ClientConfigBuilder() + .addWrapper( + mockUriResolverExtUri, + new PluginWrapper( + { version: "0.1", type: "plugin", name: "counter", abi: {} }, + new MockUriResolverExt({}) + ) + ) + .build(); + return new PolywrapClient(config); +} + +function createUriResolverExtensionFileReader( + mockClient: PolywrapClient +): UriResolverExtensionFileReader { + return new UriResolverExtensionFileReader( + Uri.from(mockUriResolverExtUri), + Uri.from("wrap://foo/bar"), + mockClient + ); +} + +async function getCallCount(client: PolywrapClient): Promise { + const result = await client.invoke({ + uri: mockUriResolverExtUri, + method: "getCallCount" + }); + + if (!result.ok) throw result.error; + return result.value; +} + +describe("UriResolverExtensionFileReader", () => { + it("sanity", async () => { + const mockClient = createMockClient(); + const fileReader = createUriResolverExtensionFileReader(mockClient); + + const file = await fileReader.readFile("some/path"); + expect(file.ok).toBeTruthy(); + if (!file.ok) throw file.error; + expect(file.value).toMatchObject(mockFile); + }); + + it("caches files", async () => { + const mockClient = createMockClient(); + const fileReader = createUriResolverExtensionFileReader(mockClient); + + const file1 = await fileReader.readFile("some/path"); + expect(file1.ok).toBeTruthy(); + if (!file1.ok) throw file1.error; + expect(file1.value).toMatchObject(mockFile); + + // Ensure the call counter is 1 + expect(await getCallCount(mockClient)).toBe(1); + + // Call again + const file2 = await fileReader.readFile("some/path"); + expect(file2.ok).toBeTruthy(); + if (!file2.ok) throw file2.error; + expect(file2.value).toMatchObject(mockFile); + + // Ensure the call counter is still 1 + expect(await getCallCount(mockClient)).toBe(1); + }); + + it("retries when an error is thrown", async () => { + const mockClient = createMockClient(); + const fileReader = createUriResolverExtensionFileReader(mockClient); + + // It returns an error + const file1 = await fileReader.readFile("throw/now"); + expect(file1.ok).toBeFalsy(); + + // Ensure the call counter is 1 + expect(await getCallCount(mockClient)).toBe(1); + + // Call again + const file2 = await fileReader.readFile("throw/now"); + expect(file2.ok).toBeFalsy(); + + // Ensure the call counter is now 2 + expect(await getCallCount(mockClient)).toBe(2); + }); + + it("caches result when not found", async () => { + const mockClient = createMockClient(); + const fileReader = createUriResolverExtensionFileReader(mockClient); + + const file1 = await fileReader.readFile("not/found"); + expect(file1.ok).toBeFalsy(); + + // Ensure the call counter is 1 + expect(await getCallCount(mockClient)).toBe(1); + + // Call again + const file2 = await fileReader.readFile("not/found"); + expect(file2.ok).toBeFalsy(); + + // Ensure the call counter is still 1 + expect(await getCallCount(mockClient)).toBe(1); + }); +}); From 176544cccfaa2e3a04f8f0f47643ea22d98e2c87 Mon Sep 17 00:00:00 2001 From: dOrgJelli Date: Wed, 26 Apr 2023 16:44:45 -0500 Subject: [PATCH 3/4] chore: lint fix --- .../src/ClientConfigBuilder.ts | 3 +- .../src/UriResolverExtensionFileReader.ts | 58 ++++++++++--------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/packages/client-config-builder/src/ClientConfigBuilder.ts b/packages/client-config-builder/src/ClientConfigBuilder.ts index 192df5e0a..b32d0ea73 100644 --- a/packages/client-config-builder/src/ClientConfigBuilder.ts +++ b/packages/client-config-builder/src/ClientConfigBuilder.ts @@ -48,7 +48,8 @@ export class ClientConfigBuilder extends BaseClientConfigBuilder { resolver ?? RecursiveResolver.from( RequestSynchronizerResolver.from( - ResolutionResultCacheResolver.from([ + ResolutionResultCacheResolver.from( + [ StaticResolver.from([ ...this.buildRedirects(), ...this.buildWrappers(), diff --git a/packages/uri-resolver-extensions/src/UriResolverExtensionFileReader.ts b/packages/uri-resolver-extensions/src/UriResolverExtensionFileReader.ts index d9476afec..d4792b4e0 100644 --- a/packages/uri-resolver-extensions/src/UriResolverExtensionFileReader.ts +++ b/packages/uri-resolver-extensions/src/UriResolverExtensionFileReader.ts @@ -13,7 +13,6 @@ import { Result, ResultErr } from "@polywrap/result"; // $start: UriResolverExtensionFileReader /** An IFileReader that reads files by invoking URI Resolver Extension wrappers */ export class UriResolverExtensionFileReader implements IFileReader /* $ */ { - private _fileCache: Map< string, Promise> @@ -52,13 +51,14 @@ export class UriResolverExtensionFileReader implements IFileReader /* $ */ { } // else, create a new read file request - const getFileRequest = new Promise>( - async (resolve) => { - const result = await UriResolverInterface.module.getFile( + const getFileRequest = new Promise>((resolve) => { + return UriResolverInterface.module + .getFile( { invoke: ( options: InvokeOptions - ): Promise> => this._client.invoke(options), + ): Promise> => + this._client.invoke(options), invokeWrapper: ( options: InvokeOptions & { wrapper: Wrapper } ): Promise> => @@ -66,29 +66,31 @@ export class UriResolverExtensionFileReader implements IFileReader /* $ */ { }, this._resolverExtensionUri, path - ); - - if (!result.ok) { - // The UriResolver has encountered an error, - // return the error & reset the file cache (enabling retries). - this._fileCache.delete(path); - resolve(result); - } else if (!result.value) { - // The UriResolver did not find the file @ the provided URI. - resolve(ResultErr( - new Error( - `File not found at ${path} using resolver ${this._resolverExtensionUri.uri}` - ) - )); - } else { - // The file has been found. - resolve({ - value: result.value, - ok: true, - }); - } - } - ); + ) + .then((result) => { + if (!result.ok) { + // The UriResolver has encountered an error, + // return the error & reset the file cache (enabling retries). + this._fileCache.delete(path); + resolve(result); + } else if (!result.value) { + // The UriResolver did not find the file @ the provided URI. + resolve( + ResultErr( + new Error( + `File not found at ${path} using resolver ${this._resolverExtensionUri.uri}` + ) + ) + ); + } else { + // The file has been found. + resolve({ + value: result.value, + ok: true, + }); + } + }); + }); this._fileCache.set(path, getFileRequest); From ba8ffea2034b965ae0e11131f8c5a826a190be0a Mon Sep 17 00:00:00 2001 From: dOrgJelli Date: Wed, 26 Apr 2023 22:27:43 -0500 Subject: [PATCH 4/4] chore: update tests --- packages/uri-resolver-extensions/package.json | 1 + ...plugin-extension-error-with-subinvoke.json | 8 +++---- ...uri-resolver-extension-file-reader.spec.ts | 23 +++++++++++++++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/uri-resolver-extensions/package.json b/packages/uri-resolver-extensions/package.json index 6b44fd830..912c3998e 100644 --- a/packages/uri-resolver-extensions/package.json +++ b/packages/uri-resolver-extensions/package.json @@ -30,6 +30,7 @@ }, "devDependencies": { "@polywrap/cli-js": "0.10.0", + "@polywrap/client-js": "0.10.0", "@polywrap/core-client-js": "0.10.0", "@polywrap/plugin-js": "0.10.0", "@polywrap/test-cases": "0.10.0", diff --git a/packages/uri-resolver-extensions/src/__tests__/histories/shows-plugin-extension-error-with-subinvoke.json b/packages/uri-resolver-extensions/src/__tests__/histories/shows-plugin-extension-error-with-subinvoke.json index 514e8f46d..2e1d6583b 100644 --- a/packages/uri-resolver-extensions/src/__tests__/histories/shows-plugin-extension-error-with-subinvoke.json +++ b/packages/uri-resolver-extensions/src/__tests__/histories/shows-plugin-extension-error-with-subinvoke.json @@ -1,10 +1,10 @@ [ - "wrap://test/error => UriResolverAggregator => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 201, col: 15 })", + "wrap://test/error => UriResolverAggregator => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 177, col: 15 })", [ "wrap://test/error => StaticResolver - Miss", - "wrap://test/error => ExtendableUriResolver => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 201, col: 15 })", + "wrap://test/error => ExtendableUriResolver => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 177, col: 15 })", [ - "wrap://test/error => ResolverExtension (wrap://package/subinvoke-resolver) => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 201, col: 15 })", + "wrap://test/error => ResolverExtension (wrap://package/subinvoke-resolver) => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 177, col: 15 })", [ "wrap://package/subinvoke-resolver => Client.loadWrapper => wrapper (wrap://package/subinvoke-resolver)", [ @@ -13,7 +13,7 @@ "wrap://package/subinvoke-resolver => StaticResolver - Package (wrap://package/subinvoke-resolver) => package (wrap://package/subinvoke-resolver)" ] ], - "wrap://package/subinvoke-resolver => Client.invokeWrapper => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 201, col: 15 })", + "wrap://package/subinvoke-resolver => Client.invokeWrapper => error (__wrap_abort: Test error\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/test-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 35, col: 21 }\ncode: 51 WRAPPER INVOKE ABORTED\nuri: wrap://package/subinvoke-resolver\nmethod: tryResolveUri\nargs: {\n \"authority\": \"test\",\n \"path\": \"error\"\n} \nsource: { , row: 177, col: 15 })", [ "wrap://package/test-resolver => Client.loadWrapper => wrapper (wrap://package/test-resolver)", [ diff --git a/packages/uri-resolver-extensions/src/__tests__/uri-resolver-extension-file-reader.spec.ts b/packages/uri-resolver-extensions/src/__tests__/uri-resolver-extension-file-reader.spec.ts index 419eb392b..0771108f4 100644 --- a/packages/uri-resolver-extensions/src/__tests__/uri-resolver-extension-file-reader.spec.ts +++ b/packages/uri-resolver-extensions/src/__tests__/uri-resolver-extension-file-reader.spec.ts @@ -65,7 +65,7 @@ async function getCallCount(client: PolywrapClient): Promise { } describe("UriResolverExtensionFileReader", () => { - it("sanity", async () => { + it("resolves a file", async () => { const mockClient = createMockClient(); const fileReader = createUriResolverExtensionFileReader(mockClient); @@ -97,7 +97,26 @@ describe("UriResolverExtensionFileReader", () => { expect(await getCallCount(mockClient)).toBe(1); }); - it("retries when an error is thrown", async () => { + it("can synchronize parallel requests", async () => { + const mockClient = createMockClient(); + const fileReader = createUriResolverExtensionFileReader(mockClient); + + const results = await Promise.all([ + fileReader.readFile("some/path"), + fileReader.readFile("some/path") + ]); + + for (const result of results) { + expect(result.ok).toBeTruthy(); + if (!result.ok) throw result.error; + expect(result.value).toMatchObject(mockFile); + } + + // Ensure the call counter is 1 + expect(await getCallCount(mockClient)).toBe(1); + }); + + it("can retry when an error is thrown", async () => { const mockClient = createMockClient(); const fileReader = createUriResolverExtensionFileReader(mockClient);