Skip to content

Commit

Permalink
feat(game-servers): game server diagnostics (#942)
Browse files Browse the repository at this point in the history
  • Loading branch information
garrappachc authored Mar 24, 2021
1 parent 41d6901 commit 6cda591
Show file tree
Hide file tree
Showing 35 changed files with 834 additions and 219 deletions.
70 changes: 39 additions & 31 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,29 @@ on:
- 'master'

jobs:
unit-tests:
tests:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [12.x, 14.x]

services:
mongo:
image: mongo:4.0
ports:
- 27017:27017

gameserver:
image: tf2pickuppl/tf2-gameserver
ports:
- 27015:27015/tcp
- 27015:27015/udp
- 27020:27020/udp
env:
RCON_PASSWORD: 123456
options: --tty --add-host host.docker.internal:host-gateway

steps:
- name: Checkout repository
uses: actions/checkout@v2
Expand All @@ -31,40 +47,32 @@ jobs:
- name: Install dependencies
run: npm install

- name: Test
- name: Run unit test
run: npm run test:ci

- name: Build
run: npm run build

- name: Upload test coverage info
uses: codecov/codecov-action@v1

e2e-tests:
runs-on: ubuntu-latest
needs: [unit-tests]

strategy:
matrix:
node-version: [12.x, 14.x]

steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}

- name: Start services
run: docker-compose up -d

- name: Install dependencies
run: npm install

- name: Run tests
- name: Run e2e tests
env:
API_URL: 'http://localhost:3000'
CLIENT_URL: 'http://localhost:4200'
BOT_NAME: 'tf2pickup.pl'
MONGODB_HOST: 'localhost'
MONGODB_PORT: 27017
MONGODB_DB: 'tf2pickuppl_e2e_tests'
STEAM_API_KEY: 'FAKE_API_KEY'
KEY_STORE_PASSPHARE: 'a_password'
SUPER_USER: '76561198074409147'
QUEUE_CONFIG: '6v6'
MUMBLE_SERVER_URL: 'melkor.tf'
MUMBLE_CHANNEL_NAME: 'tf2pickup'
LOG_RELAY_ADDRESS: 'host.docker.internal'
LOG_RELAY_PORT: '9871'
run: npm run test:e2e

- name: Stop services
run: docker-compose down
- name: Upload test coverage info
uses: codecov/codecov-action@v1

- name: Build
run: npm run build
10 changes: 9 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@ services:
ports:
- '3100:3100'
environment:
- MONGOKU_DEFAULT_HOST=mongodb://mongo:27017
- 'MONGOKU_DEFAULT_HOST=mongodb://mongo:27017'

gameserver:
image: tf2pickuppl/tf2-gameserver:latest
environment:
- 'RCON_PASSWORD=123456'
network_mode: host
stdin_open: true
tty: true

volumes:
mongodb:
6 changes: 2 additions & 4 deletions e2e.jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ import { compilerOptions } from './tsconfig.json';

const config: Config.InitialOptions = {
...defaults,
collectCoverage: true,
coverageDirectory: 'coverage',
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' }),
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: [
'<rootDir>/e2e/**/*.e2e-spec.ts',
],
setupFiles: [
'<rootDir>/e2e/prepare-env.ts',
],
globalSetup: '<rootDir>/e2e/setup.ts',
globalTeardown: '<rootDir>/e2e/teardown.ts',
};
export default config;
11 changes: 1 addition & 10 deletions e2e/app.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,14 @@ import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '@/app.module';
import { MockLogReceiver } from './mock-log-receiver';
import { LogReceiver } from 'srcds-log-receiver';

describe('AppController (e2e)', () => {
let app: INestApplication;
let mockLogReceiver: MockLogReceiver;

beforeAll(() => {
mockLogReceiver = new MockLogReceiver({ address: 'localhost', port: 9871 });
});

beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [ AppModule ],
}).overrideProvider(LogReceiver)
.useValue(mockLogReceiver)
.compile();
}).compile();

app = moduleFixture.createNestApplication();
await app.init();
Expand Down
11 changes: 1 addition & 10 deletions e2e/auth.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,14 @@ import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '@/app.module';
import { LogReceiver } from 'srcds-log-receiver';
import { MockLogReceiver } from './mock-log-receiver';

describe('AuthController (e2e)', () => {
let app: INestApplication;
let mockLogReceiver: MockLogReceiver;

beforeAll(() => {
mockLogReceiver = new MockLogReceiver({ address: 'localhost', port: 9871 });
});

beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [ AppModule ],
}).overrideProvider(LogReceiver)
.useValue(mockLogReceiver)
.compile();
}).compile();

app = moduleFixture.createNestApplication();
await app.init();
Expand Down
18 changes: 0 additions & 18 deletions e2e/e2e.env

This file was deleted.

139 changes: 139 additions & 0 deletions e2e/game-server-diagnostics.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/* eslint-disable jest/expect-expect */
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '@/app.module';
import { PlayersService } from '@/players/services/players.service';
import { AuthService } from '@/auth/services/auth.service';
import { JwtTokenPurpose } from '@/auth/jwt-token-purpose';
import { GameServersService } from '@/game-servers/services/game-servers.service';
import { GameServerDiagnosticsService } from '@/game-servers/services/game-server-diagnostics.service';
import { DiagnosticRunStatus } from '@/game-servers/models/diagnostic-run-status';
import { getModelToken } from 'nestjs-typegoose';
import { Player } from '@/players/models/player';
import { ReturnModelType } from '@typegoose/typegoose';
import { GameServer } from '@/game-servers/models/game-server';

jest.mock('@/players/services/steam-api.service');
jest.setTimeout(10000);

describe('Game server diagnostics (e2e)', () => {
let app: INestApplication;
let authToken: string;
let workingGameServerId: string;
let faultyGameServerId: string;
let diagnosticsService: GameServerDiagnosticsService;

const waitForDiagnosticRunToComplete = async (runId: string) => new Promise<void>(resolve => {
const isDone = async () => {
const run = await diagnosticsService.getDiagnosticRunById(runId);
return [DiagnosticRunStatus.completed, DiagnosticRunStatus.failed].includes(run.status);
};

const i = setInterval(async () => {
if (await isDone()) {
clearInterval(i);
resolve();
}
}, 1000);
});

beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [ AppModule ],
}).compile();

app = moduleFixture.createNestApplication();
await app.init();

diagnosticsService = app.get(GameServerDiagnosticsService);
});

beforeAll(async () => {
const playersService = app.get(PlayersService);
const maly = await playersService.createPlayer((await import('./steam-profiles')).maly);

const authService = app.get(AuthService);
authToken = await authService.generateJwtToken(JwtTokenPurpose.auth, maly.id);

const gameServersService = app.get(GameServersService);
workingGameServerId = (await gameServersService.addGameServer({
name: 'working game server',
address: '127.0.0.1',
port: '27015',
rconPassword: '123456',
})).id;

faultyGameServerId = (await gameServersService.addGameServer({
name: 'faulty game server',
address: '127.0.0.1',
port: '30987',
rconPassword: '1234',
})).id;
});

afterAll(async () => {
const playerModel = app.get(getModelToken(Player.name)) as ReturnModelType<typeof Player>;
await playerModel.deleteMany({ });

const gameServerModel = app.get(getModelToken(GameServer.name)) as ReturnModelType<typeof GameServer>;
await gameServerModel.deleteMany({ });

await app.close();
});

describe('diagnostic run for a working game server', () => {
let diagnosticRunId: string;

beforeEach(async () => {
diagnosticRunId = await diagnosticsService.runDiagnostics(workingGameServerId);
});

afterEach(async () => {
await waitForDiagnosticRunToComplete(diagnosticRunId);
});

it('GET /game-server-diagnostics/:id', async () => {
await waitForDiagnosticRunToComplete(diagnosticRunId);

return request(app.getHttpServer())
.get(`/game-server-diagnostics/${diagnosticRunId}`)
.auth(authToken, { type: 'bearer' })
.expect(200)
.then(response => {
const body = response.body;
expect(body.id).toEqual(diagnosticRunId);
expect(body.gameServer).toEqual(workingGameServerId);
expect(body.status).toEqual('completed');
expect(body.checks.every(check => check.status === 'completed')).toBe(true);
});
});
});

describe('diagnostic run for a faulty game server', () => {
let diagnosticRunId: string;

beforeEach(async () => {
diagnosticRunId = await diagnosticsService.runDiagnostics(faultyGameServerId);
});

afterEach(async () => {
await waitForDiagnosticRunToComplete(diagnosticRunId);
});

it('GET /game-server-diagnostics/:id', async () => {
await waitForDiagnosticRunToComplete(diagnosticRunId);

return request(app.getHttpServer())
.get(`/game-server-diagnostics/${diagnosticRunId}`)
.auth(authToken, { type: 'bearer' })
.expect(200)
.then(response => {
const body = response.body;
expect(body.id).toEqual(diagnosticRunId);
expect(body.gameServer).toEqual(faultyGameServerId);
expect(body.status).toEqual('failed');
});
});
});
});
7 changes: 0 additions & 7 deletions e2e/mock-log-receiver.ts

This file was deleted.

Loading

0 comments on commit 6cda591

Please sign in to comment.