From 2d755d636799374d7c9f9a3916cab0ee44788667 Mon Sep 17 00:00:00 2001 From: Kamil Listopad Date: Tue, 28 Jul 2026 11:52:26 +0200 Subject: [PATCH 1/3] feat(pic): support overriding the PocketIC binary path The server binary can now be provided externally via the binPath option or the POCKET_IC_BIN environment variable, matching the Rust and Python PocketIC clients. When POCKET_IC_BIN is set at install time, the install script skips downloading the binary. The downloaded binary remains the default. --- .../src/content/docs/guides/running-tests.mdx | 20 +++++++++++ packages/pic/postinstall.mjs | 8 ++++- packages/pic/src/error.ts | 2 +- packages/pic/src/pocket-ic-server-types.ts | 7 ++++ packages/pic/src/pocket-ic-server.ts | 10 ++++-- .../pic/tests/src/pocket-ic-server.spec.ts | 36 +++++++++++++++++++ 6 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 packages/pic/tests/src/pocket-ic-server.spec.ts diff --git a/docs/src/content/docs/guides/running-tests.mdx b/docs/src/content/docs/guides/running-tests.mdx index c2c162c..157f3a2 100644 --- a/docs/src/content/docs/guides/running-tests.mdx +++ b/docs/src/content/docs/guides/running-tests.mdx @@ -4,6 +4,26 @@ sidebar: order: 5 --- +## Configuring the server binary + +By default, the PocketIC server binary downloaded when installing `@dfinity/pic` is used. A different binary can be provided using the `binPath` option, for example: + +```ts +const pic = await PocketIcServer.start({ + binPath: '/path/to/pocket-ic', +}); +``` + +Alternatively, the `POCKET_IC_BIN` environment variable can be set to the path of the binary, for example: + +```shell +POCKET_IC_BIN=/path/to/pocket-ic npm test +``` + +The `binPath` option takes precedence over the `POCKET_IC_BIN` environment variable. + +If `POCKET_IC_BIN` is set when installing `@dfinity/pic`, the install script skips downloading the binary. + ## Configuring logging ### Canister logs diff --git a/packages/pic/postinstall.mjs b/packages/pic/postinstall.mjs index 8827607..1258ca4 100644 --- a/packages/pic/postinstall.mjs +++ b/packages/pic/postinstall.mjs @@ -85,4 +85,10 @@ async function downloadPicBinary() { chmodSync(TARGET_PATH, 0o700); } -downloadPicBinary(); +if (process.env.POCKET_IC_BIN) { + console.log( + `POCKET_IC_BIN is set to ${process.env.POCKET_IC_BIN}, skipping pocket-ic download.`, + ); +} else { + await downloadPicBinary(); +} diff --git a/packages/pic/src/error.ts b/packages/pic/src/error.ts index 5d6885c..4158a31 100644 --- a/packages/pic/src/error.ts +++ b/packages/pic/src/error.ts @@ -34,7 +34,7 @@ export class BinNotFoundError extends Error { constructor(picBinPath: string) { super( - `Could not find the PocketIC binary. The PocketIC binary could not be found at ${picBinPath}. Please try installing @dfinity/pic again.`, + `Could not find the PocketIC binary at ${picBinPath}. If the path was provided via the binPath option or the POCKET_IC_BIN environment variable, please check that it points to an existing binary. Otherwise, please try installing @dfinity/pic again.`, ); } } diff --git a/packages/pic/src/pocket-ic-server-types.ts b/packages/pic/src/pocket-ic-server-types.ts index 694819b..8d94e2b 100644 --- a/packages/pic/src/pocket-ic-server-types.ts +++ b/packages/pic/src/pocket-ic-server-types.ts @@ -11,4 +11,11 @@ export interface StartServerOptions { * Whether to pipe the canister logs to the parent process's stderr. */ showCanisterLogs?: boolean; + + /** + * Path to the PocketIC server binary. + * Defaults to the `POCKET_IC_BIN` environment variable if set, + * otherwise the binary downloaded by this package's install script. + */ + binPath?: string; } diff --git a/packages/pic/src/pocket-ic-server.ts b/packages/pic/src/pocket-ic-server.ts index 4bd7982..80065d3 100644 --- a/packages/pic/src/pocket-ic-server.ts +++ b/packages/pic/src/pocket-ic-server.ts @@ -63,7 +63,7 @@ export class PocketIcServer { public static async start( options: StartServerOptions = {}, ): Promise { - const binPath = this.getBinPath(); + const binPath = this.getBinPath(options); await this.assertBinExists(binPath); const pid = process.ppid; @@ -145,8 +145,12 @@ export class PocketIcServer { }); } - private static getBinPath(): string { - return resolve(__dirname, '..', 'pocket-ic'); + private static getBinPath(options: StartServerOptions): string { + return ( + options.binPath || + process.env.POCKET_IC_BIN || + resolve(__dirname, '..', 'pocket-ic') + ); } private static async assertBinExists(binPath: string): Promise { diff --git a/packages/pic/tests/src/pocket-ic-server.spec.ts b/packages/pic/tests/src/pocket-ic-server.spec.ts new file mode 100644 index 0000000..14e6cf9 --- /dev/null +++ b/packages/pic/tests/src/pocket-ic-server.spec.ts @@ -0,0 +1,36 @@ +import { PocketIcServer } from '../../src'; +import { BinNotFoundError } from '../../src/error'; + +describe('PocketIcServer', () => { + const originalPocketIcBin = process.env.POCKET_IC_BIN; + + afterEach(() => { + if (originalPocketIcBin === undefined) { + delete process.env.POCKET_IC_BIN; + } else { + process.env.POCKET_IC_BIN = originalPocketIcBin; + } + }); + + it('should use the binary from the binPath option', async () => { + await expect( + PocketIcServer.start({ binPath: '/non-existent/bin-path/pocket-ic' }), + ).rejects.toThrow(new BinNotFoundError('/non-existent/bin-path/pocket-ic')); + }); + + it('should use the binary from the POCKET_IC_BIN environment variable', async () => { + process.env.POCKET_IC_BIN = '/non-existent/env-path/pocket-ic'; + + await expect(PocketIcServer.start()).rejects.toThrow( + new BinNotFoundError('/non-existent/env-path/pocket-ic'), + ); + }); + + it('should prefer the binPath option over the POCKET_IC_BIN environment variable', async () => { + process.env.POCKET_IC_BIN = '/non-existent/env-path/pocket-ic'; + + await expect( + PocketIcServer.start({ binPath: '/non-existent/bin-path/pocket-ic' }), + ).rejects.toThrow(new BinNotFoundError('/non-existent/bin-path/pocket-ic')); + }); +}); From 78b2fa8ab94c58c383ea4f62575df135448ddd9e Mon Sep 17 00:00:00 2001 From: Kamil Listopad Date: Tue, 28 Jul 2026 12:09:09 +0200 Subject: [PATCH 2/3] docs(pic): clarify binPath fallback when download was skipped --- packages/pic/src/pocket-ic-server-types.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/pic/src/pocket-ic-server-types.ts b/packages/pic/src/pocket-ic-server-types.ts index 8d94e2b..08225b2 100644 --- a/packages/pic/src/pocket-ic-server-types.ts +++ b/packages/pic/src/pocket-ic-server-types.ts @@ -16,6 +16,9 @@ export interface StartServerOptions { * Path to the PocketIC server binary. * Defaults to the `POCKET_IC_BIN` environment variable if set, * otherwise the binary downloaded by this package's install script. + * Note that the install script skips the download when `POCKET_IC_BIN` + * is set at install time, so there is no downloaded binary to fall + * back to in that case. */ binPath?: string; } From 792ec55d8485798f614da48408cf07c0e839aef3 Mon Sep 17 00:00:00 2001 From: Kamil Listopad Date: Tue, 28 Jul 2026 15:09:44 +0200 Subject: [PATCH 3/3] fix(pic): always download the binary at install time Drop the install-time download skip: a POCKET_IC_BIN value set at install time (potentially by an unrelated context) would leave the package without a binary to fall back to at runtime. --- docs/src/content/docs/guides/running-tests.mdx | 2 -- packages/pic/postinstall.mjs | 8 +------- packages/pic/src/pocket-ic-server-types.ts | 3 --- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/docs/src/content/docs/guides/running-tests.mdx b/docs/src/content/docs/guides/running-tests.mdx index 157f3a2..f62d2ae 100644 --- a/docs/src/content/docs/guides/running-tests.mdx +++ b/docs/src/content/docs/guides/running-tests.mdx @@ -22,8 +22,6 @@ POCKET_IC_BIN=/path/to/pocket-ic npm test The `binPath` option takes precedence over the `POCKET_IC_BIN` environment variable. -If `POCKET_IC_BIN` is set when installing `@dfinity/pic`, the install script skips downloading the binary. - ## Configuring logging ### Canister logs diff --git a/packages/pic/postinstall.mjs b/packages/pic/postinstall.mjs index 1258ca4..e9fa903 100644 --- a/packages/pic/postinstall.mjs +++ b/packages/pic/postinstall.mjs @@ -85,10 +85,4 @@ async function downloadPicBinary() { chmodSync(TARGET_PATH, 0o700); } -if (process.env.POCKET_IC_BIN) { - console.log( - `POCKET_IC_BIN is set to ${process.env.POCKET_IC_BIN}, skipping pocket-ic download.`, - ); -} else { - await downloadPicBinary(); -} +await downloadPicBinary(); diff --git a/packages/pic/src/pocket-ic-server-types.ts b/packages/pic/src/pocket-ic-server-types.ts index 08225b2..8d94e2b 100644 --- a/packages/pic/src/pocket-ic-server-types.ts +++ b/packages/pic/src/pocket-ic-server-types.ts @@ -16,9 +16,6 @@ export interface StartServerOptions { * Path to the PocketIC server binary. * Defaults to the `POCKET_IC_BIN` environment variable if set, * otherwise the binary downloaded by this package's install script. - * Note that the install script skips the download when `POCKET_IC_BIN` - * is set at install time, so there is no downloaded binary to fall - * back to in that case. */ binPath?: string; }