mirror of
https://github.com/apricote/Listory.git
synced 2026-01-13 13:11:02 +00:00
test: create initial unit tests
This commit is contained in:
parent
be7fb7d13a
commit
e476243b85
16 changed files with 1177 additions and 18 deletions
|
|
@ -98,6 +98,7 @@
|
||||||
"^.+\\.(t|j)s$": "ts-jest"
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
},
|
},
|
||||||
"coverageDirectory": "./coverage",
|
"coverageDirectory": "./coverage",
|
||||||
|
"collectCoverageFrom": ["src/**/*.ts"],
|
||||||
"testEnvironment": "node",
|
"testEnvironment": "node",
|
||||||
"roots": [
|
"roots": [
|
||||||
"<rootDir>/src/"
|
"<rootDir>/src/"
|
||||||
|
|
|
||||||
99
src/auth/auth.controller.spec.ts
Normal file
99
src/auth/auth.controller.spec.ts
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
|
import type { Response } from "express";
|
||||||
|
import { Logger } from "../logger/logger.service";
|
||||||
|
import { User } from "../users/user.entity";
|
||||||
|
import { AuthSession } from "./auth-session.entity";
|
||||||
|
import { AuthController } from "./auth.controller";
|
||||||
|
import { AuthService } from "./auth.service";
|
||||||
|
import { COOKIE_REFRESH_TOKEN } from "./constants";
|
||||||
|
|
||||||
|
describe("AuthController", () => {
|
||||||
|
let controller: AuthController;
|
||||||
|
let authService: AuthService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [AuthController],
|
||||||
|
providers: [
|
||||||
|
{ provide: AuthService, useFactory: () => ({}) },
|
||||||
|
{ provide: Logger, useClass: Logger },
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
controller = module.get<AuthController>(AuthController);
|
||||||
|
authService = module.get<AuthService>(AuthService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be defined", () => {
|
||||||
|
expect(controller).toBeDefined();
|
||||||
|
expect(authService).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("spotifyCallback", () => {
|
||||||
|
let user: User;
|
||||||
|
let res: Response;
|
||||||
|
let refreshToken: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
user = { id: "user" } as User;
|
||||||
|
res = {
|
||||||
|
statusCode: 200,
|
||||||
|
cookie: jest.fn(),
|
||||||
|
redirect: jest.fn(),
|
||||||
|
} as unknown as Response;
|
||||||
|
|
||||||
|
refreshToken = "REFRESH_TOKEN";
|
||||||
|
authService.createSession = jest.fn().mockResolvedValue({ refreshToken });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates session for user", async () => {
|
||||||
|
await controller.spotifyCallback(user, res);
|
||||||
|
|
||||||
|
expect(authService.createSession).toHaveBeenCalledTimes(1);
|
||||||
|
expect(authService.createSession).toHaveBeenCalledWith(user);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets refresh token as cookie", async () => {
|
||||||
|
await controller.spotifyCallback(user, res);
|
||||||
|
|
||||||
|
expect(res.cookie).toHaveBeenCalledTimes(1);
|
||||||
|
expect(res.cookie).toHaveBeenCalledWith(
|
||||||
|
COOKIE_REFRESH_TOKEN,
|
||||||
|
refreshToken,
|
||||||
|
{ httpOnly: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("redirects to login success page", async () => {
|
||||||
|
await controller.spotifyCallback(user, res);
|
||||||
|
|
||||||
|
expect(res.redirect).toHaveBeenCalledTimes(1);
|
||||||
|
expect(res.redirect).toHaveBeenCalledWith(
|
||||||
|
"/login/success?source=spotify"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("refreshAccessToken", () => {
|
||||||
|
let session: AuthSession;
|
||||||
|
let accessToken: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
session = { id: "session" } as AuthSession;
|
||||||
|
accessToken = "ACCESS_TOKEN";
|
||||||
|
|
||||||
|
authService.createAccessToken = jest
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValue({ accessToken });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns new access token", async () => {
|
||||||
|
await expect(controller.refreshAccessToken(session)).resolves.toEqual({
|
||||||
|
accessToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(authService.createAccessToken).toHaveBeenCalledTimes(1);
|
||||||
|
expect(authService.createAccessToken).toHaveBeenCalledWith(session);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -6,7 +6,6 @@ import {
|
||||||
UseFilters,
|
UseFilters,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { ConfigService } from "@nestjs/config";
|
|
||||||
import type { Response } from "express";
|
import type { Response } from "express";
|
||||||
import { User } from "../users/user.entity";
|
import { User } from "../users/user.entity";
|
||||||
import { AuthSession } from "./auth-session.entity";
|
import { AuthSession } from "./auth-session.entity";
|
||||||
|
|
@ -22,10 +21,7 @@ import { SpotifyAuthFilter } from "./spotify.filter";
|
||||||
|
|
||||||
@Controller("api/v1/auth")
|
@Controller("api/v1/auth")
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
constructor(
|
constructor(private readonly authService: AuthService) {}
|
||||||
private readonly authService: AuthService,
|
|
||||||
private readonly config: ConfigService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@Get("spotify")
|
@Get("spotify")
|
||||||
@UseGuards(SpotifyAuthGuard)
|
@UseGuards(SpotifyAuthGuard)
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,319 @@
|
||||||
|
import { ForbiddenException } from "@nestjs/common";
|
||||||
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
import { JwtService } from "@nestjs/jwt";
|
||||||
import { Test, TestingModule } from "@nestjs/testing";
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
|
import { User } from "../users/user.entity";
|
||||||
|
import { UsersService } from "../users/users.service";
|
||||||
|
import { AuthSession } from "./auth-session.entity";
|
||||||
|
import { AuthSessionRepository } from "./auth-session.repository";
|
||||||
import { AuthService } from "./auth.service";
|
import { AuthService } from "./auth.service";
|
||||||
|
import { LoginDto } from "./dto/login.dto";
|
||||||
|
|
||||||
describe("AuthService", () => {
|
describe("AuthService", () => {
|
||||||
let service: AuthService;
|
let service: AuthService;
|
||||||
|
let config: ConfigService;
|
||||||
|
let usersService: UsersService;
|
||||||
|
let jwtService: JwtService;
|
||||||
|
let authSessionRepository: AuthSessionRepository;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [AuthService],
|
providers: [
|
||||||
|
AuthService,
|
||||||
|
{
|
||||||
|
provide: ConfigService,
|
||||||
|
useFactory: () => ({ get: jest.fn().mockReturnValue("FOOBAR") }),
|
||||||
|
},
|
||||||
|
{ provide: UsersService, useFactory: () => ({}) },
|
||||||
|
{ provide: JwtService, useFactory: () => ({}) },
|
||||||
|
{ provide: AuthSessionRepository, useFactory: () => ({}) },
|
||||||
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<AuthService>(AuthService);
|
service = module.get<AuthService>(AuthService);
|
||||||
|
config = module.get<ConfigService>(ConfigService);
|
||||||
|
usersService = module.get<UsersService>(UsersService);
|
||||||
|
jwtService = module.get<JwtService>(JwtService);
|
||||||
|
authSessionRepository = module.get<AuthSessionRepository>(
|
||||||
|
AuthSessionRepository
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be defined", () => {
|
it("should be defined", () => {
|
||||||
expect(service).toBeDefined();
|
expect(service).toBeDefined();
|
||||||
|
expect(config).toBeDefined();
|
||||||
|
expect(usersService).toBeDefined();
|
||||||
|
expect(jwtService).toBeDefined();
|
||||||
|
expect(authSessionRepository).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("spotifyLogin", () => {
|
||||||
|
let loginDto: LoginDto;
|
||||||
|
let user: User;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
loginDto = {
|
||||||
|
accessToken: "ACCESS_TOKEN",
|
||||||
|
refreshToken: "REFRESH_TOKEN",
|
||||||
|
profile: {
|
||||||
|
id: "FOOBAR",
|
||||||
|
displayName: "Max Mustermann",
|
||||||
|
photos: ["https://example.com/profile.jpeg"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
user = {
|
||||||
|
id: "USER",
|
||||||
|
} as User;
|
||||||
|
|
||||||
|
service.allowedByUserFilter = jest.fn().mockReturnValue(true);
|
||||||
|
usersService.createOrUpdate = jest.fn().mockResolvedValue(user);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the logged in user", async () => {
|
||||||
|
await expect(service.spotifyLogin(loginDto)).resolves.toEqual(user);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("verifies that the user is allowed by the user filter", async () => {
|
||||||
|
await service.spotifyLogin(loginDto);
|
||||||
|
|
||||||
|
expect(service.allowedByUserFilter).toHaveBeenCalledTimes(1);
|
||||||
|
expect(service.allowedByUserFilter).toHaveBeenCalledWith(
|
||||||
|
loginDto.profile.id
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws ForbiddenException is user is not in the filter", async () => {
|
||||||
|
service.allowedByUserFilter = jest.fn().mockReturnValue(false);
|
||||||
|
|
||||||
|
await expect(service.spotifyLogin(loginDto)).rejects.toThrow(
|
||||||
|
ForbiddenException
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates the user profile with data from spotify", async () => {
|
||||||
|
await service.spotifyLogin(loginDto);
|
||||||
|
|
||||||
|
expect(usersService.createOrUpdate).toHaveBeenCalledTimes(1);
|
||||||
|
expect(usersService.createOrUpdate).toHaveBeenLastCalledWith({
|
||||||
|
displayName: "Max Mustermann",
|
||||||
|
photo: "https://example.com/profile.jpeg",
|
||||||
|
spotify: {
|
||||||
|
id: "FOOBAR",
|
||||||
|
accessToken: "ACCESS_TOKEN",
|
||||||
|
refreshToken: "REFRESH_TOKEN",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("createSession", () => {
|
||||||
|
let user: User;
|
||||||
|
let session: AuthSession;
|
||||||
|
let refreshToken: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
user = { id: "USER" } as User;
|
||||||
|
session = {
|
||||||
|
id: "SESSION",
|
||||||
|
user,
|
||||||
|
} as AuthSession;
|
||||||
|
refreshToken = "REFRESH_TOKEN";
|
||||||
|
|
||||||
|
authSessionRepository.create = jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue({ id: "SESSION" });
|
||||||
|
|
||||||
|
authSessionRepository.save = jest.fn().mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
service.createRefreshToken = jest
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValue({ refreshToken });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the session and refresh token", async () => {
|
||||||
|
await expect(service.createSession(user)).resolves.toEqual({
|
||||||
|
session,
|
||||||
|
refreshToken,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates a new session", async () => {
|
||||||
|
await service.createSession(user);
|
||||||
|
|
||||||
|
expect(authSessionRepository.create).toHaveBeenCalledTimes(1);
|
||||||
|
expect(authSessionRepository.save).toHaveBeenCalledTimes(1);
|
||||||
|
expect(authSessionRepository.save).toHaveBeenCalledWith(session);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("it creates a refresh token", async () => {
|
||||||
|
await service.createSession(user);
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
expect(service.createRefreshToken).toHaveBeenCalledTimes(1);
|
||||||
|
// @ts-expect-error
|
||||||
|
expect(service.createRefreshToken).toHaveBeenCalledWith(session);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("createRefreshToken", () => {
|
||||||
|
let session: AuthSession;
|
||||||
|
let refreshToken: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
session = {
|
||||||
|
user: { id: "USER", displayName: "Max Mustermann" } as User,
|
||||||
|
id: "SESSION",
|
||||||
|
} as AuthSession;
|
||||||
|
|
||||||
|
refreshToken = "REFRESH_TOKEN";
|
||||||
|
|
||||||
|
jwtService.signAsync = jest.fn().mockResolvedValue(refreshToken);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the refreshToken", async () => {
|
||||||
|
// @ts-expect-error
|
||||||
|
await expect(service.createRefreshToken(session)).resolves.toEqual({
|
||||||
|
refreshToken,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates a token with correct values", async () => {
|
||||||
|
// @ts-expect-error
|
||||||
|
service.sessionExpirationTime = "EXPIRATION_TIME";
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
await service.createRefreshToken(session);
|
||||||
|
|
||||||
|
expect(jwtService.signAsync).toHaveBeenCalledTimes(1);
|
||||||
|
expect(jwtService.signAsync).toHaveBeenCalledWith(
|
||||||
|
{ sub: "USER", name: "Max Mustermann" },
|
||||||
|
{
|
||||||
|
jwtid: session.id,
|
||||||
|
expiresIn: "EXPIRATION_TIME",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("createAccessToken", () => {
|
||||||
|
let session: AuthSession;
|
||||||
|
let accessToken: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
session = {
|
||||||
|
id: "AUTH_SESSION",
|
||||||
|
user: {
|
||||||
|
id: "USER",
|
||||||
|
displayName: "Max Mustermann",
|
||||||
|
photo: "https://example.com/picture.jpg",
|
||||||
|
},
|
||||||
|
} as AuthSession;
|
||||||
|
|
||||||
|
accessToken = "ACCESS_TOKEN";
|
||||||
|
|
||||||
|
jwtService.signAsync = jest.fn().mockResolvedValue(accessToken);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the access token", async () => {
|
||||||
|
await expect(service.createAccessToken(session)).resolves.toEqual({
|
||||||
|
accessToken,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws ForbiddenException if the session is revoked", async () => {
|
||||||
|
session.revokedAt = new Date("2020-01-01T00:00:00Z");
|
||||||
|
|
||||||
|
await expect(service.createAccessToken(session)).rejects.toThrow(
|
||||||
|
ForbiddenException
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates a token with correct values", async () => {
|
||||||
|
await service.createAccessToken(session);
|
||||||
|
|
||||||
|
expect(jwtService.signAsync).toHaveBeenCalledTimes(1);
|
||||||
|
expect(jwtService.signAsync).toHaveBeenLastCalledWith({
|
||||||
|
sub: "USER",
|
||||||
|
name: "Max Mustermann",
|
||||||
|
picture: "https://example.com/picture.jpg",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("findSession", () => {
|
||||||
|
let session: AuthSession;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
session = { id: "AUTH_SESSION" } as AuthSession;
|
||||||
|
|
||||||
|
authSessionRepository.findOne = jest.fn().mockResolvedValue(session);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the session", async () => {
|
||||||
|
await expect(service.findSession("AUTH_SESSION")).resolves.toEqual(
|
||||||
|
session
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(authSessionRepository.findOne).toHaveBeenCalledTimes(1);
|
||||||
|
expect(authSessionRepository.findOne).toHaveBeenLastCalledWith(
|
||||||
|
"AUTH_SESSION"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("findUser", () => {
|
||||||
|
let user: User;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
user = { id: "USER" } as User;
|
||||||
|
|
||||||
|
usersService.findById = jest.fn().mockResolvedValue(user);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the user", async () => {
|
||||||
|
await expect(service.findUser("USER")).resolves.toEqual(user);
|
||||||
|
|
||||||
|
expect(usersService.findById).toHaveBeenCalledTimes(1);
|
||||||
|
expect(usersService.findById).toHaveBeenLastCalledWith("USER");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("allowedByUserFilter", () => {
|
||||||
|
it("returns true if user filter is unset", () => {
|
||||||
|
// @ts-expect-error
|
||||||
|
service.userFilter = null;
|
||||||
|
|
||||||
|
expect(service.allowedByUserFilter("123")).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns true if the user filter is an empty string", () => {
|
||||||
|
// @ts-expect-error
|
||||||
|
service.userFilter = "";
|
||||||
|
|
||||||
|
expect(service.allowedByUserFilter("123")).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns true if the user is the only user in filter", () => {
|
||||||
|
// @ts-expect-error
|
||||||
|
service.userFilter = "123";
|
||||||
|
|
||||||
|
expect(service.allowedByUserFilter("123")).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns true if the user is one of the users in filter", () => {
|
||||||
|
// @ts-expect-error
|
||||||
|
service.userFilter = "123,456";
|
||||||
|
|
||||||
|
expect(service.allowedByUserFilter("456")).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns false if the user is not in the filter", () => {
|
||||||
|
// @ts-expect-error
|
||||||
|
service.userFilter = "123,456";
|
||||||
|
|
||||||
|
expect(service.allowedByUserFilter("789")).toEqual(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
69
src/auth/strategies/refresh-token.strategy.spec.ts
Normal file
69
src/auth/strategies/refresh-token.strategy.spec.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { ForbiddenException, UnauthorizedException } from "@nestjs/common";
|
||||||
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
|
import { AuthService } from "../auth.service";
|
||||||
|
import { RefreshTokenStrategy } from "./refresh-token.strategy";
|
||||||
|
|
||||||
|
describe("RefreshTokenStrategy", () => {
|
||||||
|
let strategy: RefreshTokenStrategy;
|
||||||
|
let authService: AuthService;
|
||||||
|
let configService: ConfigService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
RefreshTokenStrategy,
|
||||||
|
{ provide: AuthService, useFactory: () => ({}) },
|
||||||
|
{
|
||||||
|
provide: ConfigService,
|
||||||
|
useFactory: () => ({ get: jest.fn().mockReturnValue("foobar") }),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
strategy = module.get<RefreshTokenStrategy>(RefreshTokenStrategy);
|
||||||
|
authService = module.get<AuthService>(AuthService);
|
||||||
|
configService = module.get<ConfigService>(ConfigService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be defined", () => {
|
||||||
|
expect(strategy).toBeDefined();
|
||||||
|
expect(authService).toBeDefined();
|
||||||
|
expect(configService).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("validate", () => {
|
||||||
|
let session;
|
||||||
|
let payload;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
payload = { jti: "123-foo-bar" };
|
||||||
|
|
||||||
|
session = { mock: "Session" };
|
||||||
|
authService.findSession = jest.fn().mockResolvedValue(session);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("return session from authService", async () => {
|
||||||
|
await expect(strategy.validate(payload)).resolves.toEqual(session);
|
||||||
|
|
||||||
|
expect(authService.findSession).toHaveBeenCalledTimes(1);
|
||||||
|
expect(authService.findSession).toHaveBeenCalledWith(payload.jti);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws UnauthorizedException if session does not exist", async () => {
|
||||||
|
authService.findSession = jest.fn().mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
await expect(strategy.validate(payload)).rejects.toThrow(
|
||||||
|
UnauthorizedException
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws ForbiddenException is session is revoked", async () => {
|
||||||
|
session.revokedAt = "2021-01-01";
|
||||||
|
|
||||||
|
await expect(strategy.validate(payload)).rejects.toThrow(
|
||||||
|
ForbiddenException
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,18 +1,64 @@
|
||||||
import { Test, TestingModule } from "@nestjs/testing";
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
|
import { Pagination } from "nestjs-typeorm-paginate";
|
||||||
|
import { User } from "../users/user.entity";
|
||||||
|
import { GetListensFilterDto } from "./dto/get-listens.dto";
|
||||||
|
import { Listen } from "./listen.entity";
|
||||||
import { ListensController } from "./listens.controller";
|
import { ListensController } from "./listens.controller";
|
||||||
|
import { ListensService } from "./listens.service";
|
||||||
|
|
||||||
describe("Listens Controller", () => {
|
describe("Listens Controller", () => {
|
||||||
let controller: ListensController;
|
let controller: ListensController;
|
||||||
|
let listensService: ListensService;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
controllers: [ListensController],
|
controllers: [ListensController],
|
||||||
|
providers: [{ provide: ListensService, useFactory: () => ({}) }],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
controller = module.get<ListensController>(ListensController);
|
controller = module.get<ListensController>(ListensController);
|
||||||
|
listensService = module.get<ListensService>(ListensService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be defined", () => {
|
it("should be defined", () => {
|
||||||
expect(controller).toBeDefined();
|
expect(controller).toBeDefined();
|
||||||
|
expect(listensService).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getRecentlyPlayed", () => {
|
||||||
|
let filter: GetListensFilterDto;
|
||||||
|
let user: User;
|
||||||
|
let listens: Pagination<Listen>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
filter = { time: { start: new Date(), end: new Date() } };
|
||||||
|
user = { id: "USER" } as User;
|
||||||
|
|
||||||
|
listens = { items: [{ id: "LISTEN" } as Listen] } as Pagination<Listen>;
|
||||||
|
|
||||||
|
listensService.getListens = jest.fn().mockResolvedValue(listens);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the listens", async () => {
|
||||||
|
await expect(
|
||||||
|
controller.getRecentlyPlayed(1, 10, filter, user)
|
||||||
|
).resolves.toEqual(listens);
|
||||||
|
|
||||||
|
expect(listensService.getListens).toHaveBeenCalledTimes(1);
|
||||||
|
expect(listensService.getListens).toHaveBeenCalledWith({
|
||||||
|
page: 1,
|
||||||
|
limit: 10,
|
||||||
|
user,
|
||||||
|
filter,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("clamps the limit to 100", async () => {
|
||||||
|
await controller.getRecentlyPlayed(1, 1000, filter, user);
|
||||||
|
|
||||||
|
expect(listensService.getListens).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ limit: 100 })
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,152 @@
|
||||||
import { Test, TestingModule } from "@nestjs/testing";
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
|
import { IPaginationOptions, paginate } from "nestjs-typeorm-paginate";
|
||||||
|
import { Track } from "../music-library/track.entity";
|
||||||
|
import { User } from "../users/user.entity";
|
||||||
|
import { CreateListenResponseDto } from "./dto/create-listen.dto";
|
||||||
|
import { GetListensDto } from "./dto/get-listens.dto";
|
||||||
|
import { Listen } from "./listen.entity";
|
||||||
|
import { ListenRepository, ListenScopes } from "./listen.repository";
|
||||||
import { ListensService } from "./listens.service";
|
import { ListensService } from "./listens.service";
|
||||||
|
|
||||||
|
jest.mock("nestjs-typeorm-paginate");
|
||||||
|
|
||||||
describe("ListensService", () => {
|
describe("ListensService", () => {
|
||||||
let service: ListensService;
|
let service: ListensService;
|
||||||
|
let listenRepository: ListenRepository;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [ListensService],
|
providers: [
|
||||||
|
ListensService,
|
||||||
|
{ provide: ListenRepository, useFactory: () => ({}) },
|
||||||
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<ListensService>(ListensService);
|
service = module.get<ListensService>(ListensService);
|
||||||
|
listenRepository = module.get<ListenRepository>(ListenRepository);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be defined", () => {
|
it("should be defined", () => {
|
||||||
expect(service).toBeDefined();
|
expect(service).toBeDefined();
|
||||||
|
expect(listenRepository).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("createListen", () => {
|
||||||
|
let user: User;
|
||||||
|
let track: Track;
|
||||||
|
let playedAt: Date;
|
||||||
|
let response: CreateListenResponseDto;
|
||||||
|
beforeEach(() => {
|
||||||
|
user = { id: "USER" } as User;
|
||||||
|
track = { id: "TRACK" } as Track;
|
||||||
|
playedAt = new Date("2021-01-01T00:00:00Z");
|
||||||
|
|
||||||
|
response = {
|
||||||
|
listen: {
|
||||||
|
id: "LISTEN",
|
||||||
|
} as Listen,
|
||||||
|
isDuplicate: true,
|
||||||
|
};
|
||||||
|
listenRepository.insertNoConflict = jest.fn().mockResolvedValue(response);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates the listen", async () => {
|
||||||
|
await expect(
|
||||||
|
service.createListen({ user, track, playedAt })
|
||||||
|
).resolves.toEqual(response);
|
||||||
|
|
||||||
|
expect(listenRepository.insertNoConflict).toHaveBeenCalledTimes(1);
|
||||||
|
expect(listenRepository.insertNoConflict).toHaveBeenLastCalledWith({
|
||||||
|
user,
|
||||||
|
track,
|
||||||
|
playedAt,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getListens", () => {
|
||||||
|
let options: GetListensDto & IPaginationOptions;
|
||||||
|
let user: User;
|
||||||
|
let scopes: ListenScopes;
|
||||||
|
|
||||||
|
let listen: Listen;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
user = { id: "USER" } as User;
|
||||||
|
|
||||||
|
options = {
|
||||||
|
page: 2,
|
||||||
|
limit: 45,
|
||||||
|
user,
|
||||||
|
filter: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
scopes = {
|
||||||
|
byUser: jest.fn().mockReturnThis(),
|
||||||
|
duringInterval: jest.fn().mockReturnThis(),
|
||||||
|
leftJoinAndSelect: jest.fn().mockReturnThis(),
|
||||||
|
orderBy: jest.fn().mockReturnThis(),
|
||||||
|
};
|
||||||
|
// @ts-expect-error
|
||||||
|
listenRepository.scoped = scopes;
|
||||||
|
|
||||||
|
listen = {
|
||||||
|
id: "LISTEN",
|
||||||
|
} as Listen;
|
||||||
|
|
||||||
|
(paginate as jest.Mock).mockResolvedValue({ items: [listen] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the listens", async () => {
|
||||||
|
await expect(service.getListens(options)).resolves.toEqual({
|
||||||
|
items: [listen],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(paginate).toHaveBeenCalledTimes(1);
|
||||||
|
expect(paginate).toHaveBeenCalledWith(scopes, {
|
||||||
|
page: options.page,
|
||||||
|
limit: options.limit,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("filters for user", async () => {
|
||||||
|
await service.getListens(options);
|
||||||
|
|
||||||
|
expect(scopes.byUser).toHaveBeenCalledTimes(1);
|
||||||
|
expect(scopes.byUser).toHaveBeenCalledWith(user);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("filters for time", async () => {
|
||||||
|
await service.getListens(options);
|
||||||
|
expect(scopes.duringInterval).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
|
jest.clearAllMocks();
|
||||||
|
|
||||||
|
options.filter.time = {
|
||||||
|
start: new Date("2021-01-01T00:00:00Z"),
|
||||||
|
end: new Date("2021-01-01T00:00:00Z"),
|
||||||
|
};
|
||||||
|
|
||||||
|
await service.getListens(options);
|
||||||
|
|
||||||
|
expect(scopes.duringInterval).toHaveBeenCalledTimes(1);
|
||||||
|
expect(scopes.duringInterval).toHaveBeenCalledWith(options.filter.time);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getScopedQueryBuilder", () => {
|
||||||
|
let scopes: ListenScopes;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// @ts-expect-error
|
||||||
|
scopes = { id: "SCOPES" };
|
||||||
|
// @ts-expect-error
|
||||||
|
listenRepository.scoped = scopes;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the scoped query builder", async () => {
|
||||||
|
expect(service.getScopedQueryBuilder()).toEqual(scopes);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
74
src/metrics/metrics-auth.middleware.spec.ts
Normal file
74
src/metrics/metrics-auth.middleware.spec.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { UnauthorizedException } from "@nestjs/common";
|
||||||
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
|
import { IncomingMessage } from "http";
|
||||||
|
import { MetricsAuthMiddleware } from "./metrics-auth.middleware";
|
||||||
|
|
||||||
|
describe("MetricsAuthMiddleware", () => {
|
||||||
|
let middleware: MetricsAuthMiddleware;
|
||||||
|
let config: ConfigService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
MetricsAuthMiddleware,
|
||||||
|
{
|
||||||
|
provide: ConfigService,
|
||||||
|
useFactory: () => ({
|
||||||
|
get: jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValueOnce("foo") // Username
|
||||||
|
.mockReturnValueOnce("bar"), // Password
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
middleware = module.get<MetricsAuthMiddleware>(MetricsAuthMiddleware);
|
||||||
|
config = module.get<ConfigService>(ConfigService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be defined", () => {
|
||||||
|
expect(middleware).toBeDefined();
|
||||||
|
expect(config).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("use", () => {
|
||||||
|
let req: IncomingMessage;
|
||||||
|
let res: any;
|
||||||
|
let next: () => void;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
req = {
|
||||||
|
headers: { authorization: `Basic Zm9vOmJhcg==` },
|
||||||
|
} as IncomingMessage; // Buffer.from("foo:bar").toString("base64")
|
||||||
|
|
||||||
|
res = {};
|
||||||
|
|
||||||
|
next = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls next", async () => {
|
||||||
|
middleware.use(req, res, next);
|
||||||
|
|
||||||
|
expect(next).toHaveBeenCalledTimes(1);
|
||||||
|
expect(next).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws UnauthorizedException if header is not set", async () => {
|
||||||
|
delete req.headers.authorization;
|
||||||
|
|
||||||
|
expect(() => middleware.use(req, res, next)).toThrow(
|
||||||
|
UnauthorizedException
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws UnauthorizedException if header does not match", async () => {
|
||||||
|
req.headers.authorization = `Basic doesnotmatch`;
|
||||||
|
|
||||||
|
expect(() => middleware.use(req, res, next)).toThrow(
|
||||||
|
UnauthorizedException
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,18 +1,399 @@
|
||||||
import { Test, TestingModule } from "@nestjs/testing";
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
|
import { PostgresErrorCodes } from "../database/error-codes";
|
||||||
|
import { Album } from "./album.entity";
|
||||||
|
import { AlbumRepository } from "./album.repository";
|
||||||
|
import { Artist } from "./artist.entity";
|
||||||
|
import { ArtistRepository } from "./artist.repository";
|
||||||
|
import { CreateAlbumDto } from "./dto/create-album.dto";
|
||||||
|
import { CreateArtistDto } from "./dto/create-artist.dto";
|
||||||
|
import { CreateGenreDto } from "./dto/create-genre.dto";
|
||||||
|
import { CreateTrackDto } from "./dto/create-track.dto";
|
||||||
|
import { Genre } from "./genre.entity";
|
||||||
|
import { GenreRepository } from "./genre.repository";
|
||||||
import { MusicLibraryService } from "./music-library.service";
|
import { MusicLibraryService } from "./music-library.service";
|
||||||
|
import { Track } from "./track.entity";
|
||||||
|
import { TrackRepository } from "./track.repository";
|
||||||
|
|
||||||
describe("MusicLibraryService", () => {
|
describe("MusicLibraryService", () => {
|
||||||
let service: MusicLibraryService;
|
let service: MusicLibraryService;
|
||||||
|
let albumRepository: AlbumRepository;
|
||||||
|
let artistRepository: ArtistRepository;
|
||||||
|
let genreRepository: GenreRepository;
|
||||||
|
let trackRepository: TrackRepository;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [MusicLibraryService],
|
providers: [
|
||||||
|
MusicLibraryService,
|
||||||
|
{ provide: AlbumRepository, useFactory: () => ({}) },
|
||||||
|
{ provide: ArtistRepository, useFactory: () => ({}) },
|
||||||
|
{ provide: GenreRepository, useFactory: () => ({}) },
|
||||||
|
{ provide: TrackRepository, useFactory: () => ({}) },
|
||||||
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<MusicLibraryService>(MusicLibraryService);
|
service = module.get<MusicLibraryService>(MusicLibraryService);
|
||||||
|
albumRepository = module.get<AlbumRepository>(AlbumRepository);
|
||||||
|
artistRepository = module.get<ArtistRepository>(ArtistRepository);
|
||||||
|
genreRepository = module.get<GenreRepository>(GenreRepository);
|
||||||
|
trackRepository = module.get<TrackRepository>(TrackRepository);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be defined", () => {
|
it("should be defined", () => {
|
||||||
expect(service).toBeDefined();
|
expect(service).toBeDefined();
|
||||||
|
expect(albumRepository).toBeDefined();
|
||||||
|
expect(artistRepository).toBeDefined();
|
||||||
|
expect(genreRepository).toBeDefined();
|
||||||
|
expect(trackRepository).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Artist", () => {
|
||||||
|
describe("findArtist", () => {
|
||||||
|
it("returns the entity", async () => {
|
||||||
|
const artist = {
|
||||||
|
id: "ARTIST",
|
||||||
|
};
|
||||||
|
artistRepository.findOne = jest.fn().mockResolvedValue(artist);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
service.findArtist({ spotify: { id: "spotify_artist" } })
|
||||||
|
).resolves.toEqual(artist);
|
||||||
|
|
||||||
|
expect(artistRepository.findOne).toHaveBeenCalledTimes(1);
|
||||||
|
expect(artistRepository.findOne).toHaveBeenCalledWith({
|
||||||
|
where: { spotify: { id: "spotify_artist" } },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("createArtist", () => {
|
||||||
|
let createArtistDto: CreateArtistDto;
|
||||||
|
let artist: Artist;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
createArtistDto = {
|
||||||
|
name: "Foo",
|
||||||
|
spotify: {
|
||||||
|
id: "SPOTIFY_ID",
|
||||||
|
},
|
||||||
|
} as CreateArtistDto;
|
||||||
|
|
||||||
|
artist = {
|
||||||
|
...createArtistDto,
|
||||||
|
id: "ARTIST",
|
||||||
|
} as Artist;
|
||||||
|
|
||||||
|
artistRepository.create = jest.fn().mockReturnValue({ id: "ARTIST" });
|
||||||
|
artistRepository.save = jest.fn().mockResolvedValue(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates the entity", async () => {
|
||||||
|
await expect(service.createArtist(createArtistDto)).resolves.toEqual(
|
||||||
|
artist
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(artistRepository.create).toHaveBeenCalledTimes(1);
|
||||||
|
expect(artistRepository.save).toHaveBeenCalledTimes(1);
|
||||||
|
expect(artistRepository.save).toHaveBeenCalledWith(artist);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the entity on a unique violation", async () => {
|
||||||
|
artist.id = "ARTIST2";
|
||||||
|
service.findArtist = jest
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValue({ ...artist, id: "ARTIST2" });
|
||||||
|
|
||||||
|
const uniqueViolationError = new Error();
|
||||||
|
// @ts-expect-error
|
||||||
|
uniqueViolationError.code = PostgresErrorCodes.UNIQUE_VIOLATION;
|
||||||
|
// @ts-expect-error
|
||||||
|
uniqueViolationError.constraint = "IDX_ARTIST_SPOTIFY_ID";
|
||||||
|
|
||||||
|
artistRepository.save = jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue(uniqueViolationError);
|
||||||
|
|
||||||
|
await expect(service.createArtist(createArtistDto)).resolves.toEqual(
|
||||||
|
artist
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(service.findArtist).toHaveBeenCalledTimes(1);
|
||||||
|
expect(service.findArtist).toHaveBeenCalledWith({
|
||||||
|
spotify: { id: "SPOTIFY_ID" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws on generic errors", async () => {
|
||||||
|
artistRepository.save = jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue(new Error("Generic Error"));
|
||||||
|
|
||||||
|
await expect(service.createArtist(createArtistDto)).rejects.toThrow(
|
||||||
|
"Generic Error"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Album", () => {
|
||||||
|
describe("findAlbum", () => {
|
||||||
|
it("returns the entity", async () => {
|
||||||
|
const album = {
|
||||||
|
id: "ALBUM",
|
||||||
|
};
|
||||||
|
albumRepository.findOne = jest.fn().mockResolvedValue(album);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
service.findAlbum({ spotify: { id: "spotify_album" } })
|
||||||
|
).resolves.toEqual(album);
|
||||||
|
|
||||||
|
expect(albumRepository.findOne).toHaveBeenCalledTimes(1);
|
||||||
|
expect(albumRepository.findOne).toHaveBeenCalledWith({
|
||||||
|
where: { spotify: { id: "spotify_album" } },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("createAlbum", () => {
|
||||||
|
let createAlbumDto: CreateAlbumDto;
|
||||||
|
let album: Album;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
createAlbumDto = {
|
||||||
|
name: "Foo",
|
||||||
|
spotify: {
|
||||||
|
id: "SPOTIFY_ID",
|
||||||
|
},
|
||||||
|
} as CreateAlbumDto;
|
||||||
|
|
||||||
|
album = {
|
||||||
|
...createAlbumDto,
|
||||||
|
id: "ALBUM",
|
||||||
|
} as Album;
|
||||||
|
|
||||||
|
albumRepository.create = jest.fn().mockReturnValue({ id: "ALBUM" });
|
||||||
|
albumRepository.save = jest.fn().mockResolvedValue(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates the entity", async () => {
|
||||||
|
await expect(service.createAlbum(createAlbumDto)).resolves.toEqual(
|
||||||
|
album
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(albumRepository.create).toHaveBeenCalledTimes(1);
|
||||||
|
expect(albumRepository.save).toHaveBeenCalledTimes(1);
|
||||||
|
expect(albumRepository.save).toHaveBeenCalledWith(album);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the entity on a unique violation", async () => {
|
||||||
|
album.id = "ALBUM2";
|
||||||
|
service.findAlbum = jest
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValue({ ...album, id: "ALBUM2" });
|
||||||
|
|
||||||
|
const uniqueViolationError = new Error();
|
||||||
|
// @ts-expect-error
|
||||||
|
uniqueViolationError.code = PostgresErrorCodes.UNIQUE_VIOLATION;
|
||||||
|
// @ts-expect-error
|
||||||
|
uniqueViolationError.constraint = "IDX_ALBUM_SPOTIFY_ID";
|
||||||
|
|
||||||
|
albumRepository.save = jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue(uniqueViolationError);
|
||||||
|
|
||||||
|
await expect(service.createAlbum(createAlbumDto)).resolves.toEqual(
|
||||||
|
album
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(service.findAlbum).toHaveBeenCalledTimes(1);
|
||||||
|
expect(service.findAlbum).toHaveBeenCalledWith({
|
||||||
|
spotify: { id: "SPOTIFY_ID" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws on generic errors", async () => {
|
||||||
|
albumRepository.save = jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue(new Error("Generic Error"));
|
||||||
|
|
||||||
|
await expect(service.createAlbum(createAlbumDto)).rejects.toThrow(
|
||||||
|
"Generic Error"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Genre", () => {
|
||||||
|
describe("findGenre", () => {
|
||||||
|
it("returns the entity", async () => {
|
||||||
|
const genre = {
|
||||||
|
id: "GENRE",
|
||||||
|
name: "Foo",
|
||||||
|
};
|
||||||
|
genreRepository.findOne = jest.fn().mockResolvedValue(genre);
|
||||||
|
|
||||||
|
await expect(service.findGenre({ name: "Foo" })).resolves.toEqual(
|
||||||
|
genre
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(genreRepository.findOne).toHaveBeenCalledTimes(1);
|
||||||
|
expect(genreRepository.findOne).toHaveBeenCalledWith({
|
||||||
|
where: { name: "Foo" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("createGenre", () => {
|
||||||
|
let createGenreDto: CreateGenreDto;
|
||||||
|
let genre: Genre;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
createGenreDto = {
|
||||||
|
name: "Foo",
|
||||||
|
} as CreateGenreDto;
|
||||||
|
|
||||||
|
genre = {
|
||||||
|
...createGenreDto,
|
||||||
|
id: "GENRE",
|
||||||
|
} as Genre;
|
||||||
|
|
||||||
|
genreRepository.create = jest.fn().mockReturnValue({ id: "GENRE" });
|
||||||
|
genreRepository.save = jest.fn().mockResolvedValue(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates the entity", async () => {
|
||||||
|
await expect(service.createGenre(createGenreDto)).resolves.toEqual(
|
||||||
|
genre
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(genreRepository.create).toHaveBeenCalledTimes(1);
|
||||||
|
expect(genreRepository.save).toHaveBeenCalledTimes(1);
|
||||||
|
expect(genreRepository.save).toHaveBeenCalledWith(genre);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the entity on a unique violation", async () => {
|
||||||
|
genre.id = "GENRE2";
|
||||||
|
service.findGenre = jest
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValue({ ...genre, id: "GENRE2" });
|
||||||
|
|
||||||
|
const uniqueViolationError = new Error();
|
||||||
|
// @ts-expect-error
|
||||||
|
uniqueViolationError.code = PostgresErrorCodes.UNIQUE_VIOLATION;
|
||||||
|
// @ts-expect-error
|
||||||
|
uniqueViolationError.constraint = "IDX_GENRE_NAME";
|
||||||
|
|
||||||
|
genreRepository.save = jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue(uniqueViolationError);
|
||||||
|
|
||||||
|
await expect(service.createGenre(createGenreDto)).resolves.toEqual(
|
||||||
|
genre
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(service.findGenre).toHaveBeenCalledTimes(1);
|
||||||
|
expect(service.findGenre).toHaveBeenCalledWith({
|
||||||
|
name: "Foo",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws on generic errors", async () => {
|
||||||
|
genreRepository.save = jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue(new Error("Generic Error"));
|
||||||
|
|
||||||
|
await expect(service.createGenre(createGenreDto)).rejects.toThrow(
|
||||||
|
"Generic Error"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Track", () => {
|
||||||
|
describe("findTrack", () => {
|
||||||
|
it("returns the entity", async () => {
|
||||||
|
const track = {
|
||||||
|
id: "TRACK",
|
||||||
|
};
|
||||||
|
trackRepository.findOne = jest.fn().mockResolvedValue(track);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
service.findTrack({ spotify: { id: "spotify_track" } })
|
||||||
|
).resolves.toEqual(track);
|
||||||
|
|
||||||
|
expect(trackRepository.findOne).toHaveBeenCalledTimes(1);
|
||||||
|
expect(trackRepository.findOne).toHaveBeenCalledWith({
|
||||||
|
where: { spotify: { id: "spotify_track" } },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("createTrack", () => {
|
||||||
|
let createTrackDto: CreateTrackDto;
|
||||||
|
let track: Track;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
createTrackDto = {
|
||||||
|
name: "Foo",
|
||||||
|
spotify: {
|
||||||
|
id: "SPOTIFY_ID",
|
||||||
|
},
|
||||||
|
} as CreateTrackDto;
|
||||||
|
|
||||||
|
track = {
|
||||||
|
...createTrackDto,
|
||||||
|
id: "TRACK",
|
||||||
|
} as Track;
|
||||||
|
|
||||||
|
trackRepository.create = jest.fn().mockReturnValue({ id: "TRACK" });
|
||||||
|
trackRepository.save = jest.fn().mockResolvedValue(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates the entity", async () => {
|
||||||
|
await expect(service.createTrack(createTrackDto)).resolves.toEqual(
|
||||||
|
track
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(trackRepository.create).toHaveBeenCalledTimes(1);
|
||||||
|
expect(trackRepository.save).toHaveBeenCalledTimes(1);
|
||||||
|
expect(trackRepository.save).toHaveBeenCalledWith(track);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the entity on a unique violation", async () => {
|
||||||
|
track.id = "TRACK2";
|
||||||
|
service.findTrack = jest
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValue({ ...track, id: "TRACK2" });
|
||||||
|
|
||||||
|
const uniqueViolationError = new Error();
|
||||||
|
// @ts-expect-error
|
||||||
|
uniqueViolationError.code = PostgresErrorCodes.UNIQUE_VIOLATION;
|
||||||
|
// @ts-expect-error
|
||||||
|
uniqueViolationError.constraint = "IDX_TRACK_SPOTIFY_ID";
|
||||||
|
|
||||||
|
trackRepository.save = jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue(uniqueViolationError);
|
||||||
|
|
||||||
|
await expect(service.createTrack(createTrackDto)).resolves.toEqual(
|
||||||
|
track
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(service.findTrack).toHaveBeenCalledTimes(1);
|
||||||
|
expect(service.findTrack).toHaveBeenCalledWith({
|
||||||
|
spotify: { id: "SPOTIFY_ID" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws on generic errors", async () => {
|
||||||
|
trackRepository.save = jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue(new Error("Generic Error"));
|
||||||
|
|
||||||
|
await expect(service.createTrack(createTrackDto)).rejects.toThrow(
|
||||||
|
"Generic Error"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,8 @@ export class MusicLibraryService {
|
||||||
// executed and it is now available in the database for use to retrieve
|
// executed and it is now available in the database for use to retrieve
|
||||||
return this.findAlbum({ spotify: { id: data.spotify.id } });
|
return this.findAlbum({ spotify: { id: data.spotify.id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return album;
|
return album;
|
||||||
|
|
@ -139,6 +141,8 @@ export class MusicLibraryService {
|
||||||
// executed and it is now available in the database for use to retrieve
|
// executed and it is now available in the database for use to retrieve
|
||||||
return this.findTrack({ spotify: { id: data.spotify.id } });
|
return this.findTrack({ spotify: { id: data.spotify.id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return track;
|
return track;
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,23 @@
|
||||||
import { Test, TestingModule } from "@nestjs/testing";
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
import { ReportsController } from "./reports.controller";
|
import { ReportsController } from "./reports.controller";
|
||||||
|
import { ReportsService } from "./reports.service";
|
||||||
|
|
||||||
describe("Reports Controller", () => {
|
describe("Reports Controller", () => {
|
||||||
let controller: ReportsController;
|
let controller: ReportsController;
|
||||||
|
let reportsService: ReportsService;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
controllers: [ReportsController],
|
controllers: [ReportsController],
|
||||||
|
providers: [{ provide: ReportsService, useFactory: () => ({}) }],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
controller = module.get<ReportsController>(ReportsController);
|
controller = module.get<ReportsController>(ReportsController);
|
||||||
|
reportsService = module.get<ReportsService>(ReportsService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be defined", () => {
|
it("should be defined", () => {
|
||||||
expect(controller).toBeDefined();
|
expect(controller).toBeDefined();
|
||||||
|
expect(reportsService).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,25 @@
|
||||||
import { Test, TestingModule } from "@nestjs/testing";
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
|
import { ListensService } from "../listens/listens.service";
|
||||||
import { ReportsService } from "./reports.service";
|
import { ReportsService } from "./reports.service";
|
||||||
|
|
||||||
describe("ReportsService", () => {
|
describe("ReportsService", () => {
|
||||||
let service: ReportsService;
|
let service: ReportsService;
|
||||||
|
let listensService: ListensService;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [ReportsService],
|
providers: [
|
||||||
|
ReportsService,
|
||||||
|
{ provide: ListensService, useFactory: () => ({}) },
|
||||||
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<ReportsService>(ReportsService);
|
service = module.get<ReportsService>(ReportsService);
|
||||||
|
listensService = module.get<ListensService>(ListensService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be defined", () => {
|
it("should be defined", () => {
|
||||||
expect(service).toBeDefined();
|
expect(service).toBeDefined();
|
||||||
|
expect(listensService).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,25 @@
|
||||||
|
import { HttpService } from "@nestjs/common";
|
||||||
import { Test, TestingModule } from "@nestjs/testing";
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
import { SpotifyApiService } from "./spotify-api.service";
|
import { SpotifyApiService } from "./spotify-api.service";
|
||||||
|
|
||||||
describe("SpotifyApiService", () => {
|
describe("SpotifyApiService", () => {
|
||||||
let service: SpotifyApiService;
|
let service: SpotifyApiService;
|
||||||
|
let httpService: HttpService;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [SpotifyApiService],
|
providers: [
|
||||||
|
SpotifyApiService,
|
||||||
|
{ provide: HttpService, useFactory: () => ({}) },
|
||||||
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<SpotifyApiService>(SpotifyApiService);
|
service = module.get<SpotifyApiService>(SpotifyApiService);
|
||||||
|
httpService = module.get<HttpService>(HttpService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be defined", () => {
|
it("should be defined", () => {
|
||||||
expect(service).toBeDefined();
|
expect(service).toBeDefined();
|
||||||
|
expect(httpService).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,50 @@
|
||||||
|
import { Logger } from "@nestjs/common";
|
||||||
import { Test, TestingModule } from "@nestjs/testing";
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
|
import { ListensService } from "../../listens/listens.service";
|
||||||
|
import { MusicLibraryService } from "../../music-library/music-library.service";
|
||||||
|
import { UsersService } from "../../users/users.service";
|
||||||
|
import { SpotifyApiService } from "./spotify-api/spotify-api.service";
|
||||||
|
import { SpotifyAuthService } from "./spotify-auth/spotify-auth.service";
|
||||||
import { SpotifyService } from "./spotify.service";
|
import { SpotifyService } from "./spotify.service";
|
||||||
|
|
||||||
describe("SpotifyService", () => {
|
describe("SpotifyService", () => {
|
||||||
let service: SpotifyService;
|
let service: SpotifyService;
|
||||||
|
let usersService: UsersService;
|
||||||
|
let listensService: ListensService;
|
||||||
|
let musicLibraryService: MusicLibraryService;
|
||||||
|
let spotifyApi: SpotifyApiService;
|
||||||
|
let spotifyAuth: SpotifyAuthService;
|
||||||
|
let logger: Logger;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [SpotifyService],
|
providers: [
|
||||||
|
SpotifyService,
|
||||||
|
{ provide: UsersService, useFactory: () => ({}) },
|
||||||
|
{ provide: ListensService, useFactory: () => ({}) },
|
||||||
|
{ provide: MusicLibraryService, useFactory: () => ({}) },
|
||||||
|
{ provide: SpotifyApiService, useFactory: () => ({}) },
|
||||||
|
{ provide: SpotifyAuthService, useFactory: () => ({}) },
|
||||||
|
{ provide: Logger, useValue: new Logger() },
|
||||||
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<SpotifyService>(SpotifyService);
|
service = module.get<SpotifyService>(SpotifyService);
|
||||||
|
usersService = module.get<UsersService>(UsersService);
|
||||||
|
listensService = module.get<ListensService>(ListensService);
|
||||||
|
musicLibraryService = module.get<MusicLibraryService>(MusicLibraryService);
|
||||||
|
spotifyApi = module.get<SpotifyApiService>(SpotifyApiService);
|
||||||
|
spotifyAuth = module.get<SpotifyAuthService>(SpotifyAuthService);
|
||||||
|
logger = module.get<Logger>(Logger);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be defined", () => {
|
it("should be defined", () => {
|
||||||
expect(service).toBeDefined();
|
expect(service).toBeDefined();
|
||||||
|
expect(usersService).toBeDefined();
|
||||||
|
expect(listensService).toBeDefined();
|
||||||
|
expect(musicLibraryService).toBeDefined();
|
||||||
|
expect(spotifyApi).toBeDefined();
|
||||||
|
expect(spotifyAuth).toBeDefined();
|
||||||
|
expect(logger).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,25 @@
|
||||||
import { Test, TestingModule } from "@nestjs/testing";
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
|
import { UserRepository } from "./user.repository";
|
||||||
import { UsersService } from "./users.service";
|
import { UsersService } from "./users.service";
|
||||||
|
|
||||||
describe("UsersService", () => {
|
describe("UsersService", () => {
|
||||||
let service: UsersService;
|
let service: UsersService;
|
||||||
|
let userRepository: UserRepository;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [UsersService],
|
providers: [
|
||||||
|
UsersService,
|
||||||
|
{ provide: UserRepository, useFactory: () => ({}) },
|
||||||
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<UsersService>(UsersService);
|
service = module.get<UsersService>(UsersService);
|
||||||
|
userRepository = module.get<UserRepository>(UserRepository);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be defined", () => {
|
it("should be defined", () => {
|
||||||
expect(service).toBeDefined();
|
expect(service).toBeDefined();
|
||||||
|
expect(userRepository).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,6 @@ describe("AppController (e2e)", () => {
|
||||||
await app.init();
|
await app.init();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("/ (GET)", () => {
|
it("/ (GET)", () =>
|
||||||
return request(app.getHttpServer())
|
request(app.getHttpServer()).get("/").expect(200).expect("Hello World!"));
|
||||||
.get("/")
|
|
||||||
.expect(200)
|
|
||||||
.expect("Hello World!");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue