From 3365a313ef06fb581f28b62535bc85206ea8ce6e Mon Sep 17 00:00:00 2001 From: Roman Vaivod Date: Sun, 26 Apr 2020 02:25:18 +0200 Subject: [PATCH] gh-133: Add strict as true --- backend/bot/bot-factory.ts | 4 +++- backend/match-reporter/MatchReporter.ts | 6 ++++-- backend/repositories/GameRepository.test.ts | 4 ++-- backend/repositories/GameRepository.ts | 4 ++-- backend/repositories/MatchRepository.test.ts | 2 +- backend/repositories/MatchRepository.ts | 2 +- backend/storage/Storage.ts | 7 +++++-- backend/storage/StorageContext.test.ts | 11 +++++++---- backend/storage/StorageContext.ts | 19 +++++++++++-------- backend/storage/db/db-transactions.ts | 4 ++-- backend/tests/TestData.ts | 3 ++- backend/tsconfig.json | 2 +- backend/types/Player.ts | 8 +++++++- 13 files changed, 48 insertions(+), 28 deletions(-) diff --git a/backend/bot/bot-factory.ts b/backend/bot/bot-factory.ts index 8323415e..c660ac51 100644 --- a/backend/bot/bot-factory.ts +++ b/backend/bot/bot-factory.ts @@ -17,10 +17,12 @@ export class SingleChannelBot { } } -export const makeBot = (botToken: string, channelName: string): Promise => { +export const makeBot = +(botToken: string|undefined, channelName: string|undefined): Promise => { return new Promise((resolve, reject): void => { if (!botToken || !channelName) { reject(Error('botToken or channelName missing')) + return } const slackbot = new SlackBot({ diff --git a/backend/match-reporter/MatchReporter.ts b/backend/match-reporter/MatchReporter.ts index 67c8aff4..755cfab5 100644 --- a/backend/match-reporter/MatchReporter.ts +++ b/backend/match-reporter/MatchReporter.ts @@ -5,7 +5,8 @@ import { Match } from '../types/Match' const DEFAULT_LEADERBOARD_SIZE = 5 -const parseMatchReportDecorations = (config: string): Array => config +const parseMatchReportDecorations = +(config: string | undefined): Array => config ? config.split(';').map(configPart => { const splitConfigPart = configPart.split(',') if (splitConfigPart.length !== 4) { @@ -111,13 +112,14 @@ export class MatchReporter { readonly decorations: MatchReportDecoration[] constructor( readonly bot: SingleChannelBot, - matchReportPrefixSuffixConfig: string, + matchReportPrefixSuffixConfig: string | undefined, readonly leaderboardSize = DEFAULT_LEADERBOARD_SIZE ) { try { this.decorations = parseMatchReportDecorations(matchReportPrefixSuffixConfig) } catch (e) { console.warn(`Parsing matchReportPrefixSuffixConfig failed: ${e.message}`) + this.decorations = [] } } diff --git a/backend/repositories/GameRepository.test.ts b/backend/repositories/GameRepository.test.ts index 98ca5d7d..95e0e562 100644 --- a/backend/repositories/GameRepository.test.ts +++ b/backend/repositories/GameRepository.test.ts @@ -1,5 +1,5 @@ import { mocked } from 'ts-jest/utils' -import { FOOSBALL_DATA } from '../tests/TestData' +import { FOOSBALL_DATA, FOOSBALL_GAME } from '../tests/TestData' import { insertGame } from '../storage/Storage' import { addGame } from './GameRepository' @@ -30,7 +30,7 @@ describe('GameRepository', () => { }) describe('when insertGame resolves', () => { beforeEach(() => { - mockedInsertGame.mockResolvedValue(undefined) + mockedInsertGame.mockResolvedValue(FOOSBALL_GAME) }) }) it('stores a valid foosball data via insertGame', async () => { diff --git a/backend/repositories/GameRepository.ts b/backend/repositories/GameRepository.ts index cda9cf01..bed2aca5 100644 --- a/backend/repositories/GameRepository.ts +++ b/backend/repositories/GameRepository.ts @@ -4,8 +4,8 @@ import * as storage from '../storage/Storage' const isValidGameData = (data: unknown): data is GameData => { const gameData = data as GameData - return gameData.name && - gameData.description && + return !!gameData.name && + !!gameData.description && gameData.name.trim() == gameData.name && gameData.name.toLowerCase() == gameData.name && !gameData.name.includes(' ') && diff --git a/backend/repositories/MatchRepository.test.ts b/backend/repositories/MatchRepository.test.ts index a00a5dde..25454965 100644 --- a/backend/repositories/MatchRepository.test.ts +++ b/backend/repositories/MatchRepository.test.ts @@ -41,7 +41,7 @@ describe('MatchRepository', () => { .mockResolvedValueOnce(RADEK_PLAYER) .mockResolvedValueOnce(PETR_PLAYER) mockedGetLatestMatchByGameId.mockResolvedValueOnce(FOOSBALL_MATCH_WITH_ID) - mockedStoreMatch.mockResolvedValueOnce(undefined) + mockedStoreMatch.mockResolvedValueOnce(FOOSBALL_MATCH_WITH_ID) }) describe('called one minute later', () => { lockDate(ONE_MINUTE_AFTER) diff --git a/backend/repositories/MatchRepository.ts b/backend/repositories/MatchRepository.ts index 9836f7d3..e9fe6aad 100644 --- a/backend/repositories/MatchRepository.ts +++ b/backend/repositories/MatchRepository.ts @@ -49,7 +49,7 @@ Promise => { ) } -const getElapsedSecondsSinceLatestMatch = async (gameId: number): Promise => { +const getElapsedSecondsSinceLatestMatch = async (gameId: number): Promise => { const latestMatch = await storage.getLatestMatchByGameId(gameId) if (latestMatch == null) { return null diff --git a/backend/storage/Storage.ts b/backend/storage/Storage.ts index b8c97e06..2eacefbe 100644 --- a/backend/storage/Storage.ts +++ b/backend/storage/Storage.ts @@ -7,6 +7,9 @@ import { Player } from '../types/Player' export const makeStorageContext = async (): Promise => { const transaction = await dbTransactions.beginTransaction() + if (!transaction) { + throw new Error('Failed to create transaction') + } return new StorageContext(transaction) } @@ -49,7 +52,7 @@ export const getAllMatches = async (): Promise> => { return executeAndCommit(context => context.getAllMatches()) } -export const getLatestMatchByGameId = async (gameId: number): Promise => { +export const getLatestMatchByGameId = async (gameId: number): Promise => { return executeAndCommit(context => context.getLatestMatchByGameId(gameId)) } @@ -61,7 +64,7 @@ export const getGameByName = async (name: string): Promise => { return executeAndCommit(context => context.getGameByName(name)) } -export const insertGame = async (game: GameData): Promise => { +export const insertGame = async (game: GameData): Promise => { return executeAndCommit(context => context.insertGame(game)) } diff --git a/backend/storage/StorageContext.test.ts b/backend/storage/StorageContext.test.ts index 45451f85..dc14bbaf 100644 --- a/backend/storage/StorageContext.test.ts +++ b/backend/storage/StorageContext.test.ts @@ -46,7 +46,7 @@ describe('StorageContext', () => { [ 'null', 'null', null, null ], [ 'foosball row', 'foosball game', FOOSBALL_ROW, FOOSBALL_GAME ], ])('when executeSingleResultQuery resolves with %s', (res1Desc, res2Desc, row, result) => { - let foosballGame: Game + let foosballGame: Game | null beforeEach(async () => { TRANSACTION_MOCK.executeSingleResultQuery.mockResolvedValueOnce(row) foosballGame = await context.insertGame(FOOSBALL_DATA) @@ -181,9 +181,12 @@ describe('StorageContext', () => { }) describe('when a new user Tonda is successfully added to foosball game', () => { beforeEach(async () => { + // select Tonda as a user TRANSACTION_MOCK.executeSingleResultQuery.mockResolvedValueOnce(null) + // insert Tonda as a user TRANSACTION_MOCK.executeSingleResultQuery.mockResolvedValueOnce(TONDA_USER_ROW) - TRANSACTION_MOCK.executeSingleResultQuery.mockResolvedValueOnce(undefined) + // insert Tonda as a player + TRANSACTION_MOCK.executeSingleResultQuery.mockResolvedValueOnce(TONDA_PLAYER_ROW) await context.addUserToGame(FOOSBALL_GAME.name, TONDA_PLAYER) }) it('searches game by name', () => { @@ -215,7 +218,7 @@ describe('StorageContext', () => { describe('when an existing player Tonda is successfully added to foosball game', () => { beforeEach(async () => { TRANSACTION_MOCK.executeSingleResultQuery.mockResolvedValueOnce(TONDA_USER_ROW) - TRANSACTION_MOCK.executeSingleResultQuery.mockResolvedValueOnce(undefined) + TRANSACTION_MOCK.executeSingleResultQuery.mockResolvedValueOnce(null) await context.addUserToGame(FOOSBALL_GAME.name, TONDA_PLAYER) }) it('does not insert Tonda as a user', () => { @@ -299,7 +302,7 @@ describe('StorageContext', () => { }) describe('getLatestMatchByGameId', () => { describe('called with foosball id', () => { - let matchWithId: MatchWithId + let matchWithId: MatchWithId | null beforeEach(async () => { TRANSACTION_MOCK.executeSingleResultQuery.mockResolvedValueOnce(FOOSBALL_MATCH_ROW) matchWithId = await context.getLatestMatchByGameId(FOOSBALL_GAME.id) diff --git a/backend/storage/StorageContext.ts b/backend/storage/StorageContext.ts index 9b5d154c..cc672e52 100644 --- a/backend/storage/StorageContext.ts +++ b/backend/storage/StorageContext.ts @@ -9,7 +9,6 @@ import { MatchWithId, Match } from '../types/Match' import { Game, GameData } from '../types/Game' import { Player, NULL_PLAYER, PlayerData } from '../types/Player' import { BadRequestError } from '../errors/BadRequestError' -import { QueryResultRow } from 'pg' export class StorageContext { constructor(private transaction: Transaction) {} @@ -81,7 +80,7 @@ export class StorageContext { return dbTransformations.createUserFromDbRow(row) } - async getUserByName(userName: string): Promise { + async getUserByName(userName: string): Promise { let row try { row = await this.transaction.executeSingleResultQuery(dbQueries.selectUserByName, [userName]) @@ -126,7 +125,7 @@ export class StorageContext { async addUserToGame(gameName: string, { name, initialRating }: UserData): Promise { const game = await this.getGameByName(gameName) if (!game) { - throw new Error(`Unable to find game '${game.name}'`) + throw new Error(`Unable to find game '${gameName}'`) } let user = await this.getUserByName(name) if (!user) { @@ -149,7 +148,9 @@ export class StorageContext { } throw new Error('Unable to add user') } - + if (!row) { + throw new Error(`No user was added with name '${name}'`) + } return dbTransformations.createUserFromDbRow(row) } @@ -194,7 +195,9 @@ export class StorageContext { } throw new Error('Unable to create match') } - + if (!row) { + throw new Error('No match was created') + } return dbTransformations.createMatchFromDbRow(row) } @@ -210,7 +213,7 @@ export class StorageContext { return rows.map(dbTransformations.createMatchFromDbRow) } - async getLatestMatchByGameId(gameId: number): Promise { + async getLatestMatchByGameId(gameId: number): Promise { let row try { row = await this.transaction.executeSingleResultQuery( @@ -233,7 +236,7 @@ export class StorageContext { return rows.map(dbTransformations.createGameFromDbRow) } - async insertGame(game: GameData): Promise { + async insertGame(game: GameData): Promise { let row try { row = await this.transaction.executeSingleResultQuery(dbQueries.insertGame, [ @@ -263,7 +266,7 @@ export class StorageContext { } async getGameByName(name: string): Promise { - let row: QueryResultRow + let row try { row = await this.transaction.executeSingleResultQuery(dbQueries.selectGameByName, [name]) } catch (error) { diff --git a/backend/storage/db/db-transactions.ts b/backend/storage/db/db-transactions.ts index a287aa78..f63d6149 100644 --- a/backend/storage/db/db-transactions.ts +++ b/backend/storage/db/db-transactions.ts @@ -22,7 +22,7 @@ export class Transaction { } async executeSingleResultQuery(query: string, values: QueryValues): - Promise { + Promise { const rows = await this.executeQuery(query, values) if (rows.length == 0) { return null @@ -52,7 +52,7 @@ export class Transaction { } } -export const beginTransaction = async (): Promise => { +export const beginTransaction = async (): Promise => { const client = await pool.connect() try { await client.query('BEGIN') diff --git a/backend/tests/TestData.ts b/backend/tests/TestData.ts index bddb686c..ecbd3e46 100644 --- a/backend/tests/TestData.ts +++ b/backend/tests/TestData.ts @@ -2,6 +2,7 @@ import { MatchWithId, Match } from '../types/Match' import { Player } from '../types/Player' import { MatchDescription } from '../types/MatchDescription' import * as moment from 'moment' +import { UserRow } from '../types/Database' const NOW_MOMENT = moment('2020-03-25 10:00:00') export const NOW = NOW_MOMENT.toDate() @@ -115,7 +116,7 @@ export const FOOSBALL_MATCH: Match = { gameId: 1, } -export const TONDA_USER_ROW = { +export const TONDA_USER_ROW: UserRow = { Id: '4', Name: 'Tonda', Active: 'true', diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 8b9d75b2..c28a6fc7 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -3,7 +3,7 @@ "module": "commonjs", "target": "es6", "sourceMap": true, - "noImplicitAny": true, + "strict": true, "typeRoots": ["./types", "./node_modules/@types"], }, "exclude": [ diff --git a/backend/types/Player.ts b/backend/types/Player.ts index 093b7154..710e96f1 100644 --- a/backend/types/Player.ts +++ b/backend/types/Player.ts @@ -20,4 +20,10 @@ export class Player extends User { } } -export const NULL_PLAYER = new Player(null, null, null, null, null) +export const NULL_PLAYER = { + id: null, + name: null, + rating: null, + active: null, + initialRating: null, +}