test: event dispatcher
This commit is contained in:
parent
a024b03508
commit
b6e41bbfb6
10 changed files with 232 additions and 36 deletions
|
@ -55,7 +55,7 @@ services:
|
|||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ${PWD}/apps:/runtipi/apps:ro
|
||||
- ${PWD}/repos:/runtipi/repos:ro
|
||||
- ${PWD}/state:/runtipi/state:ro
|
||||
- ${PWD}/state:/runtipi/state
|
||||
- ${PWD}/packages/system-api/src:/api/src
|
||||
- ${PWD}/logs:/app/logs
|
||||
- ${STORAGE_PATH}:/app/storage
|
||||
|
|
|
@ -48,7 +48,7 @@ services:
|
|||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ${PWD}/apps:/runtipi/apps:ro
|
||||
- ${PWD}/repos:/runtipi/repos:ro
|
||||
- ${PWD}/state:/runtipi/state:ro
|
||||
- ${PWD}/state:/runtipi/state
|
||||
- ${PWD}/logs:/app/logs
|
||||
- ${STORAGE_PATH}:/app/storage
|
||||
environment:
|
||||
|
|
|
@ -48,7 +48,7 @@ services:
|
|||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ${PWD}/apps:/runtipi/apps:ro
|
||||
- ${PWD}/repos:/runtipi/repos:ro
|
||||
- ${PWD}/state:/runtipi/state:ro
|
||||
- ${PWD}/state:/runtipi/state
|
||||
- ${PWD}/logs:/app/logs
|
||||
- ${STORAGE_PATH}:/app/storage
|
||||
environment:
|
||||
|
|
|
@ -33,6 +33,8 @@ class EventDispatcher {
|
|||
|
||||
private interval: NodeJS.Timer;
|
||||
|
||||
private intervals: NodeJS.Timer[] = [];
|
||||
|
||||
constructor() {
|
||||
const timer = this.pollQueue();
|
||||
this.interval = timer;
|
||||
|
@ -67,7 +69,6 @@ class EventDispatcher {
|
|||
return;
|
||||
}
|
||||
|
||||
console.log('Status: ', status, 'clearing');
|
||||
this.clearEvent(this.lock.id);
|
||||
this.lock = null;
|
||||
}
|
||||
|
@ -77,10 +78,17 @@ class EventDispatcher {
|
|||
*/
|
||||
private pollQueue() {
|
||||
logger.info('EventDispatcher: Polling queue...');
|
||||
return setInterval(() => {
|
||||
this.runEvent();
|
||||
this.collectLockStatusAndClean();
|
||||
}, 1000);
|
||||
|
||||
if (!this.interval) {
|
||||
const id = setInterval(() => {
|
||||
this.runEvent();
|
||||
this.collectLockStatusAndClean();
|
||||
}, 1000);
|
||||
this.intervals.push(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
return this.interval;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,7 +130,7 @@ class EventDispatcher {
|
|||
}
|
||||
|
||||
const file = fs.readFileSync(WATCH_FILE, 'utf8');
|
||||
const lines = file.split('\n') || [];
|
||||
const lines = file?.split('\n') || [];
|
||||
const line = lines.find((l) => l.startsWith(`${event.type} ${event.id}`));
|
||||
|
||||
if (!line) {
|
||||
|
@ -131,10 +139,6 @@ class EventDispatcher {
|
|||
|
||||
const status = line.split(' ')[2] as EventStatusTypes;
|
||||
|
||||
if (status === 'error') {
|
||||
console.error(lines);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -180,6 +184,7 @@ class EventDispatcher {
|
|||
|
||||
return new Promise((resolve) => {
|
||||
const interval = setInterval(() => {
|
||||
this.intervals.push(interval);
|
||||
const status = this.getEventStatus(event.id);
|
||||
|
||||
let log = '';
|
||||
|
@ -198,10 +203,14 @@ class EventDispatcher {
|
|||
});
|
||||
}
|
||||
|
||||
public clearInterval() {
|
||||
clearInterval(this.interval);
|
||||
this.intervals.forEach((i) => clearInterval(i));
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.queue = [];
|
||||
this.lock = null;
|
||||
clearInterval(this.interval);
|
||||
EventDispatcher.instance = null;
|
||||
fs.writeFileSync(WATCH_FILE, '');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
import fs from 'fs-extra';
|
||||
import { eventDispatcher, EventTypes } from '../EventDispatcher';
|
||||
|
||||
const WATCH_FILE = '/runtipi/state/events';
|
||||
|
||||
jest.mock('fs-extra');
|
||||
|
||||
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
beforeEach(() => {
|
||||
eventDispatcher.clear();
|
||||
fs.writeFileSync(WATCH_FILE, '');
|
||||
fs.writeFileSync('/app/logs/123.log', 'test');
|
||||
});
|
||||
|
||||
describe('EventDispatcher - dispatchEvent', () => {
|
||||
it('should dispatch an event', () => {
|
||||
const event = eventDispatcher.dispatchEvent(EventTypes.APP);
|
||||
expect(event.id).toBeDefined();
|
||||
});
|
||||
|
||||
it('should dispatch an event with args', () => {
|
||||
const event = eventDispatcher.dispatchEvent(EventTypes.APP, ['--help']);
|
||||
expect(event.id).toBeDefined();
|
||||
});
|
||||
|
||||
it('Should put events into queue', async () => {
|
||||
eventDispatcher.dispatchEvent(EventTypes.APP, ['--help']);
|
||||
eventDispatcher.dispatchEvent(EventTypes.APP, ['--help']);
|
||||
|
||||
// @ts-ignore
|
||||
const queue = eventDispatcher.queue;
|
||||
|
||||
expect(queue.length).toBe(2);
|
||||
});
|
||||
|
||||
it('Should put first event into lock after 1 sec', async () => {
|
||||
eventDispatcher.dispatchEvent(EventTypes.APP, ['--help']);
|
||||
eventDispatcher.dispatchEvent(EventTypes.UPDATE, ['--help']);
|
||||
|
||||
// @ts-ignore
|
||||
const queue = eventDispatcher.queue;
|
||||
|
||||
await wait(1050);
|
||||
|
||||
// @ts-ignore
|
||||
const lock = eventDispatcher.lock;
|
||||
|
||||
expect(queue.length).toBe(2);
|
||||
expect(lock).toBeDefined();
|
||||
expect(lock?.type).toBe(EventTypes.APP);
|
||||
});
|
||||
|
||||
it('Should clear event once its status is success', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(eventDispatcher, 'getEventStatus').mockReturnValueOnce('success');
|
||||
eventDispatcher.dispatchEvent(EventTypes.APP, ['--help']);
|
||||
|
||||
await wait(1050);
|
||||
|
||||
// @ts-ignore
|
||||
const queue = eventDispatcher.queue;
|
||||
|
||||
expect(queue.length).toBe(0);
|
||||
});
|
||||
|
||||
it('Should clear event once its status is error', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(eventDispatcher, 'getEventStatus').mockReturnValueOnce('error');
|
||||
eventDispatcher.dispatchEvent(EventTypes.APP, ['--help']);
|
||||
|
||||
await wait(1050);
|
||||
|
||||
// @ts-ignore
|
||||
const queue = eventDispatcher.queue;
|
||||
|
||||
expect(queue.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('EventDispatcher - dispatchEventAsync', () => {
|
||||
it('Should dispatch an event and wait for it to finish', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(eventDispatcher, 'getEventStatus').mockReturnValueOnce('success');
|
||||
const { success } = await eventDispatcher.dispatchEventAsync(EventTypes.APP, ['--help']);
|
||||
|
||||
expect(success).toBe(true);
|
||||
});
|
||||
|
||||
it('Should dispatch an event and wait for it to finish with error', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(eventDispatcher, 'getEventStatus').mockReturnValueOnce('error');
|
||||
|
||||
const { success } = await eventDispatcher.dispatchEventAsync(EventTypes.APP, ['--help']);
|
||||
|
||||
expect(success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('EventDispatcher - runEvent', () => {
|
||||
it('Should do nothing if there is a lock', async () => {
|
||||
// @ts-ignore
|
||||
eventDispatcher.lock = { id: '123', type: EventTypes.APP, args: [] };
|
||||
|
||||
// @ts-ignore
|
||||
await eventDispatcher.runEvent();
|
||||
|
||||
// @ts-ignore
|
||||
const file = fs.readFileSync(WATCH_FILE, 'utf8');
|
||||
|
||||
expect(file).toBe('');
|
||||
});
|
||||
|
||||
it('Should do nothing if there is no event in queue', async () => {
|
||||
// @ts-ignore
|
||||
await eventDispatcher.runEvent();
|
||||
|
||||
// @ts-ignore
|
||||
const file = fs.readFileSync(WATCH_FILE, 'utf8');
|
||||
|
||||
expect(file).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('EventDispatcher - getEventStatus', () => {
|
||||
it('Should return success if event is not in the queue', async () => {
|
||||
// @ts-ignore
|
||||
eventDispatcher.queue = [];
|
||||
// @ts-ignore
|
||||
const status = eventDispatcher.getEventStatus('123');
|
||||
|
||||
expect(status).toBe('success');
|
||||
});
|
||||
|
||||
it('Should return error if event is expired', async () => {
|
||||
const dateFiveMinutesAgo = new Date(new Date().getTime() - 5 * 60 * 10000);
|
||||
// @ts-ignore
|
||||
eventDispatcher.queue = [{ id: '123', type: EventTypes.APP, args: [], creationDate: dateFiveMinutesAgo }];
|
||||
// @ts-ignore
|
||||
const status = eventDispatcher.getEventStatus('123');
|
||||
|
||||
expect(status).toBe('error');
|
||||
});
|
||||
|
||||
it('Should be waiting if line is not found in the file', async () => {
|
||||
// @ts-ignore
|
||||
eventDispatcher.queue = [{ id: '123', type: EventTypes.APP, args: [], creationDate: new Date() }];
|
||||
// @ts-ignore
|
||||
const status = eventDispatcher.getEventStatus('123');
|
||||
|
||||
expect(status).toBe('waiting');
|
||||
});
|
||||
});
|
||||
|
||||
describe('EventDispatcher - clearEvent', () => {
|
||||
it('Should clear event', async () => {
|
||||
// @ts-ignore
|
||||
eventDispatcher.queue = [{ id: '123', type: EventTypes.APP, args: [], creationDate: new Date() }];
|
||||
// @ts-ignore
|
||||
eventDispatcher.clearEvent('123');
|
||||
|
||||
// @ts-ignore
|
||||
const queue = eventDispatcher.queue;
|
||||
|
||||
expect(queue.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('EventDispatcher - pollQueue', () => {
|
||||
it('Should not create a new interval if one already exists', async () => {
|
||||
// @ts-ignore
|
||||
eventDispatcher.interval = 123;
|
||||
// @ts-ignore
|
||||
const id = eventDispatcher.pollQueue();
|
||||
// @ts-ignore
|
||||
const interval = eventDispatcher.interval;
|
||||
|
||||
expect(interval).toBe(123);
|
||||
expect(id).toBe(123);
|
||||
|
||||
clearInterval(interval);
|
||||
clearInterval(id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('EventDispatcher - collectLockStatusAndClean', () => {
|
||||
it('Should do nothing if there is no lock', async () => {
|
||||
// @ts-ignore
|
||||
eventDispatcher.lock = null;
|
||||
// @ts-ignore
|
||||
eventDispatcher.collectLockStatusAndClean();
|
||||
|
||||
// @ts-ignore
|
||||
const lock = eventDispatcher.lock;
|
||||
|
||||
expect(lock).toBeNull();
|
||||
});
|
||||
});
|
|
@ -1,15 +1,10 @@
|
|||
import fs from 'fs-extra';
|
||||
import childProcess from 'child_process';
|
||||
import { getConfig } from '../../core/config/TipiConfig';
|
||||
|
||||
export const readJsonFile = (path: string): any => {
|
||||
try {
|
||||
const rawFile = fs.readFileSync(path).toString();
|
||||
|
||||
if (!rawFile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JSON.parse(rawFile);
|
||||
} catch (e) {
|
||||
return null;
|
||||
|
|
|
@ -7,5 +7,5 @@ jest.mock('../config/logger/logger', () => ({
|
|||
}));
|
||||
|
||||
afterAll(() => {
|
||||
eventDispatcher.clear();
|
||||
eventDispatcher.clearInterval();
|
||||
});
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
echo "Restarting Tipi..."
|
||||
|
||||
scripts/stop.sh
|
||||
scripts/start.sh
|
||||
|
||||
exit
|
|
@ -1,8 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
echo "Updating Tipi to latest version..."
|
||||
|
||||
scripts/stop.sh
|
||||
git pull origin master
|
||||
scripts/start.sh
|
||||
exit
|
|
@ -83,6 +83,16 @@ function select_command() {
|
|||
return 0
|
||||
fi
|
||||
|
||||
if [ "$command" = "update" ]; then
|
||||
run_command "${ROOT_FOLDER}/scripts/system.sh" "$id" "update"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$command" = "restart" ]; then
|
||||
run_command "${ROOT_FOLDER}/scripts/system.sh" "$id" "restart"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Unknown command ${command}"
|
||||
return 0
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue