mirror of
https://github.com/n8n-io/n8n.git
synced 2026-09-24 23:22:38 +08:00
test(core): Stop Redis retryStrategy from exiting the process under test (#31959)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,14 @@ import { RedisClientService } from '@/services/redis-client.service';
|
||||
|
||||
type EventHandler = (...args: unknown[]) => void;
|
||||
|
||||
/**
|
||||
* The service disables process exit under test by default. Re-enable it so the
|
||||
* cumulative-timeout accounting tests can assert on the exit decision.
|
||||
*/
|
||||
const enableExitOnRedisUnreachable = (service: RedisClientService) => {
|
||||
(service as unknown as { exitOnRedisUnreachable: boolean }).exitOnRedisUnreachable = true;
|
||||
};
|
||||
|
||||
jest.mock('ioredis', () => {
|
||||
return jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
@@ -129,6 +137,7 @@ describe('RedisClientService', () => {
|
||||
const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
||||
|
||||
const service = new RedisClientService(logger, globalConfig);
|
||||
enableExitOnRedisUnreachable(service);
|
||||
service.createClient({ type: 'client(bull)' });
|
||||
service.createClient({ type: 'subscriber(bull)' });
|
||||
|
||||
@@ -168,6 +177,7 @@ describe('RedisClientService', () => {
|
||||
const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
||||
|
||||
const service = new RedisClientService(logger, globalConfig);
|
||||
enableExitOnRedisUnreachable(service);
|
||||
service.createClient({ type: 'client(bull)' });
|
||||
service.createClient({ type: 'subscriber(bull)' });
|
||||
|
||||
@@ -219,6 +229,7 @@ describe('RedisClientService', () => {
|
||||
const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
||||
|
||||
const service = new RedisClientService(logger, globalConfig);
|
||||
enableExitOnRedisUnreachable(service);
|
||||
service.createClient({ type: 'client(bull)' });
|
||||
|
||||
const mockClient = mockedRedis.mock.results[0].value;
|
||||
@@ -247,5 +258,28 @@ describe('RedisClientService', () => {
|
||||
dateNowSpy.mockRestore();
|
||||
exitSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should not exit the process under test even when the timeout is exceeded', () => {
|
||||
const T0 = 1_700_000_000_000;
|
||||
const dateNowSpy = jest.spyOn(Date, 'now').mockReturnValue(T0);
|
||||
const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
||||
|
||||
// No enableExitOnRedisUnreachable() — exercise the default test behaviour.
|
||||
const service = new RedisClientService(logger, globalConfig);
|
||||
service.createClient({ type: 'client(bull)' });
|
||||
|
||||
const retryStrategy = mockedRedis.mock.calls[0][0].retryStrategy as () => number;
|
||||
|
||||
// Fail continuously well past the timeout threshold.
|
||||
dateNowSpy.mockReturnValue(T0 + 1_000);
|
||||
retryStrategy();
|
||||
dateNowSpy.mockReturnValue(T0 + 12_001);
|
||||
retryStrategy();
|
||||
|
||||
expect(exitSpy).not.toHaveBeenCalled();
|
||||
|
||||
dateNowSpy.mockRestore();
|
||||
exitSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Logger } from '@n8n/backend-common';
|
||||
import { inTest, Logger } from '@n8n/backend-common';
|
||||
import { GlobalConfig } from '@n8n/config';
|
||||
import { Debounce } from '@n8n/decorators';
|
||||
import { Service } from '@n8n/di';
|
||||
@@ -43,6 +43,13 @@ export class RedisClientService extends TypedEmitter<RedisEventMap> {
|
||||
/** Whether any client has lost connection to Redis. */
|
||||
private lostConnection = false;
|
||||
|
||||
/**
|
||||
* Whether to exit the process when Redis stays unreachable past the timeout.
|
||||
* Disabled under test so a client that can never reach Redis (e.g. no Redis
|
||||
* in DB-test runs) does not call `process.exit` and kill the test worker.
|
||||
*/
|
||||
private readonly exitOnRedisUnreachable = !inTest;
|
||||
|
||||
constructor(
|
||||
private readonly logger: Logger,
|
||||
private readonly globalConfig: GlobalConfig,
|
||||
@@ -243,8 +250,10 @@ export class RedisClientService extends TypedEmitter<RedisEventMap> {
|
||||
if (cumulativeTimeout > this.config.maxTimeout) {
|
||||
const maxTimeout = Math.round(this.config.maxTimeout / 1000) + 's';
|
||||
this.logger.error(`Unable to connect to Redis after trying to connect for ${maxTimeout}`);
|
||||
this.logger.error('Exiting process due to Redis connection error');
|
||||
process.exit(1);
|
||||
if (this.exitOnRedisUnreachable) {
|
||||
this.logger.error('Exiting process due to Redis connection error');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user