Skip to content

Commit

Permalink
test(profile, user, reply): add tests for use cases and services
Browse files Browse the repository at this point in the history
  • Loading branch information
havrydotdev committed Oct 19, 2023
1 parent 8b43582 commit 54f7e5e
Show file tree
Hide file tree
Showing 8 changed files with 323 additions and 43 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": [
"^.+\\.ts$": [
"ts-jest",
{
"isolatedModules": true
Expand Down
10 changes: 5 additions & 5 deletions src/controllers/app.update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import {
Start,
Update,
} from 'nestjs-telegraf';
import { Context } from 'telegraf';
import { REGISTER_WIZARD_ID } from 'src/core/constants';
import { Language } from 'src/core/enums/languages.enum';
import { ReplyUseCases } from 'src/use-cases/reply';
import { UserUseCases } from 'src/use-cases/user/user.use-case';
import { MessageContext } from 'src/types/telegraf';

@Update()
export class AppUpdate {
Expand All @@ -21,7 +21,7 @@ export class AppUpdate {
) {}

@Start()
async onStart(@Ctx() ctx: Context) {
async onStart(@Ctx() ctx: MessageContext) {
// if user does not exist in session, create it
if (!ctx.session.user) {
ctx.session.user = await this.userUseCases.create({
Expand All @@ -34,7 +34,7 @@ export class AppUpdate {
}

@Hears(/🇺🇦|🇬🇧|🇷🇺/)
async onUa(@Ctx() ctx: Context, @Message() msg: { text: string }) {
async onUa(@Ctx() ctx: MessageContext, @Message() msg: { text: string }) {
// convert ctx.message to Message.TextMessage so we can access text property
switch (msg.text) {
case '🇺🇦':
Expand All @@ -59,12 +59,12 @@ export class AppUpdate {
}

@Command('language')
async onLanguage(@Ctx() ctx: Context) {
async onLanguage(@Ctx() ctx: MessageContext) {
await this.replyUseCases.updateLanguage(ctx);
}

@Help()
async onHelp(@Ctx() ctx: Context) {
async onHelp(@Ctx() ctx: MessageContext) {
await this.replyUseCases.helpCommandMessage(ctx);
}
}
36 changes: 9 additions & 27 deletions src/controllers/register.wizard.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import { Ctx, Message, On, Wizard, WizardStep } from 'nestjs-telegraf';
import { Game, User } from 'src/core';
import { Game } from 'src/core';
import { REGISTER_WIZARD_ID } from 'src/core/constants';
import { Language } from 'src/core/enums';
import { WizardMessageContext, WizardContext } from 'src/types/telegraf';
import { GameUseCases } from 'src/use-cases/game';
import { ReplyUseCases } from 'src/use-cases/reply';
import { UserUseCases } from 'src/use-cases/user';
import { Context } from 'telegraf/typings';
import { Message as MessageType } from 'telegraf/typings/core/types/typegram';
import { WizardContext } from 'telegraf/typings/scenes';

@Wizard(REGISTER_WIZARD_ID)
export class RegisterWizard {
private games: Game[];

constructor(
private readonly userUseCases: UserUseCases,
private readonly replyUseCases: ReplyUseCases,
private readonly gameUseCases: GameUseCases,
) {
Expand All @@ -24,7 +20,7 @@ export class RegisterWizard {
}

@WizardStep(1)
async onEnter(@Ctx() ctx: Context & WizardContext) {
async onEnter(@Ctx() ctx: WizardMessageContext) {
ctx.wizard.next();

await this.replyUseCases.enterName(ctx);
Expand All @@ -33,7 +29,7 @@ export class RegisterWizard {
@On('text')
@WizardStep(2)
async onName(
@Ctx() ctx: Context & WizardContext,
@Ctx() ctx: WizardMessageContext,
@Message() msg: { text: string },
) {
ctx.wizard.state['name'] = msg.text;
Expand All @@ -46,7 +42,7 @@ export class RegisterWizard {
@On('text')
@WizardStep(3)
async onAge(
@Ctx() ctx: Context & WizardContext,
@Ctx() ctx: WizardMessageContext,
@Message() msg: { text: string },
) {
const age = parseInt(msg.text);
Expand All @@ -67,7 +63,7 @@ export class RegisterWizard {
@On('text')
@WizardStep(4)
async onLocation(
@Ctx() ctx: Context & WizardContext,
@Ctx() ctx: WizardMessageContext,
@Message() msg: { text: string },
) {
const location = msg.text;
Expand All @@ -82,7 +78,7 @@ export class RegisterWizard {
@On('text')
@WizardStep(5)
async onGame(
@Ctx() ctx: Context & WizardContext,
@Ctx() ctx: WizardMessageContext,
@Message() msg: { text: string },
) {
if (msg.text === '✅') {
Expand Down Expand Up @@ -119,7 +115,7 @@ export class RegisterWizard {
@WizardStep(6)
async onPhoto(
@Ctx()
ctx: Context & WizardContext,
ctx: WizardMessageContext,
@Message() msg: MessageType.PhotoMessage,
) {
if (msg.photo.length === 0) {
Expand All @@ -134,21 +130,7 @@ export class RegisterWizard {
@WizardStep(7)
async onDone(
@Ctx()
ctx: WizardContext & {
session: {
user?: User;
lang: Language;
};
} & {
wizard: {
state: {
name: string;
location: string;
age: number;
games: number[];
};
};
},
ctx: WizardContext,
) {
await ctx.scene.leave();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Test, TestingModule } from '@nestjs/testing';
import { TypeOrmProfileService } from '../typeorm-profile.service';
import { Profile } from 'src/core';
import { Repository } from 'typeorm';
import { MockDatabaseModule } from 'src/services/mock-database/mock-database.module';
import { TypeOrmModule, getRepositoryToken } from '@nestjs/typeorm';

describe('TypeOrmProfileService', () => {
let service: TypeOrmProfileService;
let repo: Repository<Profile>;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [MockDatabaseModule, TypeOrmModule.forFeature([Profile])],
providers: [TypeOrmProfileService],
}).compile();

service = module.get<TypeOrmProfileService>(TypeOrmProfileService);
repo = module.get<Repository<Profile>>(getRepositoryToken(Profile));
});

it('should return a profile for a given user ID', async () => {
const userId = 1;
const profile = new Profile();
jest.spyOn(repo, 'findOne').mockResolvedValue(profile);

const result = await service.findByUser(userId);

expect(result).toEqual(profile);
expect(repo.findOne).toHaveBeenCalledWith({
where: {
user: {
id: userId,
},
},
});
});

it('should create a profile', async () => {
const profile = new Profile();
jest.spyOn(repo, 'save').mockResolvedValue(profile);

const result = await service.createProfile(profile);

expect(result).toEqual(profile);
expect(repo.save).toHaveBeenCalledWith(profile);
});

it('should update a profile', async () => {
const profileId = 1;
const profile = new Profile();
jest.spyOn(repo, 'save').mockResolvedValue(profile);

const result = await service.updateProfile(profileId, profile);

expect(result).toEqual(profile);
expect(repo.save).toHaveBeenCalledWith({
id: profileId,
...profile,
});
});

it('should delete a profile', async () => {
const profileId = 1;
jest.spyOn(repo, 'delete').mockResolvedValue(undefined);

await service.deleteProfile(profileId);

expect(repo.delete).toHaveBeenCalledWith(profileId);
});
});
51 changes: 51 additions & 0 deletions src/frameworks/reply/telegraf/tests/telegraf-reply.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Test, TestingModule } from '@nestjs/testing';
import { TelegrafReplyService } from '../telegraf-reply.service';
import { I18nService } from 'nestjs-i18n';
import { I18nTranslations } from 'src/generated/i18n.generated';
import { Context } from 'telegraf/typings';
import { Extra } from 'src/core/types';
import { PathImpl2 } from '@nestjs/config';
import { Language } from 'src/core/enums';

describe('TelegrafReplyService', () => {
let service: TelegrafReplyService;
let i18n: I18nService<I18nTranslations>;
const ctx = {
session: {
lang: Language.UA,
},
reply: jest.fn(),
} as unknown as Context;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
TelegrafReplyService,
{
provide: I18nService,
useValue: {
t: jest.fn(),
},
},
],
}).compile();

service = module.get<TelegrafReplyService>(TelegrafReplyService);
i18n = module.get<I18nService<I18nTranslations>>(I18nService);
});

it('should reply with a translated message', async () => {
const msgCode: PathImpl2<I18nTranslations> = 'messages.help';
const message = 'This is a test message';
const extra: Extra = {};

jest.spyOn(i18n, 't').mockReturnValue(message);

await service.reply(ctx, msgCode, extra);

expect(i18n.t).toHaveBeenCalledWith(msgCode, {
lang: Language.UA,
});
expect(ctx.reply).toHaveBeenCalledWith(message, extra);
});
});
63 changes: 63 additions & 0 deletions src/frameworks/user/typeorm/tests/typeorm-user.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Test, TestingModule } from '@nestjs/testing';
import { TypeOrmUserService } from '../typeorm-user.service';
import { User } from 'src/core';
import { Repository } from 'typeorm';
import { MockDatabaseModule } from 'src/services/mock-database/mock-database.module';
import { TypeOrmModule, getRepositoryToken } from '@nestjs/typeorm';

describe('TypeOrmUserService', () => {
let service: TypeOrmUserService;
let repo: Repository<User>;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [MockDatabaseModule, TypeOrmModule.forFeature([User])],
providers: [TypeOrmUserService],
}).compile();

service = module.get<TypeOrmUserService>(TypeOrmUserService);
repo = module.get<Repository<User>>(getRepositoryToken(User));
});

it('should return a user for a given Telegram ID', async () => {
const tgId = 123;
const user = User.create();
jest.spyOn(repo, 'findOne').mockResolvedValue(user);

const result = await service.findByTgId(tgId);

expect(result).toEqual(user);
expect(repo.findOne).toHaveBeenCalledWith({
where: {
userId: tgId,
},
});
});

it('should create a user', async () => {
const user = User.create();
jest.spyOn(repo, 'save').mockResolvedValue(user);

const result = await service.create(user);

expect(result).toEqual(user);
expect(repo.save).toHaveBeenCalledWith(user);
});

it('should update a user', async () => {
const userId = 1;
const user = User.create();
jest
.spyOn(repo, 'update')
.mockImplementationOnce(() => Promise.resolve(undefined));
jest.spyOn(repo, 'findOneBy').mockResolvedValue(user);

const result = await service.update(userId, user);

expect(result).toEqual(user);
expect(repo.update).toHaveBeenCalledWith(userId, user);
expect(repo.findOneBy).toHaveBeenCalledWith({
id: user.id,
});
});
});
32 changes: 22 additions & 10 deletions src/types/telegraf.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,33 @@ import { WizardContext } from 'telegraf/typings/scenes';
import { Context } from 'telegraf';
import { Language } from 'src/core/enums';

declare module 'telegraf/typings' {
interface Context extends CustomSessionContext, CustomSceneContext {}
}
type MessageContext = Context & CustomSceneContext & CustomSessionContext;

declare module 'telegraf/typings/scenes' {
interface WizardContext extends CustomSessionContext {}
}
type WizardMessageContext = Context & WizardContext;

interface CustomSessionContext {
type WizardContext = WizardContext & {
session: {
user?: User;
lang: Language;
};
}
} & {
wizard: {
state: {
name?: string;
location?: string;
age?: number;
games?: number[];
};
};
};

type CustomSessionContext = {
session: {
user?: User;
lang: Language;
};
};

interface CustomSceneContext {
type CustomSceneContext = {
scene: SceneContextScene<SceneContext>;
}
};
Loading

0 comments on commit 54f7e5e

Please sign in to comment.