Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix!: stop piping into process.stdout/stderr and use console.log #2558

Merged
merged 29 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
aa25af1
fix: stop piping into `process.stdout/stderr` and use `console.log`
nedsalk Jun 27, 2024
8828c5f
revert change
nedsalk Jun 27, 2024
89312fd
simplify test
nedsalk Jun 27, 2024
b2d8ad3
fix: test
nedsalk Jun 27, 2024
e326075
fix: do cleanup when local
nedsalk Jun 27, 2024
25158a5
fix: test
nedsalk Jun 27, 2024
c2b9b17
Merge branch 'master' into ns/fix/stop-silencing-errors-fuels-init
nedsalk Jun 27, 2024
1d53220
Merge branch 'master' into ns/fix/stop-silencing-errors-fuels-init
nedsalk Jun 27, 2024
8785d9a
log without new line
nedsalk Jun 28, 2024
b1d11c9
Merge remote-tracking branch 'origin/master' into ns/fix/stop-silenci…
nedsalk Jun 28, 2024
ce5648f
backticks unnecessary
nedsalk Jun 28, 2024
7f61cc3
fix tests
nedsalk Jun 28, 2024
457ec7e
Update packages/create-fuels/src/utils/logger.test.ts
nedsalk Jun 28, 2024
70bf66c
Update packages/create-fuels/src/utils/logger.test.ts
nedsalk Jun 28, 2024
017a371
purge remaining `process.stderr/stdout` usage
nedsalk Jun 28, 2024
d6039e5
Merge branch 'master' into ns/fix/stop-silencing-errors-fuels-init
nedsalk Jun 28, 2024
33ab502
Merge branch 'master' into ns/fix/stop-silencing-errors-fuels-init
petertonysmith94 Jun 28, 2024
a0a0f81
Merge branch 'master' into ns/fix/stop-silencing-errors-fuels-init
nedsalk Jun 28, 2024
45a97ce
empty commit
nedsalk Jun 28, 2024
8fb884b
Merge branch 'master' into ns/fix/stop-silencing-errors-fuels-init
nedsalk Jun 30, 2024
edb4fe4
fix: debugEnabled
nedsalk Jul 1, 2024
f4f98ef
Merge branch 'master' into ns/fix/stop-silencing-errors-fuels-init
nedsalk Jul 1, 2024
8f0125d
Merge branch 'master' into ns/fix/stop-silencing-errors-fuels-init
nedsalk Jul 1, 2024
5a572b9
chore: set as breaking change
nedsalk Jul 2, 2024
b8812fe
throw `FuelError` in `launchNode`
nedsalk Jul 2, 2024
791805f
remove \n
nedsalk Jul 2, 2024
c2829f5
Merge branch 'master' into ns/fix/stop-silencing-errors-fuels-init
nedsalk Jul 2, 2024
5cdd89b
Merge branch 'master' into ns/fix/stop-silencing-errors-fuels-init
maschad Jul 2, 2024
dd206b0
Merge branch 'master' into ns/fix/stop-silencing-errors-fuels-init
nedsalk Jul 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/flat-ways-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@fuel-ts/abi-typegen": patch
"@fuel-ts/account": minor
"create-fuels": patch
"fuels": patch
---

fix!: stop piping into `process.stdout/stderr` and use `console.log`
5 changes: 3 additions & 2 deletions packages/abi-typegen/src/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ describe('cli.ts', () => {
test('should handle errors when running cli action', () => {
const runTypegenError = new Error('Pretty message');

const { exit, err } = mockDeps({ runTypegenError });
const logSpy = vi.spyOn(console, 'log');
const { exit } = mockDeps({ runTypegenError });

const inputs = ['*-no-abis-here.json'];
const output = './aything';
Expand All @@ -175,6 +176,6 @@ describe('cli.ts', () => {
});

expect(exit).toBeCalledWith(1);
expect(err).toBeCalledWith(`error: ${runTypegenError.message}\n`);
expect(logSpy).toBeCalledWith(`error: ${runTypegenError.message}`);
});
});
3 changes: 2 additions & 1 deletion packages/abi-typegen/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ export function runCliAction(options: ICliParams) {
silent: !!silent,
});
} catch (err) {
process.stderr.write(`error: ${(<Error>err).message}\n`);
// eslint-disable-next-line no-console
console.log(`error: ${(<Error>err).message}`);
process.exit(1);
}
}
Expand Down
8 changes: 4 additions & 4 deletions packages/abi-typegen/src/runTypegen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ describe('runTypegen.js', () => {
});

test('should log messages to stdout', async () => {
const stdoutWrite = vi.spyOn(process.stdout, 'write').mockResolvedValue(true);
const logSpy = vi.spyOn(console, 'log');

// setup temp sway project
const project = getTypegenForcProject(AbiTypegenProjectsEnum.SCRIPT);
Expand Down Expand Up @@ -189,7 +189,7 @@ describe('runTypegen.js', () => {
// validates execution was ok
expect(error).toBeFalsy();

expect(stdoutWrite).toHaveBeenCalledTimes(5);
expect(logSpy).toHaveBeenCalledTimes(5);
});

test('should raise error for non-existent Script BIN file', async () => {
Expand Down Expand Up @@ -279,7 +279,7 @@ describe('runTypegen.js', () => {
cpSync(fromBin, toBin);

// mocking
const write = vi.spyOn(process.stdout, 'write').mockReturnValue(true);
const logSpy = vi.spyOn(console, 'log');

// executes program
const fn = () =>
Expand Down Expand Up @@ -313,7 +313,7 @@ describe('runTypegen.js', () => {
expect(existsSync(f)).toEqual(true);
});

expect(write).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalled();
});

test('should error for no ABI in inputs', async () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/abi-typegen/src/runTypegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ export function runTypegen(params: IGenerateFilesParams) {

function log(...args: unknown[]) {
if (!silent) {
process.stdout.write(`${args.join(' ')}\n`);
// eslint-disable-next-line no-console
console.log(args.join(' '));
}
}

Expand Down
187 changes: 70 additions & 117 deletions packages/account/src/test-utils/launchNode.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { safeExec } from '@fuel-ts/errors/test-utils';
import { ErrorCode } from '@fuel-ts/errors';
import { safeExec, expectToThrowFuelError } from '@fuel-ts/errors/test-utils';
import { defaultSnapshotConfigs } from '@fuel-ts/utils';
import { waitUntilUnreachable } from '@fuel-ts/utils/test-utils';
import * as childProcessMod from 'child_process';

import type { LaunchNodeOptions } from './launchNode';
import { Provider } from '../providers';

import { killNode, launchNode } from './launchNode';

type ChildProcessWithoutNullStreams = childProcessMod.ChildProcessWithoutNullStreams;
Expand All @@ -15,51 +18,6 @@ vi.mock('child_process', async () => {
};
});

/**
* This should mimic the stderr.on('data') event, returning both
* success and error messages, as strings. These messages are like
* the ones from `fuel-core` startup log messages. We filter them
* to know fuel-core state.
*/
function mockSpawn(params: { shouldError: boolean } = { shouldError: false }) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const stderrOn = (eventName: string, fn: (data: any) => void) => {
if (eventName === 'data') {
if (params.shouldError) {
// The `IO error` message simulates a possible fuel-core error log message
fn('IO error');
} else {
// The `Binding GraphQL provider to` message simulates a fuel-core
// successful startup log message, usually meaning that the node
// is up and waiting for connections
fn('Binding GraphQL provider to 0.0.0.0:4000');
}
}
};

const innerMocks = {
on: vi.fn(),
stderr: {
pipe: vi.fn(),
on: vi.fn(stderrOn),
removeAllListeners: vi.fn(),
},
stdout: {
pipe: vi.fn(),
removeAllListeners: vi.fn(),
},
} as unknown as ChildProcessWithoutNullStreams;

const spawn = vi.spyOn(childProcessMod, 'spawn').mockReturnValue(innerMocks);

return { spawn, innerMocks };
}

const defaultLaunchNodeConfig: Partial<LaunchNodeOptions> = {
ip: '0.0.0.0',
port: '4000',
};

/**
* @group node
*/
Expand All @@ -73,110 +31,105 @@ describe('launchNode', () => {
});

it('cleanup kills the started node', async () => {
const { cleanup, url } = await launchNode({});
const { cleanup, url } = await launchNode();
expect(await fetch(url)).toBeTruthy();

cleanup();

await waitUntilUnreachable(url);
});

test('should start `fuel-core` node using built-in binary', async () => {
mockSpawn();
test('should start `fuel-core` node using system binary', async () => {
const spawnSpy = vi.spyOn(childProcessMod, 'spawn');

const { cleanup, ip, port } = await launchNode({
...defaultLaunchNodeConfig,
});
process.env.FUEL_CORE_PATH = '';

expect(ip).toBe('0.0.0.0');
expect(port).toBe('4000');
cleanup();
});
const { result } = await safeExec(async () => launchNode());

test('should start `fuel-core` node using system binary', async () => {
mockSpawn();

const { cleanup, ip, port } = await launchNode(defaultLaunchNodeConfig);
const command = spawnSpy.mock.calls[0][0];
expect(command).toEqual('fuel-core');

expect(ip).toBe('0.0.0.0');
expect(port).toBe('4000');
process.env.FUEL_CORE_PATH = 'fuels-core';

cleanup();
/**
* result can be undefined when running in CI and fuel-core is not installed
* meaning that spawn(fuel-core, ...) threw an error
*/
if (result !== undefined) {
(await result)?.cleanup();
}
});

test('should start `fuel-core` node with custom binary', async () => {
const { spawn } = mockSpawn();
test('should start `fuel-core` node using custom binary', async () => {
const spawnSpy = vi.spyOn(childProcessMod, 'spawn');

const { cleanup, ip, port } = await launchNode({
...defaultLaunchNodeConfig,
fuelCorePath: 'fuels-core',
const fuelCorePath = './my-fuel-core-binary-path';
const { error } = await safeExec(async () => {
await launchNode({ fuelCorePath, loggingEnabled: true });
});

expect(ip).toBe('0.0.0.0');
expect(port).toBe('4000');
expect(spawn).toBeCalledWith('fuels-core', expect.any(Array), expect.any(Object));
expect(error).toBeTruthy();

cleanup();
const command = spawnSpy.mock.calls[0][0];
expect(command).toEqual(fuelCorePath);
});

test('should start `fuel-core` node with custom binaries', async () => {
const { spawn } = mockSpawn();

const { cleanup, ip, port } = await launchNode({
...defaultLaunchNodeConfig,
fuelCorePath: 'custom-fuels-core',
});
test('reads FUEL_CORE_PATH environment variable for fuel-core binary', async () => {
const spawnSpy = vi.spyOn(childProcessMod, 'spawn');
process.env.FUEL_CORE_PATH = 'fuels-core';
const { cleanup, url } = await launchNode();
await Provider.create(url);

expect(ip).toBe('0.0.0.0');
expect(port).toBe('4000');
expect(spawn).toBeCalledWith('custom-fuels-core', expect.any(Array), expect.any(Object));
const command = spawnSpy.mock.calls[0][0];
expect(command).toEqual('fuels-core');

cleanup();
});

test('should throw on error', async () => {
const { innerMocks } = mockSpawn({ shouldError: true });

const { error: safeError, result } = await safeExec(async () =>
launchNode(defaultLaunchNodeConfig)
test('should throw on error and log error message', async () => {
const logSpy = vi.spyOn(console, 'log');

const invalidCoin = {
asset_id: 'whatever',
tx_id: '',
output_index: 0,
tx_pointer_block_height: 0,
tx_pointer_tx_idx: 0,
owner: '',
amount: 0,
};

const error = await expectToThrowFuelError(
async () =>
launchNode({
loggingEnabled: false,
snapshotConfig: {
...defaultSnapshotConfigs,
stateConfig: {
coins: [invalidCoin],
messages: [],
},
},
}),
{
code: ErrorCode.NODE_LAUNCH_FAILED,
}
);

expect(safeError).toBeTruthy();
expect(result).not.toBeTruthy();
expect(error).toBeTruthy();

expect(innerMocks.on).toHaveBeenCalledTimes(1);
expect(innerMocks.stderr.pipe).toHaveBeenCalledTimes(1);
expect(innerMocks.stdout.pipe).toHaveBeenCalledTimes(0);
expect(logSpy).toHaveBeenCalledWith(error?.message);
});

test('should pipe stdout', async () => {
vi.spyOn(process.stdout, 'write');

const { innerMocks } = mockSpawn();

const { cleanup } = await launchNode(defaultLaunchNodeConfig);

expect(innerMocks.stderr.pipe).toHaveBeenCalledTimes(1);
expect(innerMocks.stdout.pipe).toHaveBeenCalledTimes(0);
test('logs fuel-core outputs via console.log', async () => {
const logSpy = vi.spyOn(console, 'log');

const { cleanup } = await launchNode({ loggingEnabled: true });
const logs = logSpy.mock.calls.map((call) => call[0]);
expect(logs.some((log) => log.includes('Binding GraphQL provider'))).toBe(true);
cleanup();
});

test('should pipe stdout and stderr', async () => {
vi.spyOn(process.stderr, 'write');
vi.spyOn(process.stdout, 'write');

const { innerMocks } = mockSpawn();

await launchNode({
...defaultLaunchNodeConfig,
debugEnabled: true,
});

expect(innerMocks.stderr.pipe).toHaveBeenCalledTimes(1);
expect(innerMocks.stdout.pipe).toHaveBeenCalledTimes(1);
});

test('should kill process only if PID exists and node is alive', () => {
const killFn = vi.fn();
const state = { isDead: true };
Expand Down
Loading
Loading