897 lines
24 KiB
TypeScript
897 lines
24 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
import { setup } from "../../__testData__/controller-test";
|
|
import * as Configuration from "../../../src/init/configuration";
|
|
import * as UserDal from "../../../src/dal/user";
|
|
import * as NewQuotesDal from "../../../src/dal/new-quotes";
|
|
import type { DBNewQuote } from "../../../src/dal/new-quotes";
|
|
import * as QuoteRatingsDal from "../../../src/dal/quote-ratings";
|
|
import * as ReportDal from "../../../src/dal/report";
|
|
import * as LogsDal from "../../../src/dal/logs";
|
|
import * as Captcha from "../../../src/utils/captcha";
|
|
import { ObjectId } from "mongodb";
|
|
import { ApproveQuote } from "@monkeytype/schemas/quotes";
|
|
|
|
const { mockApp, uid } = setup();
|
|
const configuration = Configuration.getCachedConfiguration();
|
|
|
|
describe("QuotesController", () => {
|
|
const getPartialUserMock = vi.spyOn(UserDal, "getPartialUser");
|
|
const logsAddLogMock = vi.spyOn(LogsDal, "addLog");
|
|
|
|
beforeEach(() => {
|
|
enableQuotes(true);
|
|
|
|
const user = { quoteMod: true, name: "Bob" } as any;
|
|
getPartialUserMock.mockClear().mockResolvedValue(user);
|
|
logsAddLogMock.mockClear().mockResolvedValue();
|
|
});
|
|
|
|
describe("getQuotes", () => {
|
|
const getQuotesMock = vi.spyOn(NewQuotesDal, "get");
|
|
|
|
beforeEach(() => {
|
|
getQuotesMock.mockClear();
|
|
getQuotesMock.mockResolvedValue([]);
|
|
});
|
|
it("should return quotes", async () => {
|
|
//GIVEN
|
|
const quoteOne: DBNewQuote = {
|
|
_id: new ObjectId(),
|
|
text: "test",
|
|
source: "Bob",
|
|
language: "english",
|
|
submittedBy: "Kevin",
|
|
timestamp: 1000,
|
|
approved: true,
|
|
};
|
|
const quoteTwo: DBNewQuote = {
|
|
_id: new ObjectId(),
|
|
text: "test2",
|
|
source: "Stuart",
|
|
language: "english",
|
|
submittedBy: "Kevin",
|
|
timestamp: 2000,
|
|
approved: false,
|
|
};
|
|
getQuotesMock.mockResolvedValue([quoteOne, quoteTwo]);
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.get("/quotes")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(200);
|
|
|
|
//THEN
|
|
expect(body.message).toEqual("Quote submissions retrieved");
|
|
expect(body.data).toEqual([
|
|
{ ...quoteOne, _id: quoteOne._id.toHexString() },
|
|
{
|
|
...quoteTwo,
|
|
_id: quoteTwo._id.toHexString(),
|
|
},
|
|
]);
|
|
|
|
expect(getQuotesMock).toHaveBeenCalledWith("all");
|
|
});
|
|
it("should return quotes with quoteMod", async () => {
|
|
//GIVEN
|
|
getPartialUserMock
|
|
.mockClear()
|
|
.mockResolvedValue({ quoteMod: "english" } as any);
|
|
|
|
//WHEN
|
|
await mockApp
|
|
.get("/quotes")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(200);
|
|
|
|
//THEN
|
|
|
|
expect(getQuotesMock).toHaveBeenCalledWith("english");
|
|
});
|
|
it("should fail with quoteMod false", async () => {
|
|
//GIVEN
|
|
getPartialUserMock
|
|
.mockClear()
|
|
.mockResolvedValue({ quoteMod: false } as any);
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.get("/quotes")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(403);
|
|
|
|
//THEN
|
|
expect(body.message).toEqual("You don't have permission to do this.");
|
|
|
|
expect(getQuotesMock).not.toHaveBeenCalled();
|
|
});
|
|
it("should fail with quoteMod empty", async () => {
|
|
//GIVEN
|
|
getPartialUserMock.mockClear().mockResolvedValue({ quoteMod: "" } as any);
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.get("/quotes")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(403);
|
|
|
|
//THEN
|
|
expect(body.message).toEqual("You don't have permission to do this.");
|
|
|
|
expect(getQuotesMock).not.toHaveBeenCalled();
|
|
});
|
|
it("should fail without authentication", async () => {
|
|
await mockApp.get("/quotes").expect(401);
|
|
});
|
|
});
|
|
describe("isSubmissionsEnabled", () => {
|
|
it("should return for quotes enabled without authentication", async () => {
|
|
//GIVEN
|
|
enableQuotes(true);
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.get("/quotes/isSubmissionEnabled")
|
|
.expect(200);
|
|
|
|
expect(body).toEqual({
|
|
message: "Quote submission enabled",
|
|
data: { isEnabled: true },
|
|
});
|
|
});
|
|
it("should return for quotes disabled without authentication", async () => {
|
|
//GIVEN
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.get("/quotes/isSubmissionEnabled")
|
|
.expect(200);
|
|
|
|
expect(body).toEqual({
|
|
message: "Quote submission enabled",
|
|
data: { isEnabled: true },
|
|
});
|
|
});
|
|
});
|
|
describe("addQuote", () => {
|
|
const addQuoteMock = vi.spyOn(NewQuotesDal, "add");
|
|
const verifyCaptchaMock = vi.spyOn(Captcha, "verify");
|
|
|
|
beforeEach(() => {
|
|
addQuoteMock.mockClear();
|
|
addQuoteMock.mockResolvedValue({} as any);
|
|
|
|
verifyCaptchaMock.mockClear();
|
|
verifyCaptchaMock.mockResolvedValue(true);
|
|
});
|
|
|
|
it("should add quote", async () => {
|
|
//GIVEN
|
|
const newQuote = {
|
|
text: new Array(60).fill("a").join(""),
|
|
source: "Bob",
|
|
language: "english",
|
|
captcha: "captcha",
|
|
};
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send(newQuote)
|
|
.expect(200);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Quote submission added",
|
|
data: null,
|
|
});
|
|
|
|
expect(addQuoteMock).toHaveBeenCalledWith(
|
|
newQuote.text,
|
|
newQuote.source,
|
|
newQuote.language,
|
|
uid,
|
|
);
|
|
|
|
expect(verifyCaptchaMock).toHaveBeenCalledWith(newQuote.captcha);
|
|
});
|
|
it("should fail without authentication", async () => {
|
|
await mockApp.post("/quotes").expect(401);
|
|
});
|
|
it("should fail if feature is disabled", async () => {
|
|
//GIVEN
|
|
enableQuotes(false);
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(503);
|
|
|
|
//THEN
|
|
expect(body.message).toEqual(
|
|
"Quote submission is disabled temporarily. The queue is quite long and we need some time to catch up.",
|
|
);
|
|
});
|
|
it("should fail without mandatory properties", async () => {
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(422);
|
|
|
|
expect(body).toEqual({
|
|
message: "Invalid request data schema",
|
|
validationErrors: [
|
|
'"text" Required',
|
|
'"source" Required',
|
|
'"language" Required',
|
|
'"captcha" Required',
|
|
],
|
|
});
|
|
});
|
|
it("should fail with unknown properties", async () => {
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes")
|
|
.send({
|
|
text: new Array(60).fill("a").join(""),
|
|
source: "Bob",
|
|
language: "english",
|
|
captcha: "captcha",
|
|
extra: "value",
|
|
})
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(422);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Invalid request data schema",
|
|
validationErrors: ["Unrecognized key(s) in object: 'extra'"],
|
|
});
|
|
});
|
|
it("should fail with invalid capture", async () => {
|
|
//GIVEN
|
|
verifyCaptchaMock.mockResolvedValue(false);
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes")
|
|
.send({
|
|
text: new Array(60).fill("a").join(""),
|
|
source: "Bob",
|
|
language: "english",
|
|
captcha: "captcha",
|
|
})
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(422);
|
|
|
|
//THEN
|
|
expect(body.message).toEqual("Captcha check failed");
|
|
});
|
|
});
|
|
describe("approveQuote", () => {
|
|
const approveQuoteMock = vi.spyOn(NewQuotesDal, "approve");
|
|
|
|
beforeEach(() => {
|
|
approveQuoteMock.mockClear();
|
|
});
|
|
|
|
it("should approve", async () => {
|
|
//GiVEN
|
|
const quoteId = new ObjectId().toHexString();
|
|
const quote: ApproveQuote = {
|
|
id: 100,
|
|
text: "text",
|
|
source: "source",
|
|
length: 10,
|
|
approvedBy: "Kevin",
|
|
};
|
|
approveQuoteMock.mockResolvedValue({
|
|
message: "ok",
|
|
quote,
|
|
});
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/approve")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({
|
|
quoteId,
|
|
editText: "editedText",
|
|
editSource: "editedSource",
|
|
})
|
|
.expect(200);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "ok",
|
|
data: quote,
|
|
});
|
|
|
|
expect(approveQuoteMock).toHaveBeenCalledWith(
|
|
quoteId,
|
|
"editedText",
|
|
"editedSource",
|
|
"Bob",
|
|
);
|
|
});
|
|
it("should approve with optional parameters as null", async () => {
|
|
//GiVEN
|
|
const quoteId = new ObjectId().toHexString();
|
|
approveQuoteMock.mockResolvedValue({
|
|
message: "ok",
|
|
quote: {} as any,
|
|
});
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/approve")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({ quoteId, editText: null, editSource: null })
|
|
.expect(200);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "ok",
|
|
data: {},
|
|
});
|
|
|
|
expect(approveQuoteMock).toHaveBeenCalledWith(
|
|
quoteId,
|
|
undefined,
|
|
undefined,
|
|
"Bob",
|
|
);
|
|
});
|
|
it("should approve without optional parameters", async () => {
|
|
//GiVEN
|
|
const quoteId = new ObjectId().toHexString();
|
|
approveQuoteMock.mockResolvedValue({
|
|
message: "ok",
|
|
quote: {} as any,
|
|
});
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/approve")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({ quoteId })
|
|
.expect(200);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "ok",
|
|
data: {},
|
|
});
|
|
|
|
expect(approveQuoteMock).toHaveBeenCalledWith(
|
|
quoteId,
|
|
undefined,
|
|
undefined,
|
|
"Bob",
|
|
);
|
|
});
|
|
it("should fail without mandatory properties", async () => {
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/approve")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(422);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Invalid request data schema",
|
|
validationErrors: ['"quoteId" Required'],
|
|
});
|
|
});
|
|
it("should fail with unknown properties", async () => {
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/approve")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({ quoteId: new ObjectId().toHexString(), extra: "value" })
|
|
.expect(422);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Invalid request data schema",
|
|
validationErrors: ["Unrecognized key(s) in object: 'extra'"],
|
|
});
|
|
});
|
|
it("should fail if user is no quote mod", async () => {
|
|
//GIVEN
|
|
getPartialUserMock.mockClear().mockResolvedValue({} as any);
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/approve")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({ quoteId: new ObjectId().toHexString() })
|
|
.expect(403);
|
|
|
|
//THEN
|
|
expect(body.message).toEqual("You don't have permission to do this.");
|
|
});
|
|
it("should fail without authentication", async () => {
|
|
await mockApp
|
|
.post("/quotes/approve")
|
|
.send({ quoteId: new ObjectId().toHexString() })
|
|
.expect(401);
|
|
});
|
|
});
|
|
describe("refuseQuote", () => {
|
|
const refuseQuoteMock = vi.spyOn(NewQuotesDal, "refuse");
|
|
|
|
beforeEach(() => {
|
|
refuseQuoteMock.mockClear();
|
|
refuseQuoteMock.mockResolvedValue();
|
|
});
|
|
|
|
it("should refuse quote", async () => {
|
|
//GIVEN
|
|
const quoteId = new ObjectId().toHexString();
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/reject")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({ quoteId })
|
|
.expect(200);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Quote refused",
|
|
data: null,
|
|
});
|
|
expect(refuseQuoteMock).toHaveBeenCalledWith(quoteId);
|
|
});
|
|
it("should fail without mandatory properties", async () => {
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/reject")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
|
|
.expect(422);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Invalid request data schema",
|
|
validationErrors: ['"quoteId" Required'],
|
|
});
|
|
});
|
|
it("should fail with unknown properties", async () => {
|
|
//GIVEN
|
|
const quoteId = new ObjectId().toHexString();
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/reject")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({ quoteId, extra: "value" })
|
|
.expect(422);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Invalid request data schema",
|
|
validationErrors: ["Unrecognized key(s) in object: 'extra'"],
|
|
});
|
|
});
|
|
it("should fail if user is no quote mod", async () => {
|
|
//GIVEN
|
|
getPartialUserMock.mockClear().mockResolvedValue({} as any);
|
|
const quoteId = new ObjectId().toHexString();
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/reject")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({ quoteId })
|
|
.expect(403);
|
|
|
|
//THEN
|
|
expect(body.message).toEqual("You don't have permission to do this.");
|
|
});
|
|
it("should fail without authentication", async () => {
|
|
await mockApp
|
|
.post("/quotes/reject")
|
|
.send({ quoteId: new ObjectId().toHexString() })
|
|
.expect(401);
|
|
});
|
|
});
|
|
describe("getRating", () => {
|
|
const getRatingMock = vi.spyOn(QuoteRatingsDal, "get");
|
|
|
|
beforeEach(() => {
|
|
getRatingMock.mockClear();
|
|
});
|
|
|
|
it("should get", async () => {
|
|
//GIVEN
|
|
const quoteRating = {
|
|
_id: new ObjectId(),
|
|
average: 2,
|
|
language: "english",
|
|
quoteId: 23,
|
|
ratings: 100,
|
|
totalRating: 122,
|
|
};
|
|
getRatingMock.mockResolvedValue(quoteRating as any);
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.get("/quotes/rating")
|
|
.query({ quoteId: 42, language: "english" })
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(200);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Rating retrieved",
|
|
data: { ...quoteRating, _id: quoteRating._id.toHexString() },
|
|
});
|
|
|
|
expect(getRatingMock).toHaveBeenCalledWith(42, "english");
|
|
});
|
|
it("should fail without mandatory query parameters", async () => {
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.get("/quotes/rating")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(422);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Invalid query schema",
|
|
validationErrors: ['"quoteId" Invalid input', '"language" Required'],
|
|
});
|
|
});
|
|
it("should fail with unknown query parameters", async () => {
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.get("/quotes/rating")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.query({ quoteId: 42, language: "english", extra: "value" })
|
|
.expect(422);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Invalid query schema",
|
|
validationErrors: ["Unrecognized key(s) in object: 'extra'"],
|
|
});
|
|
});
|
|
it("should fail without authentication", async () => {
|
|
await mockApp
|
|
.get("/quotes/rating")
|
|
.query({ quoteId: 42, language: "english" })
|
|
.expect(401);
|
|
});
|
|
});
|
|
describe("submitRating", () => {
|
|
const updateQuotesRatingsMock = vi.spyOn(UserDal, "updateQuoteRatings");
|
|
const submitQuoteRating = vi.spyOn(QuoteRatingsDal, "submit");
|
|
|
|
beforeEach(() => {
|
|
getPartialUserMock
|
|
.mockClear()
|
|
.mockResolvedValue({ quoteRatings: null } as any);
|
|
|
|
updateQuotesRatingsMock.mockClear().mockResolvedValue({} as any);
|
|
submitQuoteRating.mockClear().mockResolvedValue();
|
|
});
|
|
it("should submit new rating", async () => {
|
|
//GIVEN
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/rating")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({
|
|
quoteId: 23,
|
|
rating: 4,
|
|
language: "english",
|
|
})
|
|
.expect(200);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Rating submitted",
|
|
data: null,
|
|
});
|
|
|
|
expect(submitQuoteRating).toHaveBeenCalledWith(23, "english", 4, false);
|
|
|
|
expect(updateQuotesRatingsMock).toHaveBeenCalledWith(uid, {
|
|
english: { "23": 4 },
|
|
});
|
|
});
|
|
it("should update existing rating", async () => {
|
|
//GIVEN
|
|
|
|
getPartialUserMock.mockClear().mockResolvedValue({
|
|
quoteRatings: { german: { "4": 1 }, english: { "5": 5, "23": 4 } },
|
|
} as any);
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/rating")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({
|
|
quoteId: 23,
|
|
rating: 2,
|
|
language: "english",
|
|
})
|
|
.expect(200);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Rating updated",
|
|
data: null,
|
|
});
|
|
|
|
expect(submitQuoteRating).toHaveBeenCalledWith(23, "english", -2, true);
|
|
|
|
expect(updateQuotesRatingsMock).toHaveBeenCalledWith(uid, {
|
|
german: { "4": 1 },
|
|
english: { "5": 5, "23": 2 },
|
|
});
|
|
});
|
|
|
|
it("should update existing rating with same rating", async () => {
|
|
//GIVEN
|
|
|
|
getPartialUserMock.mockClear().mockResolvedValue({
|
|
quoteRatings: { german: { "4": 1 }, english: { "5": 5, "23": 4 } },
|
|
} as any);
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/rating")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({
|
|
quoteId: 23,
|
|
rating: 4,
|
|
language: "english",
|
|
})
|
|
.expect(200);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Rating updated",
|
|
data: null,
|
|
});
|
|
|
|
expect(submitQuoteRating).toHaveBeenCalledWith(23, "english", 0, true);
|
|
|
|
expect(updateQuotesRatingsMock).toHaveBeenCalledWith(uid, {
|
|
german: { "4": 1 },
|
|
english: { "5": 5, "23": 4 },
|
|
});
|
|
});
|
|
|
|
it("should fail with missing mandatory parameter", async () => {
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/rating")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(422);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Invalid request data schema",
|
|
validationErrors: [
|
|
'"quoteId" Invalid input',
|
|
'"language" Required',
|
|
'"rating" Required',
|
|
],
|
|
});
|
|
});
|
|
it("should fail with unknown parameter", async () => {
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/rating")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({ quoteId: 23, language: "english", rating: 5, extra: "value" })
|
|
.expect(422);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Invalid request data schema",
|
|
validationErrors: ["Unrecognized key(s) in object: 'extra'"],
|
|
});
|
|
});
|
|
it("should fail with zero rating", async () => {
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/rating")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({ quoteId: 23, language: "english", rating: 0 })
|
|
.expect(422);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Invalid request data schema",
|
|
validationErrors: [
|
|
'"rating" Number must be greater than or equal to 1',
|
|
],
|
|
});
|
|
});
|
|
it("should fail with rating bigger than 5", async () => {
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/rating")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({ quoteId: 23, language: "english", rating: 6 })
|
|
.expect(422);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Invalid request data schema",
|
|
validationErrors: ['"rating" Number must be less than or equal to 5'],
|
|
});
|
|
});
|
|
|
|
it("should fail with non-integer rating", async () => {
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/rating")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({ quoteId: 23, language: "english", rating: 2.5 })
|
|
.expect(422);
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Invalid request data schema",
|
|
validationErrors: ['"rating" Expected integer, received float'],
|
|
});
|
|
});
|
|
|
|
it("should fail without authentication", async () => {
|
|
await mockApp.post("/quotes/rating").expect(401);
|
|
});
|
|
});
|
|
describe("reportQuote", () => {
|
|
const verifyCaptchaMock = vi.spyOn(Captcha, "verify");
|
|
const createReportMock = vi.spyOn(ReportDal, "createReport");
|
|
|
|
beforeEach(() => {
|
|
enableQuoteReporting(true);
|
|
|
|
verifyCaptchaMock.mockClear().mockResolvedValue(true);
|
|
createReportMock.mockClear().mockResolvedValue();
|
|
});
|
|
|
|
it("should report quote", async () => {
|
|
//GIVEN
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/report")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({
|
|
quoteId: "23", //quoteId is string on this endpoint
|
|
quoteLanguage: "english",
|
|
reason: "Inappropriate content",
|
|
comment: "I don't like this.",
|
|
captcha: "captcha",
|
|
});
|
|
//.expect(200);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Quote reported",
|
|
data: null,
|
|
});
|
|
|
|
expect(verifyCaptchaMock).toHaveBeenCalledWith("captcha");
|
|
|
|
expect(createReportMock).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
type: "quote",
|
|
uid,
|
|
contentId: "english-23",
|
|
reason: "Inappropriate content",
|
|
comment: "I don't like this.",
|
|
}),
|
|
10, //configuration maxReport
|
|
20, //configuration contentReportLimit
|
|
);
|
|
});
|
|
|
|
it("should report quote without comment", async () => {
|
|
await mockApp
|
|
.post("/quotes/report")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({
|
|
quoteId: "23", //quoteId is string on this endpoint
|
|
quoteLanguage: "english",
|
|
reason: "Inappropriate content",
|
|
captcha: "captcha",
|
|
})
|
|
.expect(200);
|
|
});
|
|
it("should report quote with empty comment", async () => {
|
|
await mockApp
|
|
.post("/quotes/report")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.send({
|
|
quoteId: "23", //quoteId is string on this endpoint
|
|
quoteLanguage: "english",
|
|
reason: "Inappropriate content",
|
|
comment: "",
|
|
captcha: "captcha",
|
|
})
|
|
.expect(200);
|
|
});
|
|
it("should fail without mandatory properties", async () => {
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/report")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(422);
|
|
|
|
//THEN
|
|
expect(body).toEqual({
|
|
message: "Invalid request data schema",
|
|
validationErrors: [
|
|
'"quoteId" Invalid input',
|
|
'"quoteLanguage" Required',
|
|
'"reason" Required',
|
|
'"captcha" Required',
|
|
],
|
|
});
|
|
});
|
|
it("should fail if feature is disabled", async () => {
|
|
//GIVEN
|
|
enableQuoteReporting(false);
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/report")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(503);
|
|
|
|
//THEN
|
|
expect(body.message).toEqual("Quote reporting is unavailable.");
|
|
});
|
|
it("should fail if user cannot report", async () => {
|
|
//GIVEN
|
|
getPartialUserMock
|
|
.mockClear()
|
|
.mockResolvedValue({ canReport: false } as any);
|
|
|
|
//WHEN
|
|
const { body } = await mockApp
|
|
.post("/quotes/report")
|
|
.set("Authorization", `Bearer ${uid}`)
|
|
.expect(403);
|
|
|
|
//THEN
|
|
expect(body.message).toEqual("You don't have permission to do this.");
|
|
});
|
|
});
|
|
});
|
|
|
|
async function enableQuotes(enabled: boolean): Promise<void> {
|
|
const mockConfig = await configuration;
|
|
mockConfig.quotes = { ...mockConfig.quotes, submissionsEnabled: enabled };
|
|
|
|
vi.spyOn(Configuration, "getCachedConfiguration").mockResolvedValue(
|
|
mockConfig,
|
|
);
|
|
}
|
|
|
|
async function enableQuoteReporting(enabled: boolean): Promise<void> {
|
|
const mockConfig = await configuration;
|
|
mockConfig.quotes.reporting = {
|
|
...mockConfig.quotes.reporting,
|
|
enabled,
|
|
maxReports: 10,
|
|
contentReportLimit: 20,
|
|
};
|
|
|
|
vi.spyOn(Configuration, "getCachedConfiguration").mockResolvedValue(
|
|
mockConfig,
|
|
);
|
|
}
|