Skip to content

Commit

Permalink
feat(processes): waitForPort helper (#452)
Browse files Browse the repository at this point in the history
* feat(processes): waitForPort helper

* ci: adjust timeout

* ci: install lsof

* chore: replace `lsof` by `node:net`

* ci: fix delay

* chore: remove lsof

* chore: fix imports

* docs: add `waitForPort` documentation and examples
  • Loading branch information
wellwelwel authored Jun 24, 2024
1 parent 9110430 commit 01751f8
Show file tree
Hide file tree
Showing 30 changed files with 431 additions and 39 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ deno run npm:poku
- [**startScript**](https://poku.io/docs/documentation/helpers/startScript) _(run **package.json** scripts in background)_
- [**startService**](https://poku.io/docs/documentation/helpers/startService) _(run files in background)_
- [**kill**](https://poku.io/docs/documentation/helpers/processes/kill) _(terminate ports, port ranges, and PIDs)_
- [**waitForPort**](https://poku.io/docs/documentation/helpers/processes/wait-for-port) _(wait for specified ports to become active)_
- [**getPIDs**](https://poku.io/docs/documentation/helpers/processes/get-pids) _(debug processes IDs using ports and port ranges)_
- _and much more_ 👇🏻

Expand Down
34 changes: 34 additions & 0 deletions src/@types/processes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export type WaitForPortOptions = {
/**
* Retry interval in milliseconds
*
* ---
*
* @default 100
*/
interval?: number;
/**
* Timeout in milliseconds
*
* ---
*
* @default 60000
*/
timeout?: number;
/**
* Delays both the start and end by the defined milliseconds.
*
* ---
*
* @default 0
*/
delay?: number;
/**
* Host to check the port on.
*
* ---
*
* @default "localhost"
*/
host?: string;
};
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ export { beforeEach, afterEach } from './modules/each.js';
export { publicListFiles as listFiles } from './modules/list-files-sync.js';
export { startService, startScript } from './modules/create-service.js';
export { getPIDs, kill } from './modules/processes.js';
export { sleep, waitForPort } from './modules/wait-for.js';
export { exit } from './modules/exit.js';
export { docker } from './modules/container.js';
export type { Code } from './@types/code.js';
export type { Configs } from './@types/poku.js';
export type { WaitForPortOptions } from './@types/processes.js';
export type { Configs as ListFilesConfigs } from './@types/list-files.js';
export type {
DockerComposeConfigs,
Expand Down
86 changes: 86 additions & 0 deletions src/modules/wait-for.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/* c8 ignore next */
import type { WaitForPortOptions } from '../@types/processes.js';
import { createConnection } from 'node:net';

const checkPort = (port: number, host: string): Promise<boolean> =>
new Promise((resolve) => {
const client = createConnection(port, host);

client.on('connect', () => {
client.destroy();
resolve(true);
});

/* c8 ignore start */
client.on('error', () => {
resolve(false);
});
/* c8 ignore stop */
});

/**
* Wait until the defined milliseconds.
*/
export const sleep = (milliseconds: number): Promise<void> => {
/* c8 ignore start */
if (!Number.isInteger(milliseconds)) {
throw new Error(`Milliseconds must be an integer.`);
}
/* c8 ignore stop */

return new Promise((resolve) => setTimeout(resolve, milliseconds));
};

/* c8 ignore start */ // c8 bug
/**
* Wait until the defined port is active.
*/
export const waitForPort = async (
port: number,
options?: WaitForPortOptions
): Promise<void> => {
/* c8 ignore stop */
const delay = options?.delay || 0;
const interval = options?.interval || 100;
const timeout = options?.timeout || 60000;
const host = options?.host || 'localhost';

/* c8 ignore start */
if (!Number.isInteger(port)) {
throw new Error('Port must be an integer.');
}

if (!Number.isInteger(interval)) {
throw new Error('Interval must be an integer.');
}

if (!Number.isInteger(timeout)) {
throw new Error('Timeout must be an integer.');
}

if (!Number.isInteger(delay)) {
throw new Error('Delay must be an integer.');
}
/* c8 ignore stop */

await sleep(delay);

const startTime = Date.now();

// eslint-disable-next-line no-constant-condition
while (true) {
const hasPort = await checkPort(port, host);

if (hasPort) break;

/* c8 ignore start */
if (Date.now() - startTime >= timeout) {
throw new Error(`Timeout waiting for port ${port} to become active`);
}
/* c8 ignore stop */

await sleep(interval);
}

await sleep(delay);
};
2 changes: 1 addition & 1 deletion test/ci.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ test(async () => {
noExit: true,
});

if (result === 0) await compose.down();
// if (result === 0) await compose.down();

exit(result);
});
2 changes: 2 additions & 0 deletions test/docker/node/06.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
3 changes: 3 additions & 0 deletions test/docker/node/07.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk update
RUN apk add lsof

CMD ["node", "test/run.test.js"]
2 changes: 2 additions & 0 deletions test/docker/node/08.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
3 changes: 3 additions & 0 deletions test/docker/node/09.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk update
RUN apk add lsof

CMD ["node", "test/run.test.js"]
2 changes: 2 additions & 0 deletions test/docker/node/10.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
2 changes: 2 additions & 0 deletions test/docker/node/11.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
2 changes: 2 additions & 0 deletions test/docker/node/12.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
2 changes: 2 additions & 0 deletions test/docker/node/13.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
2 changes: 2 additions & 0 deletions test/docker/node/14.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
2 changes: 2 additions & 0 deletions test/docker/node/15.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
2 changes: 2 additions & 0 deletions test/docker/node/16.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
2 changes: 2 additions & 0 deletions test/docker/node/17.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
2 changes: 2 additions & 0 deletions test/docker/node/18.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
2 changes: 2 additions & 0 deletions test/docker/node/19.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
2 changes: 2 additions & 0 deletions test/docker/node/20.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
2 changes: 2 additions & 0 deletions test/docker/node/21.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
2 changes: 2 additions & 0 deletions test/docker/node/latest.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ WORKDIR /usr/app
COPY ./ci ./
COPY ./fixtures/server/package.json ./fixtures/server/package.json

RUN apk add lsof

CMD ["node", "test/run.test.js"]
1 change: 1 addition & 0 deletions test/docker/playground/deno/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ COPY ./test ./test
COPY ./tools ./tools
COPY ./fixtures ./fixtures

RUN apk add lsof
RUN deno run --allow-read --allow-write --allow-env --allow-run --unstable-sloppy-imports tools/compatibility/deno.ts
# deno run --allow-read --allow-net --allow-env --allow-run test/

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { format } from '../../../src/helpers/format.js';
import { docker } from '../../../src/modules/container.js';
import { legacyFetch } from '../../helpers/legacy-fetch.test.js';
import { isWindows } from '../../../src/helpers/runner.js';
import { waitForPort } from '../../../src/modules/wait-for.js';

// External error: no matching manifest for windows/amd64
if (isWindows) process.exit(0);
Expand Down Expand Up @@ -38,21 +39,17 @@ describe('Docker Compose Service', async () => {

await compose.up();

await new Promise((resolve) =>
setTimeout(async () => {
const res = await legacyFetch('localhost', 6001);
await waitForPort(6001, { delay: 100 });

await compose.down();
const res = await legacyFetch('localhost', 6001);

assert.strictEqual(res?.statusCode, 200, 'Service is on');
assert.strictEqual(
JSON.stringify(res?.body),
'"Hello, World!\\n"',
'Service is online'
);
await compose.down();

resolve(undefined);
}, 1000)
assert.strictEqual(res?.statusCode, 200, 'Service is on');
assert.strictEqual(
JSON.stringify(res?.body),
'"Hello, World!\\n"',
'Service is online'
);
});

Expand All @@ -64,21 +61,17 @@ describe('Docker Compose Service', async () => {

await compose.up();

await new Promise((resolve) =>
setTimeout(async () => {
const res = await legacyFetch('localhost', 6001);
await waitForPort(6001, { delay: 100 });

await compose.down();
const res = await legacyFetch('localhost', 6001);

assert.strictEqual(res?.statusCode, 200, 'Service is on');
assert.strictEqual(
JSON.stringify(res?.body),
'"Hello, World!\\n"',
'Service is online'
);
await compose.down();

resolve(undefined);
}, 1000)
assert.strictEqual(res?.statusCode, 200, 'Service is on');
assert.strictEqual(
JSON.stringify(res?.body),
'"Hello, World!\\n"',
'Service is online'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { assert } from '../../../src/modules/assert.js';
import { write } from '../../../src/helpers/logs.js';
import { format } from '../../../src/helpers/format.js';
import { docker } from '../../../src/modules/container.js';
import { waitForPort } from '../../../src/modules/wait-for.js';
import { legacyFetch } from '../../helpers/legacy-fetch.test.js';
import { isWindows } from '../../../src/helpers/runner.js';

Expand Down Expand Up @@ -43,22 +44,18 @@ describe('Docker Service', async () => {
await dockerfile.build();
await dockerfile.start();

await new Promise((resolve) =>
setTimeout(async () => {
const res = await legacyFetch('localhost', 6000);
await waitForPort(6000, { delay: 100 });

await dockerfile.stop();
await dockerfile.remove();
const res = await legacyFetch('localhost', 6000);

assert.strictEqual(res?.statusCode, 200, 'Service is on');
assert.strictEqual(
JSON.stringify(res?.body),
'"Hello, World!\\n"',
'Service is online'
);
await dockerfile.stop();
await dockerfile.remove();

resolve(undefined);
}, 1000)
assert.strictEqual(res?.statusCode, 200, 'Service is on');
assert.strictEqual(
JSON.stringify(res?.body),
'"Hello, World!\\n"',
'Service is online'
);
});

Expand Down
2 changes: 2 additions & 0 deletions test/integration/import.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ index.test('Import Suite', () => {
index.assert.ok(index.afterEach, 'Importing afterEach helper');
index.assert.ok(index.log, 'Importing log helper');
index.assert.ok(index.test, 'Importing test helper');
index.assert.ok(index.sleep, 'Importing sleep helper');
index.assert.ok(index.waitForPort, 'Importing waitForPort helper');
index.assert.ok(index.exit, 'Importing exit helper');
index.assert.ok(index.listFiles, 'Importing listFiles helper');
});
Loading

0 comments on commit 01751f8

Please sign in to comment.