From 852bbbb4b2888fd2f001ca52a3fd9992981de9d2 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Thu, 26 Jan 2023 20:54:00 +0530 Subject: [PATCH 1/6] createPost test fixes --- tests/resolvers/Mutation/createPost.spec.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/resolvers/Mutation/createPost.spec.ts b/tests/resolvers/Mutation/createPost.spec.ts index e9d48abd1e..c445b9d86b 100644 --- a/tests/resolvers/Mutation/createPost.spec.ts +++ b/tests/resolvers/Mutation/createPost.spec.ts @@ -6,6 +6,10 @@ import { Organization, Interface_Organization, } from "../../../src/models"; +import { + USER_NOT_FOUND_MESSAGE, + ORGANIZATION_NOT_FOUND_MESSAGE, +} from "../../../src/constants"; import { MutationCreatePostArgs } from "../../../src/types/generatedGraphQLTypes"; import { connect, disconnect } from "../../../src/db"; import { createPost as createPostResolver } from "../../../src/resolvers/Mutation/createPost"; @@ -74,7 +78,7 @@ describe("resolvers -> Mutation -> createPost", () => { await createPostResolver?.({}, args, context); } catch (error: any) { - expect(error.message).toEqual(USER_NOT_FOUND); + expect(error.message).toEqual(USER_NOT_FOUND || USER_NOT_FOUND_MESSAGE); } }); @@ -95,7 +99,9 @@ describe("resolvers -> Mutation -> createPost", () => { await createPostResolver?.({}, args, context); } catch (error: any) { - expect(error.message).toEqual(ORGANIZATION_NOT_FOUND); + expect(error.message).toEqual( + ORGANIZATION_NOT_FOUND || ORGANIZATION_NOT_FOUND_MESSAGE + ); } }); From 0d03075b65bfe806fdcd7b8f0494f42b58ce8128 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Fri, 27 Jan 2023 01:39:54 +0530 Subject: [PATCH 2/6] 100% coverage for createPost --- tests/resolvers/Mutation/createPost.spec.ts | 202 +++++++++++++++++++- 1 file changed, 192 insertions(+), 10 deletions(-) diff --git a/tests/resolvers/Mutation/createPost.spec.ts b/tests/resolvers/Mutation/createPost.spec.ts index c445b9d86b..fb6b110213 100644 --- a/tests/resolvers/Mutation/createPost.spec.ts +++ b/tests/resolvers/Mutation/createPost.spec.ts @@ -6,16 +6,25 @@ import { Organization, Interface_Organization, } from "../../../src/models"; +import { MutationCreatePostArgs } from "../../../src/types/generatedGraphQLTypes"; +import { connect, disconnect } from "../../../src/db"; import { + ORGANIZATION_NOT_FOUND, + USER_NOT_FOUND, USER_NOT_FOUND_MESSAGE, ORGANIZATION_NOT_FOUND_MESSAGE, } from "../../../src/constants"; -import { MutationCreatePostArgs } from "../../../src/types/generatedGraphQLTypes"; -import { connect, disconnect } from "../../../src/db"; -import { createPost as createPostResolver } from "../../../src/resolvers/Mutation/createPost"; -import { ORGANIZATION_NOT_FOUND, USER_NOT_FOUND } from "../../../src/constants"; import { nanoid } from "nanoid"; -import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import shortid from "shortid"; +import { + beforeAll, + afterAll, + describe, + it, + expect, + afterEach, + vi, +} from "vitest"; let testUser: Interface_User & Document; let testOrganization: Interface_Organization & @@ -54,14 +63,60 @@ beforeAll(async () => { }, } ); + + vi.spyOn(shortid, "generate").mockReturnValue("23TplPdS"); }); afterAll(async () => { + vi.spyOn(shortid, "generate").mockRestore(); await disconnect(); }); describe("resolvers -> Mutation -> createPost", () => { - it(`throws NotFoundError if no user exists with _id === context.userId`, async () => { + afterEach(() => { + vi.doUnmock("../../../src/constants"); + vi.resetModules(); + }); + it(`throws NotFoundError if no user exists with _id === context.userId and IN_PRODUCTION === false`, async () => { + try { + const args: MutationCreatePostArgs = { + data: { + organizationId: "", + text: "", + videoUrl: "", + title: "", + }, + }; + + const context = { + userId: Types.ObjectId().toString(), + }; + + vi.doMock("../../../src/constants", async () => { + const actualConstants: object = await vi.importActual( + "../../../src/constants" + ); + return { + ...actualConstants, + IN_PRODUCTION: false, + }; + }); + + const { createPost: createPostResolver } = await import( + "../../../src/resolvers/Mutation/createPost" + ); + + await createPostResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual(USER_NOT_FOUND); + } + }); + + it(`throws NotFoundError if no user exists with _id === context.userId and IN_PRODUCTION === true`, async () => { + const { requestContext } = await import("../../../src/libraries"); + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); try { const args: MutationCreatePostArgs = { data: { @@ -76,13 +131,67 @@ describe("resolvers -> Mutation -> createPost", () => { userId: Types.ObjectId().toString(), }; + vi.doMock("../../../src/constants", async () => { + const actualConstants: object = await vi.importActual( + "../../../src/constants" + ); + return { + ...actualConstants, + IN_PRODUCTION: true, + }; + }); + + const { createPost: createPostResolver } = await import( + "../../../src/resolvers/Mutation/createPost" + ); + + await createPostResolver?.({}, args, context); + } catch (error: any) { + expect(spy).toBeCalledWith(USER_NOT_FOUND_MESSAGE); + expect(error.message).toEqual(`Translated ${USER_NOT_FOUND_MESSAGE}`); + } + }); + + it(`throws NotFoundError if no organization exists with _id === args.data.organizationId and IN_PRODUCTION === false`, async () => { + try { + const args: MutationCreatePostArgs = { + data: { + organizationId: Types.ObjectId().toString(), + text: "", + videoUrl: "", + title: "", + }, + }; + + const context = { + userId: testUser.id, + }; + + vi.doMock("../../../src/constants", async () => { + const actualConstants: object = await vi.importActual( + "../../../src/constants" + ); + return { + ...actualConstants, + IN_PRODUCTION: false, + }; + }); + + const { createPost: createPostResolver } = await import( + "../../../src/resolvers/Mutation/createPost" + ); + await createPostResolver?.({}, args, context); } catch (error: any) { - expect(error.message).toEqual(USER_NOT_FOUND || USER_NOT_FOUND_MESSAGE); + expect(error.message).toEqual(ORGANIZATION_NOT_FOUND); } }); - it(`throws NotFoundError if no organization exists with _id === args.data.organizationId`, async () => { + it(`throws NotFoundError if no organization exists with _id === args.data.organizationId and IN_PRODUCTION === true`, async () => { + const { requestContext } = await import("../../../src/libraries"); + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); try { const args: MutationCreatePostArgs = { data: { @@ -90,6 +199,7 @@ describe("resolvers -> Mutation -> createPost", () => { text: "", videoUrl: "", title: "", + imageUrl: "", }, }; @@ -97,15 +207,80 @@ describe("resolvers -> Mutation -> createPost", () => { userId: testUser.id, }; + vi.doMock("../../../src/constants", async () => { + const actualConstants: object = await vi.importActual( + "../../../src/constants" + ); + return { + ...actualConstants, + IN_PRODUCTION: true, + }; + }); + + const { createPost: createPostResolver } = await import( + "../../../src/resolvers/Mutation/createPost" + ); + await createPostResolver?.({}, args, context); } catch (error: any) { + expect(spy).toBeCalledWith(ORGANIZATION_NOT_FOUND_MESSAGE); expect(error.message).toEqual( - ORGANIZATION_NOT_FOUND || ORGANIZATION_NOT_FOUND_MESSAGE + `Translated ${ORGANIZATION_NOT_FOUND_MESSAGE}` ); } }); - it(`creates the post and returns it`, async () => { + it(`creates the post and returns it when image is not provided`, async () => { + const args: MutationCreatePostArgs = { + data: { + organizationId: testOrganization.id, + text: "text", + videoUrl: "videoUrl", + title: "title", + }, + }; + + const context = { + userId: testUser.id, + }; + + const { createPost: createPostResolver } = await import( + "../../../src/resolvers/Mutation/createPost" + ); + + const createPostPayload = await createPostResolver?.({}, args, context); + + expect(createPostPayload).toEqual( + expect.objectContaining({ + title: "title", + videoUrl: "videoUrl", + creator: testUser._id, + organization: testOrganization._id, + imageUrl: "", + }) + ); + }); + it(`creates the post and returns it when image is provided`, async () => { + const utilities = await import("../../../src/utilities"); + + const newImageFile = { + filename: "testImage.png", + createReadStream: {}, + }; + + const id = shortid.generate(); + + const returnImageFile = { + newImagePath: `images/${id}-${newImageFile.filename}`, + imageAlreadyInDbPath: "", + }; + + const uploadImageSpy = vi + .spyOn(utilities, "uploadImage") + .mockImplementation(() => { + return Promise.resolve(returnImageFile); + }); + const args: MutationCreatePostArgs = { data: { organizationId: testOrganization.id, @@ -113,20 +288,27 @@ describe("resolvers -> Mutation -> createPost", () => { videoUrl: "videoUrl", title: "title", }, + file: newImageFile, }; const context = { userId: testUser.id, }; + const { createPost: createPostResolver } = await import( + "../../../src/resolvers/Mutation/createPost" + ); + const createPostPayload = await createPostResolver?.({}, args, context); + expect(uploadImageSpy).toBeCalledWith(newImageFile, ""); expect(createPostPayload).toEqual( expect.objectContaining({ title: "title", videoUrl: "videoUrl", creator: testUser._id, organization: testOrganization._id, + imageUrl: returnImageFile.newImagePath, }) ); }); From f3820cfb11c6a135dc6b69408e47ce5452f2ca21 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Fri, 27 Jan 2023 02:48:17 +0530 Subject: [PATCH 3/6] 100% test coverage for createPost.ts --- tests/resolvers/Mutation/createPost.spec.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/resolvers/Mutation/createPost.spec.ts b/tests/resolvers/Mutation/createPost.spec.ts index fb6b110213..1fa2c11c6b 100644 --- a/tests/resolvers/Mutation/createPost.spec.ts +++ b/tests/resolvers/Mutation/createPost.spec.ts @@ -15,7 +15,6 @@ import { ORGANIZATION_NOT_FOUND_MESSAGE, } from "../../../src/constants"; import { nanoid } from "nanoid"; -import shortid from "shortid"; import { beforeAll, afterAll, @@ -63,12 +62,9 @@ beforeAll(async () => { }, } ); - - vi.spyOn(shortid, "generate").mockReturnValue("23TplPdS"); }); afterAll(async () => { - vi.spyOn(shortid, "generate").mockRestore(); await disconnect(); }); @@ -268,10 +264,8 @@ describe("resolvers -> Mutation -> createPost", () => { createReadStream: {}, }; - const id = shortid.generate(); - const returnImageFile = { - newImagePath: `images/${id}-${newImageFile.filename}`, + newImagePath: "/testImage", imageAlreadyInDbPath: "", }; From 9ddf11e77c1f519f308e3df72bde4ba1ccc3046b Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Sat, 28 Jan 2023 17:00:45 +0530 Subject: [PATCH 4/6] 100% coverage for refreshToken --- tests/resolvers/Mutation/refreshToken.spec.ts | 186 +++++++++++++++++- 1 file changed, 179 insertions(+), 7 deletions(-) diff --git a/tests/resolvers/Mutation/refreshToken.spec.ts b/tests/resolvers/Mutation/refreshToken.spec.ts index 3eb04633b2..1c05d68d76 100644 --- a/tests/resolvers/Mutation/refreshToken.spec.ts +++ b/tests/resolvers/Mutation/refreshToken.spec.ts @@ -3,11 +3,18 @@ import { Document, Types } from "mongoose"; import { Interface_User, User } from "../../../src/models"; import { MutationRefreshTokenArgs } from "../../../src/types/generatedGraphQLTypes"; import { connect, disconnect } from "../../../src/db"; -import { refreshToken as refreshTokenResolver } from "../../../src/resolvers/Mutation/refreshToken"; -import { USER_NOT_FOUND } from "../../../src/constants"; +import { USER_NOT_FOUND, USER_NOT_FOUND_MESSAGE } from "../../../src/constants"; import { createRefreshToken } from "../../../src/utilities"; import { nanoid } from "nanoid"; -import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import { + beforeAll, + afterAll, + describe, + it, + expect, + vi, + afterEach, +} from "vitest"; let testUser: Interface_User & Document; let refreshToken: string; @@ -29,20 +36,69 @@ afterAll(async () => { }); describe("resolvers -> Mutation -> refreshToken", () => { - it(`throws NotFoundError if no refreshToken is provided using args.refreshToken`, async () => { + afterEach(() => { + vi.doUnmock("../../../src/constants"); + vi.resetModules(); + }); + it(`throws NotFoundError if no refreshToken is provided using args.refreshToken and IN_PRODUCTION === false`, async () => { try { const args: MutationRefreshTokenArgs = { refreshToken: "", }; + vi.doMock("../../../src/constants", async () => { + const actualConstants: object = await vi.importActual( + "../../../src/constants" + ); + return { + ...actualConstants, + IN_PRODUCTION: false, + }; + }); + + const { refreshToken: refreshTokenResolver } = await import( + "../../../src/resolvers/Mutation/refreshToken" + ); + await refreshTokenResolver?.({}, args, {}); } catch (error: any) { expect(error.message).toEqual("Invalid refreshToken"); } }); + it(`throws NotFoundError if no refreshToken is provided using args.refreshToken and IN_PRODUCTION === true`, async () => { + const { requestContext } = await import("../../../src/libraries"); + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementation((message) => `Translated ${message}`); + try { + const args: MutationRefreshTokenArgs = { + refreshToken: "", + }; + + vi.doMock("../../../src/constants", async () => { + const actualConstants: object = await vi.importActual( + "../../../src/constants" + ); + return { + ...actualConstants, + IN_PRODUCTION: true, + }; + }); + + const { refreshToken: refreshTokenResolver } = await import( + "../../../src/resolvers/Mutation/refreshToken" + ); + + await refreshTokenResolver?.({}, args, {}); + } catch (error: any) { + expect(spy).toBeCalledWith("invalid.refreshToken"); + expect(error.message).toEqual("Translated invalid.refreshToken"); + } + }); + it(`throws NotFoundError if no user exists with _id === payload.userId for - args.refreshToken`, async () => { + args.refreshToken and IN_PRODUCTION === false`, async () => { try { refreshToken = await createRefreshToken({ ...testUser.toObject(), @@ -53,14 +109,67 @@ describe("resolvers -> Mutation -> refreshToken", () => { refreshToken, }; + const { refreshToken: refreshTokenResolver } = await import( + "../../../src/resolvers/Mutation/refreshToken" + ); + + vi.doMock("../../../src/constants", async () => { + const actualConstants: object = await vi.importActual( + "../../../src/constants" + ); + return { + ...actualConstants, + IN_PRODUCTION: false, + }; + }); + await refreshTokenResolver?.({}, args, {}); } catch (error: any) { expect(error.message).toEqual(USER_NOT_FOUND); } }); + it(`throws NotFoundError if no user exists with _id === payload.userId for + args.refreshToken and IN_PRODUCTION === true`, async () => { + const { requestContext } = await import("../../../src/libraries"); + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementation((message) => `Translated ${message}`); + try { + refreshToken = await createRefreshToken({ + ...testUser.toObject(), + _id: Types.ObjectId(), + }); + + const args: MutationRefreshTokenArgs = { + refreshToken, + }; + + vi.doMock("../../../src/constants", async () => { + const actualConstants: object = await vi.importActual( + "../../../src/constants" + ); + return { + ...actualConstants, + IN_PRODUCTION: true, + }; + }); + + const { refreshToken: refreshTokenResolver } = await import( + "../../../src/resolvers/Mutation/refreshToken" + ); + + await refreshTokenResolver?.({}, args, {}); + } catch (error: any) { + expect(spy).toBeCalledWith(USER_NOT_FOUND_MESSAGE); + expect(error.message).toEqual(`Translated ${USER_NOT_FOUND_MESSAGE}`); + + spy.mockRestore(); + } + }); + it(`throws ValidationError if user.tokenVersion !== payload.tokenVersion for user - with _id === payload.userId for args.refreshToken`, async () => { + with _id === payload.userId for args.refreshToken and IN_PRODUCTION === false`, async () => { try { await User.updateOne( { @@ -79,12 +188,71 @@ describe("resolvers -> Mutation -> refreshToken", () => { refreshToken, }; + vi.doMock("../../../src/constants", async () => { + const actualConstants: object = await vi.importActual( + "../../../src/constants" + ); + return { + ...actualConstants, + IN_PRODUCTION: false, + }; + }); + + const { refreshToken: refreshTokenResolver } = await import( + "../../../src/resolvers/Mutation/refreshToken" + ); + await refreshTokenResolver?.({}, args, {}); } catch (error: any) { expect(error.message).toEqual("Invalid refreshToken"); } }); + it(`throws ValidationError if user.tokenVersion !== payload.tokenVersion for user + with _id === payload.userId for args.refreshToken and IN_PRODUCTION === true`, async () => { + const { requestContext } = await import("../../../src/libraries"); + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementation((message) => `Translated ${message}`); + try { + await User.updateOne( + { + _id: testUser._id, + }, + { + $inc: { + tokenVersion: 1, + }, + } + ); + + refreshToken = await createRefreshToken(testUser.toObject()); + + const args: MutationRefreshTokenArgs = { + refreshToken, + }; + + vi.doMock("../../../src/constants", async () => { + const actualConstants: object = await vi.importActual( + "../../../src/constants" + ); + return { + ...actualConstants, + IN_PRODUCTION: true, + }; + }); + + const { refreshToken: refreshTokenResolver } = await import( + "../../../src/resolvers/Mutation/refreshToken" + ); + + await refreshTokenResolver?.({}, args, {}); + } catch (error: any) { + expect(spy).toBeCalledWith("invalid.refreshToken"); + expect(error.message).toEqual("Translated invalid.refreshToken"); + } + }); + it(`generates new accessToken and refreshToken and returns them`, async () => { await User.updateOne( { @@ -92,7 +260,7 @@ describe("resolvers -> Mutation -> refreshToken", () => { }, { $inc: { - tokenVersion: -1, + tokenVersion: -2, }, } ); @@ -103,6 +271,10 @@ describe("resolvers -> Mutation -> refreshToken", () => { refreshToken, }; + const { refreshToken: refreshTokenResolver } = await import( + "../../../src/resolvers/Mutation/refreshToken" + ); + const refreshTokenPayload = await refreshTokenResolver?.({}, args, {}); expect(typeof refreshTokenPayload?.accessToken).toEqual("string"); From 89d1a5cc40c57b346d09bd711283d30d19482af3 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Sat, 28 Jan 2023 22:56:06 +0530 Subject: [PATCH 5/6] 100% coverage for resolvers/index --- tests/resolvers/index.spec.ts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/resolvers/index.spec.ts diff --git a/tests/resolvers/index.spec.ts b/tests/resolvers/index.spec.ts new file mode 100644 index 0000000000..f6e1691082 --- /dev/null +++ b/tests/resolvers/index.spec.ts @@ -0,0 +1,34 @@ +import { resolvers } from "../../src/resolvers/index"; +import { Resolvers } from "../../src/types/generatedGraphQLTypes"; +import { DirectChat } from "../../src/resolvers/DirectChat"; +import { DirectChatMessage } from "../../src/resolvers/DirectChatMessage"; +import { GroupChat } from "../../src/resolvers/GroupChat"; +import { GroupChatMessage } from "../../src/resolvers/GroupChatMessage"; +import { MembershipRequest } from "../../src/resolvers/MembershipRequest"; +import { Mutation } from "../../src/resolvers/Mutation"; +import { Organization } from "../../src/resolvers/Organization"; +import { Query } from "../../src/resolvers/Query"; +import { Subscription } from "../../src/resolvers/Subscription"; +import { describe, it, beforeAll, expect } from "vitest"; + +let testResolver: Resolvers; + +beforeAll(() => { + testResolver = { + DirectChat, + DirectChatMessage, + GroupChat, + GroupChatMessage, + MembershipRequest, + Mutation, + Organization, + Query, + Subscription, + }; +}); + +describe("resolvers -> index", () => { + it("creates resolvers", () => { + expect(testResolver).toStrictEqual(resolvers); + }); +}); From 47cbd3cf1d54ad233f265269e54e4008a97541e0 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Sun, 29 Jan 2023 03:08:01 +0530 Subject: [PATCH 6/6] initializeApp check --- src/resolvers/Mutation/createEvent.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/resolvers/Mutation/createEvent.ts b/src/resolvers/Mutation/createEvent.ts index f9b91f612d..b06392e512 100644 --- a/src/resolvers/Mutation/createEvent.ts +++ b/src/resolvers/Mutation/createEvent.ts @@ -17,10 +17,13 @@ import { ORGANIZATION_NOT_AUTHORIZED_PARAM, } from "../../constants"; import admin, { credential } from "firebase-admin"; +import { getApps } from "firebase-admin/app"; const applicationDefault = credential.applicationDefault; -admin.initializeApp({ credential: applicationDefault() }); +getApps().length === 0 + ? admin.initializeApp({ credential: applicationDefault() }) + : getApps(); export const createEvent: MutationResolvers["createEvent"] = async ( _parent,