Nest can't resolve dependencies of the UserModel (?). Please make sure that the argument DatabaseConnection at index [0] is available in the

Viewed 45

I am using jest for unit tests. And I get this error when tests are run for auth.controller.spec.ts (src/auth/test/auth.controller.spec.ts).

app.module

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
import { AuthModule } from './auth/auth.module';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [
    ConfigModule.forRoot(),
    MongooseModule.forRootAsync({
      useFactory: async () => ({
        uri: process.env.DB_URI,
        useNewUrlParser: true,
        useUnifiedTopology: true,
      }),
    }),
    UsersModule,
    AuthModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule { }

users.module

import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { MongooseModule } from '@nestjs/mongoose';
import { User, UserSchema } from './schemas/user.schema';
import { UsersRepository } from './users.repository';

@Module({
  imports: [MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])],
  controllers: [UsersController],
  providers: [UsersService, UsersRepository],
  exports: [UsersService]
})
export class UsersModule { }

users.service

import { Injectable } from '@nestjs/common';
import { FilterQuery } from 'mongoose';
import { CreateUserDto } from './dtos/createUserDto.dto';
import { UpdateUserDto } from './dtos/updateUserDto.dto';
import { User } from './schemas/user.schema';
import { UsersRepository } from './users.repository';

@Injectable()
export class UsersService {
    constructor(private readonly UsersRepository: UsersRepository) { }

    async findOne(userFilterQuery: FilterQuery<User>): Promise<User> {
        return this.UsersRepository.findOne(userFilterQuery);
    }

    async createUser(createUserDto: CreateUserDto): Promise<User> {
        return this.UsersRepository.create(createUserDto);
    }

    async updateUser(userId: string, updateUserDto: UpdateUserDto): Promise<User> {
        return this.UsersRepository.findOneAndUpdate({ userId }, updateUserDto);
    }
}

auth.module

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { MongooseModule } from '@nestjs/mongoose';
import { UsersRepository } from '../users/users.repository';
import { User, UserSchema } from '../users/schemas/user.schema';
import { UsersModule } from '../users/users.module';
import { UsersService } from '../users/users.service';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';

@Module({
    imports: [
        UsersModule,
        JwtModule.registerAsync({
            useFactory: () => ({
                secret: process.env.SECRET,
                signOptions: { expiresIn: '3600s' }
            })
        }),
        MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])
    ],
    controllers: [AuthController],
    providers: [AuthService, UsersService, UsersRepository]
})
export class AuthModule { }

auth.service

import { Injectable } from '@nestjs/common';
import { HttpException } from '@nestjs/common/exceptions';
import { HttpStatus } from '@nestjs/common/enums';
import * as bcrypt from 'bcrypt';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
import { CreateUserDto } from '../users/dtos/createUserDto.dto';
import { User } from '../users/schemas/user.schema';
import { ExistingUserDto } from '../users/dtos/existingUserDto.dto';

@Injectable()
export class AuthService {
    constructor(
        private UserService: UsersService,
        private jwtService: JwtService,
    ) { }

    async hashPassword(password: string): Promise<string> {
        return bcrypt.hash(password, 12);
    }

    async register(createUser: Readonly<CreateUserDto>): Promise<User> {
        const newUser = { ...createUser };
        console.log(newUser);
        const userExists = await this.UserService.findOne({ email: newUser.email });
        console.log(userExists);
        if (userExists) {
            throw new HttpException(
                'An account with that email already exists!',
                HttpStatus.CONFLICT,
            );
        }

        const hashedPassword: string = await this.hashPassword(newUser.password);

        newUser.password = hashedPassword;

        return this.UserService.createUser(newUser);
    }

    async validateUser(user: ExistingUserDto): Promise<User | null> {
        const userExists: User = await this.UserService.findOne({ email: user.email })
        if (!userExists) return null;

        const passwordMatched: boolean = await this.doesPasswordMatch(user.password, userExists.password);

        if (!passwordMatched) return null;

        console.log(userExists)
        return userExists;
    }

    async doesPasswordMatch(password: string, hashedPassword: string): Promise<boolean> {
        return bcrypt.compare(password, hashedPassword);
    }

    async login(user: ExistingUserDto): Promise<{ token: string } | null> {
        const userIsValid = await this.validateUser(user);
        console.log(userIsValid);

        if (!userIsValid) throw new HttpException("Credentials Invalid", HttpStatus.UNAUTHORIZED);

        const jwt: string = await this.jwtService.signAsync(userIsValid);

        return { token: jwt };
    }
}

auth.controller.ts

import { Controller, Post, Body, Get } from '@nestjs/common';
import { CreateUserDto } from '../users/dtos/createUserDto.dto';
import { ExistingUserDto } from '../users/dtos/existingUserDto.dto';
import { User } from '../users/schemas/user.schema';
import { AuthService } from './auth.service';

@Controller('auth')
export class AuthController {
    constructor(private authServices: AuthService) { }

    @Get()
    getHello(): string {
        return process.env.DB_URI;
    }

    @Post('register')
    async register(@Body() user: CreateUserDto): Promise<User> {
        return this.authServices.register(user);
    }

    @Post('login')
    async login(@Body() user: ExistingUserDto): Promise<{ token: string } | null> {
        console.log(user);
        return this.authServices.login(user);
    }
}

auth.controller.spec.ts

import { Test, TestingModule } from '@nestjs/testing';
import { UsersModule } from '../../users/users.module';
import { User } from '../../users/schemas/user.schema';
import { UsersService } from '../../users/users.service';
import { AuthController } from '../auth.controller';
import { AuthService } from '../auth.service';
import { UserStub } from './stubs/user.stub';

jest.mock('../auth.service');
jest.mock('../../users/users.service');


describe('AuthController', () => {
  let authController: AuthController;
  let authService: AuthService;
  let usersService: UsersService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      imports: [UsersModule],
      controllers: [AuthController],
      providers: [AuthService, UsersService]
    }).compile();

    authController = module.get<AuthController>(AuthController);
    authService = module.get<AuthService>(AuthService);
    usersService = module.get<UsersService>(UsersService);
    jest.clearAllMocks();
  });

  describe('register', () => {
    describe('When register is called', () => {
      let user: Omit<User, "password">;

      beforeEach(async () => {
        user = await authController.register(UserStub())
      })

      test('then it should call authService.register', () => {
        expect(authService.register).toBeCalledWith(UserStub());
      })

      test('where usersService.createUser is called', () => {
        expect(usersService.createUser).toBeCalledWith(UserStub());
      })

      test('then it should return a user', () => {
        expect(user).toEqual(UserStub());
      })
    })
  })
});

auth/mocks/auth.service.ts

import { UserStub } from "../test/stubs/user.stub";

export const AuthService = jest.fn().mockReturnValue({
    hashPassword: jest.fn().mockResolvedValue(''),
    register: jest.fn().mockResolvedValue(UserStub()),
    validateUser: jest.fn().mockResolvedValue(UserStub() || null),
    login: jest.fn().mockResolvedValue({ token: '' }),
    doesPasswordMatch: jest.fn().mockResolvedValue(true)
})

users/mocks/users.service.ts

import { UserStub } from "../../auth/test/stubs/user.stub";

export const UsersService = jest.fn().mockReturnValue({
    findOne: jest.fn().mockResolvedValue(UserStub()),
    createUser: jest.fn().mockResolvedValue(UserStub()),
    updateUser: jest.fn().mockResolvedValue(UserStub())
})

Link to Github repo

Error

0 Answers
Related