Skip to content

Commit

Permalink
feat: added backend unit tests to husky precommit
Browse files Browse the repository at this point in the history
  • Loading branch information
rickimoore committed May 28, 2024
1 parent db7f411 commit a2d9662
Show file tree
Hide file tree
Showing 11 changed files with 315 additions and 60 deletions.
14 changes: 14 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,18 @@
. "$(dirname "$0")/_/husky.sh"

export PATH="/Users/rickimoore/lighthouse-ui:$PATH"

# Run the Next.js linter with auto-fixing
npx next lint --fix

# Navigate to the backend directory
cd backend

# Run Jest tests using yarn
yarn test

# Check if the Jest tests passed
if [ $? -ne 0 ]; then
echo "Jest tests failed. Aborting commit."
exit 1
fi
2 changes: 1 addition & 1 deletion app/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const Main = () => {
const searchParams = useSearchParams()
const redirect = searchParams.get('redirect')
const [isLoading, setLoading] = useState(false)
const [step, setStep] = useState<number>(1)
const [step] = useState<number>(1)
const [isReady, setReady] = useState(false)
const [isVersionError, setVersionError] = useState(false)
const [sessionToken, setToken] = useState(Cookies.get('session-token'))
Expand Down
78 changes: 75 additions & 3 deletions backend/src/logs/tests/logs.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,90 @@
import { Test, TestingModule } from '@nestjs/testing';
import { LogsController } from '../logs.controller';
import { LogsService } from '../logs.service';
import { UtilsModule } from '../../utils/utils.module';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { JwtModule } from '@nestjs/jwt';
import { HttpService } from '@nestjs/axios';
import { Cache } from 'cache-manager';
import { SequelizeModule } from '@nestjs/sequelize';
import { Log } from '../entities/log.entity';
import { Sequelize } from 'sequelize-typescript';
import { mockCritLog, mockErrorLog, mockWarningLog } from '../../../../src/mocks/logs';

describe('LogsController', () => {
let logsService: LogsService;
let controller: LogsController;
let cacheManager: Cache;
let httpService: HttpService;
let sequelize: Sequelize;

const mockCacheManager = {
get: jest.fn(),
set: jest.fn(),
};

const mockHttpService = {
request: jest.fn(),
};

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
UtilsModule,
SequelizeModule.forFeature([Log]),
SequelizeModule.forRoot({
dialect: 'sqlite',
storage: ':memory:',
autoLoadModels: true,
synchronize: true,
}),
JwtModule.register({
global: true,
secret: 'fake-value',
signOptions: { expiresIn: '7200s' }, // set to 2 hours
}),
],
providers: [LogsService],
controllers: [LogsController],
}).compile();
})
.overrideProvider(CACHE_MANAGER)
.useValue(mockCacheManager)
.overrideProvider(HttpService)
.useValue(mockHttpService)
.compile();

controller = module.get<LogsController>(LogsController);
cacheManager = module.get<Cache>(CACHE_MANAGER);
httpService = module.get<HttpService>(HttpService);
logsService = module.get<LogsService>(LogsService);
sequelize = module.get<Sequelize>(Sequelize);

mockCacheManager.get.mockResolvedValue(null);

await Log.bulkCreate([
mockWarningLog,
mockErrorLog,
mockCritLog,
]);
});

afterEach(async () => {
await sequelize.close();
});

it('should return log metrics', async () => {
const data = {
warningLogs: [mockWarningLog],
errorLogs: [mockErrorLog],
criticalLogs: [mockCritLog],
};

const result = await controller.getLogMetrics();
expect(result).toEqual(data);
});

it('should be defined', () => {
expect(controller).toBeDefined();
it('should update log metrics', async () => {
const result = await controller.dismissLogAlert('1');
expect(result).toEqual([1]);
});
});
18 changes: 0 additions & 18 deletions backend/src/logs/tests/logs.service.spec.ts

This file was deleted.

18 changes: 0 additions & 18 deletions backend/src/utils/utils.service.spec.ts

This file was deleted.

165 changes: 164 additions & 1 deletion backend/src/validator/tests/validator.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,181 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ValidatorController } from '../validator.controller';
import { Cache } from 'cache-manager';
import { HttpService } from '@nestjs/axios';
import { UtilsModule } from '../../utils/utils.module';
import { CACHE_MANAGER, CacheModule } from '@nestjs/cache-manager';
import { JwtModule } from '@nestjs/jwt';
import { ValidatorService } from '../validator.service';
import { AxiosResponse } from 'axios';
import { of } from 'rxjs';
import { mockFormattedStates, mockStateResults, mockValCacheValues } from '../../../../src/mocks/beacon';
import { mockValCacheResults, mockValInfoResult } from '../../../../src/mocks/validatorResults';
import { SequelizeModule } from '@nestjs/sequelize';
import { Metric } from '../entities/metric.entity';
import { Sequelize } from 'sequelize-typescript';

describe('ValidatorController', () => {
let controller: ValidatorController;

let cacheManager: Cache;
let httpService: HttpService;
let sequelize: Sequelize;

const mockCacheManager = {
get: jest.fn(),
set: jest.fn(),
};

const mockHttpService = {
request: jest.fn(),
};

beforeEach(async () => {
process.env.VALIDATOR_URL = 'mock-url'
process.env.API_TOKEN = 'mock-api-token'
const module: TestingModule = await Test.createTestingModule({
imports: [
UtilsModule,
CacheModule.register(),
SequelizeModule.forFeature([Metric]),
SequelizeModule.forRoot({
dialect: 'sqlite',
storage: ':memory:',
autoLoadModels: true,
synchronize: true,
}),
JwtModule.register({
global: true,
secret: 'fake-value',
signOptions: { expiresIn: '7200s' }, // set to 2 hours
})
],
providers: [
ValidatorService
],
controllers: [ValidatorController],
}).compile();
}).overrideProvider(CACHE_MANAGER).useValue(mockCacheManager)
.overrideProvider(HttpService).useValue(mockHttpService)
.compile();

controller = module.get<ValidatorController>(ValidatorController);
cacheManager = module.get<Cache>(CACHE_MANAGER);
httpService = module.get<HttpService>(HttpService);
sequelize = module.get<Sequelize>(Sequelize);

mockCacheManager.get.mockResolvedValue(null);
});

afterEach(async () => {
await sequelize.close();
});

it('should be defined', () => {
expect(controller).toBeDefined();
});

it('getValidatorAuth should return correct auth path', async () => {
const httpVcResponse: AxiosResponse = { data: { data: 'mock-auth-key' } } as AxiosResponse;
mockHttpService.request.mockReturnValueOnce(of(httpVcResponse));

const results = await controller.getValidatorAuth()

console.log(process.env.VALIDATOR_URL)

expect(results).toEqual({data: 'mock-auth-key'})
expect(mockHttpService.request).toBeCalledWith({method: "GET", url: "mock-url/lighthouse/auth"})
});

it('should call getValidatorVersion and return correct version', async () => {
const httpVcResponse: AxiosResponse = { data: { data: 'mock-version' } } as AxiosResponse;
mockHttpService.request.mockReturnValueOnce(of(httpVcResponse));

const results = await controller.getValidatorVersion()

expect(results).toEqual('mock-version')
expect(mockHttpService.request).toBeCalledWith({headers: {Authorization: "Bearer mock-api-token"}, method: "GET", url: "mock-url/lighthouse/version"})
});

describe('getValidatorStates', () => {
it('should fetch data from cache', async ()=> {
mockCacheManager.get.mockResolvedValueOnce({ SECONDS_PER_SLOT: '12' });
const mockCacheValue = { data: 'mock-state' };
mockCacheManager.get.mockResolvedValue(mockCacheValue);

const result = await controller.getValidatorStates();
expect(result).toEqual(mockCacheValue);
expect(mockCacheManager.get).toHaveBeenCalledWith('valStates');
});

it('should return correct data from node', async () => {
mockCacheManager.get.mockResolvedValueOnce({ SECONDS_PER_SLOT: '12' });
mockCacheManager.get.mockResolvedValueOnce(null);
mockCacheManager.get.mockResolvedValueOnce(mockValCacheValues);

const httpBeaconResponse: AxiosResponse = { data: { data: mockStateResults } } as AxiosResponse;
mockHttpService.request.mockReturnValueOnce(of(httpBeaconResponse));

const result = await controller.getValidatorStates();
expect(result).toEqual(mockFormattedStates);
});
})

describe('getValidatorCaches', () => {
it('should fetch data from cache', async () => {
mockCacheManager.get.mockResolvedValueOnce({ SECONDS_PER_SLOT: '12' });
const mockCacheValue = { data: 'mock-cache' };
mockCacheManager.get.mockResolvedValue(mockCacheValue);

const result = await controller.getValidatorCaches();
expect(result).toEqual(mockCacheValue);
expect(mockCacheManager.get).toHaveBeenCalledWith('valCache');
});

it('should return correct data from node', async () => {
mockCacheManager.get.mockResolvedValueOnce({ SECONDS_PER_SLOT: '12' });
mockCacheManager.get.mockResolvedValueOnce(null);
mockCacheManager.get.mockResolvedValueOnce(mockValCacheValues);

const httpBeaconResponse: AxiosResponse = { data: { data: mockValInfoResult } } as AxiosResponse;
mockHttpService.request.mockReturnValueOnce(of(httpBeaconResponse));

const result = await controller.getValidatorCaches();
expect(result).toEqual(mockValCacheResults);
});
})

describe('getValidatorMetrics', () => {
it('should fetch all metrics from db', async () => {
await Metric.bulkCreate([
{id: 1, index: '1', data: JSON.stringify({attestation_target_hit_percentage: 90, attestation_hit_percentage: 95})},
{id: 2, index: '2', data: JSON.stringify({attestation_target_hit_percentage: 90, attestation_hit_percentage: 95})},
{id: 3, index: '3', data: JSON.stringify({attestation_target_hit_percentage: 90, attestation_hit_percentage: 95})}
])

const results = await controller.getValidatorMetrics()
expect(results).toEqual({hitEffectiveness: 95, targetEffectiveness: 90, totalEffectiveness: 92.5})
});
it('should fetch metrics by id', async () => {
await Metric.bulkCreate([
{id: 1, index: '1', data: JSON.stringify({attestation_target_hit_percentage: 90, attestation_hit_percentage: 95})},
{id: 2, index: '1', data: JSON.stringify({attestation_target_hit_percentage: 100, attestation_hit_percentage: 100})},
{id: 3, index: '2', data: JSON.stringify({attestation_target_hit_percentage: 90, attestation_hit_percentage: 95})},
{id: 4, index: '3', data: JSON.stringify({attestation_target_hit_percentage: 90, attestation_hit_percentage: 95})}
])

const results = await controller.getValidatorMetricsById(1)
expect(results).toEqual({hitEffectiveness: 97.5, targetEffectiveness: 95, totalEffectiveness: 96.25})
});
})

it('should fetch val graffiti', async () => {
mockCacheManager.get.mockResolvedValueOnce(mockValCacheValues);

const httpBeaconResponse: AxiosResponse = { data: { data: {'fake-pub': 'mavrik'} } } as AxiosResponse;
mockHttpService.request.mockReturnValueOnce(of(httpBeaconResponse));

const result = await controller.fetchValidatorGraffiti('1')
expect(result).toEqual({data: 'mavrik'})
expect(mockHttpService.request).toBeCalledWith({headers: {Authorization: "Bearer mock-api-token"}, method: "GET", url: "mock-url/lighthouse/ui/graffiti"})
})
});
18 changes: 0 additions & 18 deletions backend/src/validator/tests/validator.service.spec.ts

This file was deleted.

1 change: 1 addition & 0 deletions backend/src/validator/validator.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export class ValidatorService {
try {
const options = index ? {where: {index}} : undefined
const metrics = await this.utilsService.fetchAll(Metric, options)
console.log(metrics)
const metricsData = metrics.map(metric => JSON.parse(metric.data))

const targetEffectiveness = getAverageKeyValue(metricsData, 'attestation_target_hit_percentage')
Expand Down
Loading

0 comments on commit a2d9662

Please sign in to comment.