Browse Source

test: cron jobs

Nicolas Meienberger 2 years ago
parent
commit
9d4cdf6f77

+ 0 - 0
media/data/music/.gitkeep


+ 11 - 0
packages/system-api/__mocks__/node-cron.ts

@@ -0,0 +1,11 @@
+const cron: {
+  schedule: typeof schedule;
+} = jest.genMockFromModule('node-cron');
+
+const schedule = (scd: string, cb: () => void) => {
+  cb();
+};
+
+cron.schedule = schedule;
+
+module.exports = cron;

+ 6 - 0
packages/system-api/jest.config.cjs

@@ -12,4 +12,10 @@ module.exports = {
   transform: {
     '^.+\\.graphql$': 'graphql-import-node/jest',
   },
+  globals: {
+    // NODE_ENV: 'test',
+    'ts-jest': {
+      isolatedModules: true,
+    },
+  },
 };

+ 32 - 0
packages/system-api/src/core/jobs/__tests__/jobs.test.ts

@@ -0,0 +1,32 @@
+import cron from 'node-cron';
+import * as repoHelpers from '../../../helpers/repo-helpers';
+import { getConfig } from '../../config/TipiConfig';
+import startJobs from '../jobs';
+
+jest.mock('node-cron');
+jest.mock('child_process');
+
+beforeEach(async () => {
+  jest.resetModules();
+  jest.resetAllMocks();
+});
+
+describe('Test: startJobs', () => {
+  it('Should start cron jobs', () => {
+    const spy = jest.spyOn(cron, 'schedule');
+
+    startJobs();
+    expect(spy).toHaveBeenCalled();
+    expect(spy).toHaveBeenCalledWith('0 * * * *', expect.any(Function));
+    spy.mockRestore();
+  });
+
+  it('Should update apps repo on cron trigger', () => {
+    const spy = jest.spyOn(repoHelpers, 'updateRepo');
+
+    startJobs();
+
+    expect(spy).toHaveBeenCalledWith(getConfig().appsRepoUrl);
+    spy.mockRestore();
+  });
+});

+ 1 - 1
packages/system-api/src/core/jobs/jobs.ts

@@ -7,7 +7,7 @@ const startJobs = () => {
   logger.info('Starting cron jobs...');
 
   cron.schedule('0 * * * *', () => {
-    logger.info('Cloning apps repo...');
+    logger.info('Updating apps repo...');
     updateRepo(getConfig().appsRepoUrl);
   });
 };

+ 6 - 5
packages/system-api/src/core/updates/recover-migrations.ts

@@ -1,10 +1,10 @@
-import datasource from '../../config/datasource';
+import { DataSource } from 'typeorm';
 import logger from '../../config/logger/logger';
 import App from '../../modules/apps/app.entity';
 import User from '../../modules/auth/user.entity';
 import Update from '../../modules/system/update.entity';
 
-const recover = async () => {
+const recover = async (datasource: DataSource) => {
   logger.info('Recovering broken database');
 
   const queryRunner = datasource.createQueryRunner();
@@ -33,9 +33,10 @@ const recover = async () => {
     await Update.create(update).save();
   }
 
-  logger.info('Users recovered', users.length);
-  logger.info('Apps recovered', apps.length);
-  logger.info('Database recovered');
+  logger.info(`Users recovered ${users.length}`);
+  logger.info(`Apps recovered ${apps.length}`);
+  logger.info(`Updates recovered ${updates.length}`);
+  logger.info('Database fully recovered');
 };
 
 export default recover;

+ 4 - 0
packages/system-api/src/helpers/__tests__/repo-helpers.test.ts

@@ -28,6 +28,7 @@ describe('Test: updateRepo', () => {
 
     expect(spy).toHaveBeenCalledWith(`${getConfig().rootFolder}/scripts/git.sh`, ['update', url], {}, expect.any(Function));
     expect(log).toHaveBeenCalledWith(`Update result: ${stdout}`);
+    spy.mockRestore();
   });
 
   it('Should throw and log error if script failed', async () => {
@@ -50,6 +51,7 @@ describe('Test: updateRepo', () => {
       expect(e).toBe(randomWord);
       expect(log).toHaveBeenCalledWith(`Error updating repo: ${randomWord}`);
     }
+    spy.mockRestore();
   });
 });
 
@@ -70,6 +72,7 @@ describe('Test: cloneRepo', () => {
 
     expect(spy).toHaveBeenCalledWith(`${getConfig().rootFolder}/scripts/git.sh`, ['clone', url], {}, expect.any(Function));
     expect(log).toHaveBeenCalledWith(`Clone result ${stdout}`);
+    spy.mockRestore();
   });
 
   it('Should throw and log error if script failed', async () => {
@@ -92,5 +95,6 @@ describe('Test: cloneRepo', () => {
       expect(e).toBe(randomWord);
       expect(log).toHaveBeenCalledWith(`Error cloning repo: ${randomWord}`);
     }
+    spy.mockRestore();
   });
 });

+ 36 - 0
packages/system-api/src/modules/fs/__tests__/fs.helpers.test.ts

@@ -2,6 +2,7 @@ import childProcess from 'child_process';
 import { getAbsolutePath, readJsonFile, readFile, readdirSync, fileExists, writeFile, createFolder, deleteFolder, runScript, getSeed, ensureAppFolder } from '../fs.helpers';
 import fs from 'fs-extra';
 import { getConfig } from '../../../core/config/TipiConfig';
+import { faker } from '@faker-js/faker';
 
 jest.mock('fs-extra');
 
@@ -36,6 +37,22 @@ describe('Test: readJsonFile', () => {
   it('should return null if the file does not exist', () => {
     expect(readJsonFile('/test')).toBeNull();
   });
+
+  it('Should return null if fs.readFile throws an error', () => {
+    // Arrange
+    // @ts-ignore
+    const spy = jest.spyOn(fs, 'readFileSync');
+    spy.mockImplementation(() => {
+      throw new Error('Error');
+    });
+
+    // Act
+    const file = readJsonFile('/test');
+
+    // Assert
+    expect(file).toBeNull();
+    spy.mockRestore();
+  });
 });
 
 describe('Test: readFile', () => {
@@ -197,4 +214,23 @@ describe('Test: ensureAppFolder', () => {
     const files = fs.readdirSync(`${getConfig().rootFolder}/apps/test`);
     expect(files).toEqual(['test.yml']);
   });
+
+  it('Should delete folder if it exists but has no docker-compose.yml file', () => {
+    // Arrange
+    const randomFileName = `${faker.random.word()}.yml`;
+    const mockFiles = {
+      [`${getConfig().rootFolder}/repos/${getConfig().appsRepoId}/apps/test`]: [randomFileName],
+      [`${getConfig().rootFolder}/apps/test`]: ['test.yml'],
+    };
+
+    // @ts-ignore
+    fs.__createMockFiles(mockFiles);
+
+    // Act
+    ensureAppFolder('test');
+
+    // Assert
+    const files = fs.readdirSync(`${getConfig().rootFolder}/apps/test`);
+    expect(files).toEqual([randomFileName]);
+  });
 });

+ 1 - 1
packages/system-api/src/server.ts

@@ -84,7 +84,7 @@ const main = async () => {
       await datasource.runMigrations();
     } catch (e) {
       logger.error(e);
-      await recover();
+      await recover(datasource);
     }
 
     // Run migrations

+ 2 - 1
packages/system-api/src/test/connection.ts

@@ -38,7 +38,8 @@ export const setupConnection = async (testsuite: string): Promise<DataSource> =>
     entities: [App, User, Update],
   });
 
-  return AppDataSource.initialize();
+  await AppDataSource.initialize();
+  return AppDataSource;
 };
 
 export const teardownConnection = async (testsuite: string): Promise<void> => {