Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(game-servers): game server diagnostics #942

Merged
merged 29 commits into from
Mar 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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