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

Feature/14 create unit tests #74

Merged
merged 25 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
50cd040
Initialize unit test framework in project
itanex Jan 31, 2024
4eab1d6
Add common testing mocks file
itanex Jan 31, 2024
1c96a04
Create Unit Tests for `AboutCommand`
itanex Jan 31, 2024
c53ae64
Create Unit Tests for `CuddleCommand` and address bugs
itanex Jan 31, 2024
5e0712f
Create Unit Tests for `AccountAgeCommand`
itanex Jan 31, 2024
dfe0690
Create Unit Tests for `BrainCommain` and address bugs
itanex Jan 31, 2024
e2b5389
Create Unit Tests for `CountExhaustCommand` and clean up code
itanex Jan 31, 2024
0c4aaa4
Create Unit Tests for `DeathCommands`; refactor DB `DeathCounts` code
itanex Feb 2, 2024
53df9ed
Create Unit Tests for `DiceCommand` and clean up code
itanex Feb 2, 2024
1a1220a
Create Unit Tests for `DivideByZeroCommand`
itanex Feb 2, 2024
68f5c4f
Create Unit Tests for `DrinkCommmand`
itanex Feb 2, 2024
a349f07
Create Unit Tests for `FallCommand`
itanex Feb 2, 2024
f88e473
Create Unit Tests for `FollowAgeCommand`
itanex Feb 2, 2024
cf2fdff
Create Unit Tests for `WishListCommand`
itanex Feb 2, 2024
4731d1f
Create Unit Tests for `HelpCommand`
itanex Feb 2, 2024
90ce703
Create Unit Tests for `SocialsCommand`
itanex Feb 2, 2024
e8c3f54
Create Unit Tests for `ThrowCommand`
itanex Feb 2, 2024
ae187b3
Create Unit Tests for `HugCommand` and fix bugs
itanex Feb 2, 2024
2adb9c0
Create Unit Tests for `LastRaidCommand`; refactor DB `Raiders` code
itanex Feb 2, 2024
be1d928
Create Unit Tests `LastSubCommand`
itanex Feb 2, 2024
ad7340e
Create Unit Tests for `LurkCommands`
itanex Feb 6, 2024
bdc93ef
Create Unit Tests for `UpTimeCommand`
itanex Feb 6, 2024
5dd35ba
Create Unit Tests for `ShoutOutCommand`
itanex Feb 6, 2024
1fe9066
Create Unit Tests for `EightballCommand`
itanex Feb 7, 2024
554f31c
Code cleanup and normalization
itanex Feb 7, 2024
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
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
logs/
local-cache/
coverage/
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"env": {
"es2021": true,
"node": true,
"mocha": true
"jest": true
},
"extends": ["airbnb-base"],
"parser": "@typescript-eslint/parser",
Expand Down
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
node_modules/
logs/
.env
local-cache/*
data/*
local-cache/
data/
coverage/
20 changes: 20 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@
"console": "integratedTerminal",
"sourceMaps": true,
"internalConsoleOptions": "neverOpen"
},
{
"name": "Jest (debug) - Unit Test",
"type": "node",
"request": "launch",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/jest",
"args": [
"${fileBasenameNoExtension}",
"--runInBand",
"--watch",
"--coverage=false",
"--no-cache"
],
"cwd": "${workspaceRoot}",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"sourceMaps": true,
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
}
}
]
}
64 changes: 64 additions & 0 deletions bot/commands/DivideByZeroCommand.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// reflect-metadata should be imported
// before any interface or other imports
// also it should be imported only once
// so that a singleton is created.
import 'reflect-metadata';
import { ChatClient, ChatUser } from '@twurple/chat';
import { Container } from 'inversify';
import winston from 'winston';
import { mockChatClient, mockLogger } from '../../tests/common.mocks';
import InjectionTypes from '../../dependency-management/types';
import { ICommandHandler } from './iCommandHandler';
import { DivideByZeroCommand } from './DivideByZeroCommand';

describe(' Divide By Zero Command Tests', () => {
const channel = 'TestChannel';
const command = 'TestCommand';
const message = 'TestMessage';
const user = <ChatUser>{ displayName: 'TestUser' };

const container: Container = new Container();
let expectedChatClient: ChatClient;
let expectedLogger: winston.Logger;

beforeEach(() => {
jest.resetAllMocks();
container.unbindAll();
container
.bind<ChatClient>(ChatClient)
.toConstantValue(mockChatClient);

container
.bind<winston.Logger>(InjectionTypes.Logger)
.toConstantValue(mockLogger);

container
.bind<ICommandHandler>(InjectionTypes.CommandHandlers)
.to(DivideByZeroCommand);

expectedChatClient = container
.get(ChatClient);

expectedLogger = container
.get<winston.Logger>(InjectionTypes.Logger);
});

it('should say something in chat', async () => {
// Arrange
const subject = container
.getAll<ICommandHandler>(InjectionTypes.CommandHandlers)
.find(x => x.constructor.name === `${DivideByZeroCommand.name}`);

// Act
subject.handle(channel, command, user, message, []);

// Assert
expect(expectedChatClient.say)
.toHaveBeenCalledTimes(1);
expect(expectedChatClient.say)
.toHaveBeenCalledWith(channel, expect.anything());
expect(expectedLogger.info)
.toHaveBeenCalledWith(expect
.stringMatching(`(?=.*\\b${command}\\b)(?=.*\\b${channel}\\b)(?=.*\\b${user.displayName}\\b)(?=.*\\b${message}\\b)`));
});
});
43 changes: 0 additions & 43 deletions bot/commands/GiveAwayCommand.ts

This file was deleted.

61 changes: 61 additions & 0 deletions bot/commands/aboutCommand.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// reflect-metadata should be imported
// before any interface or other imports
// also it should be imported only once
// so that a singleton is created.
import 'reflect-metadata';
import { ChatClient, ChatUser } from '@twurple/chat';
import { Container } from 'inversify';
import winston from 'winston';
import { mockChatClient, mockLogger } from '../../tests/common.mocks';
import InjectionTypes from '../../dependency-management/types';
import { ICommandHandler } from './iCommandHandler';
import { AboutCommand } from './aboutCommand';

describe('About Command Tests', () => {
const channel = 'TestChannel';
const command = 'TestCommand';
const user = <ChatUser>{ displayName: 'TestUser' };
const message = 'TestMessage';

const container: Container = new Container();
let expectedChatClient: ChatClient;
let expectedLogger: winston.Logger;

beforeEach(() => {
jest.resetAllMocks();
container.unbindAll();
container
.bind<ChatClient>(ChatClient)
.toConstantValue(mockChatClient);

container
.bind<winston.Logger>(InjectionTypes.Logger)
.toConstantValue(mockLogger);

container
.bind<ICommandHandler>(InjectionTypes.CommandHandlers)
.to(AboutCommand);

expectedChatClient = container
.get(ChatClient);

expectedLogger = container
.get<winston.Logger>(InjectionTypes.Logger);
});

it('should say something in chat about bot', () => {
// arrange
const subject = container
.getAll<ICommandHandler>(InjectionTypes.CommandHandlers)
.find(x => x.constructor.name === `${AboutCommand.name}`);

// act
subject.handle(channel, command, user, message);

// assert
expect(expectedChatClient.say).toHaveBeenCalledWith(channel, expect.anything());
expect(expectedLogger.info)
.toHaveBeenCalledWith(expect
.stringMatching(`(?=.*\\b${command}\\b)(?=.*\\b${channel}\\b)(?=.*\\b${user.displayName}\\b)(?=.*\\b${message}\\b)`));
});
});
93 changes: 93 additions & 0 deletions bot/commands/accountAgeCommand.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// reflect-metadata should be imported
// before any interface or other imports
// also it should be imported only once
// so that a singleton is created.
import 'reflect-metadata';
import { ApiClient, HelixUser } from '@twurple/api';
import { ChatClient, ChatUser } from '@twurple/chat';
import { Container } from 'inversify';
import winston from 'winston';
import { mockChatClient, mockLogger } from '../../tests/common.mocks';
import InjectionTypes from '../../dependency-management/types';
import { ICommandHandler } from './iCommandHandler';
import { AccountAgeCommand } from './accountAgeCommand';
import Timespan, { getAgeReport } from '../utilities/timeSpan';

describe('Account Age Command Tests', () => {
const channel = 'TestChannel';
const command = 'TestCommand';
const message = 'TestMessage';

const container: Container = new Container();
let expectedChatClient: ChatClient;
let expectedLogger: winston.Logger;
let mockApiClient: ApiClient;

beforeEach(() => {
jest.resetAllMocks();
container.unbindAll();
container
.bind<ChatClient>(ChatClient)
.toConstantValue(mockChatClient);

container
.bind<winston.Logger>(InjectionTypes.Logger)
.toConstantValue(mockLogger);

container
.bind<ICommandHandler>(InjectionTypes.CommandHandlers)
.to(AccountAgeCommand);

expectedChatClient = container
.get(ChatClient);

expectedLogger = container
.get<winston.Logger>(InjectionTypes.Logger);
});

describe('should report account age of target account', () => {
it.each([
[
<ChatUser>{ displayName: 'TestUser', userName: 'TestUser' },
[],
<HelixUser>{ displayName: 'TestUser', creationDate: new Date(2000, 0, 1) },
], [
<ChatUser>{ displayName: 'TestUser', userName: 'TestUser' },
['', 'UserName'],
<HelixUser>{ displayName: 'UserName', creationDate: new Date(2000, 0, 1) },
],
])(`user: '%s', arguments: '%s', reported user '%s'`, async (chatUser: ChatUser, args: string[], apiUser: HelixUser) => {
// Arrange
mockApiClient = <unknown>{
users: {
getUserByName: jest.fn().mockResolvedValue(apiUser),
},
} as ApiClient;

container
.bind<ApiClient>(ApiClient)
.toConstantValue(mockApiClient);

const subject = container
.getAll<ICommandHandler>(InjectionTypes.CommandHandlers)
.find(x => x.constructor.name === AccountAgeCommand.name);

const age = getAgeReport(Timespan.fromNow(apiUser.creationDate));

// Act
await subject.handle(channel, command, chatUser, message, args);

// Assert
expect(mockApiClient.users.getUserByName)
.toHaveBeenCalledWith(args[1]
? args[1].toLocaleLowerCase().trim()
: chatUser.userName);
expect(expectedChatClient.say)
.toHaveBeenCalledWith(channel, expect
.stringMatching(`(?=.*\\b${apiUser.displayName}\\b)(?=.*\\b${age}\\b)`));
expect(expectedLogger.info)
.toHaveBeenCalledWith(expect
.stringMatching(`(?=.*\\b${command}\\b)(?=.*\\b${channel}\\b)(?=.*\\b${chatUser.displayName}\\b)(?=.*\\b${message}\\b)`));
});
});
});
75 changes: 75 additions & 0 deletions bot/commands/brainCommand.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// reflect-metadata should be imported
// before any interface or other imports
// also it should be imported only once
// so that a singleton is created.
import 'reflect-metadata';
import { ChatClient, ChatUser } from '@twurple/chat';
import { Container } from 'inversify';
import winston from 'winston';
import { mockChatClient, mockLogger } from '../../tests/common.mocks';
import InjectionTypes from '../../dependency-management/types';
import { ICommandHandler } from './iCommandHandler';
import BrainCommand from './brainCommand';

describe('Brain Command Tests', () => {
const channel = 'TestChannel';
const command = 'TestCommand';
const message = 'TestMessage';

const container: Container = new Container();
let expectedChatClient: ChatClient;
let expectedLogger: winston.Logger;

beforeEach(() => {
jest.resetAllMocks();
container.unbindAll();
container
.bind<ChatClient>(ChatClient)
.toConstantValue(mockChatClient);

container
.bind<winston.Logger>(InjectionTypes.Logger)
.toConstantValue(mockLogger);

container
.bind<ICommandHandler>(InjectionTypes.CommandHandlers)
.to(BrainCommand);

expectedChatClient = container
.get(ChatClient);

expectedLogger = container
.get<winston.Logger>(InjectionTypes.Logger);
});

describe('should report brain about target', () => {
it.each([
[
<ChatUser>{ displayName: 'TestUser', userName: 'TestUser' },
[],
], [
<ChatUser>{ displayName: 'TestUser', userName: 'TestUser' },
['UserName'],
],
])(`user: '%s', target args: '%s'`, async (chatUser: ChatUser, args: string[]) => {
// Arrange
const subject = container
.getAll<ICommandHandler>(InjectionTypes.CommandHandlers)
.find(x => x.constructor.name === `${BrainCommand.name}`);

// Act
const user = args[0]
? args[0].toLocaleLowerCase().trim()
: chatUser.displayName;
await subject.handle(channel, command, chatUser, message, args);

// Assert
expect(expectedChatClient.say)
.toHaveBeenCalledWith(channel, expect
.stringMatching(`(?=.*(\\b${user}\\b))(?=.*(\\b[0-9]{1,3}[%\\b]{1}))`));
expect(expectedLogger.info)
.toHaveBeenCalledWith(expect
.stringMatching(`(?=.*\\b${command}\\b)(?=.*\\b${channel}\\b)(?=.*\\b${chatUser.displayName}\\b)(?=.*\\b${message}\\b)`));
});
});
});
1 change: 0 additions & 1 deletion bot/commands/brainCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export default class BrainCommand implements ICommandHandler {

constructor(
@inject(ChatClient) private chatClient: ChatClient,
@inject(ApiClient) private apiClient: ApiClient,
@inject(InjectionTypes.Logger) private logger: winston.Logger,
) {
}
Expand Down
Loading
Loading