From 4254609afbafd2389819e2ebdca56428563a16ee Mon Sep 17 00:00:00 2001 From: syedali237 Date: Mon, 14 Oct 2024 21:43:10 +0530 Subject: [PATCH] added test case for user == null --- eslint.config.mjs | 198 ++++++++++-------- package-lock.json | 15 ++ package.json | 1 + .../Mutation/sendMembershipRequest.ts | 9 +- .../resolvers/Mutation/checkUserNull.spec.ts | 88 ++++++++ 5 files changed, 216 insertions(+), 95 deletions(-) create mode 100644 tests/resolvers/Mutation/checkUserNull.spec.ts diff --git a/eslint.config.mjs b/eslint.config.mjs index 14cfc0893f..f28f832a7e 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,136 +4,160 @@ import _import from "eslint-plugin-import"; import { fixupPluginRules } from "@eslint/compat"; import globals from "globals"; import tsParser from "@typescript-eslint/parser"; -import parser from "@graphql-eslint/eslint-plugin"; import path from "node:path"; import { fileURLToPath } from "node:url"; import js from "@eslint/js"; import { FlatCompat } from "@eslint/eslintrc"; -import graphqlEslint from "@graphql-eslint/eslint-plugin"; +import * as graphqlEslint from "@graphql-eslint/eslint-plugin"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const compat = new FlatCompat({ - baseDirectory: __dirname + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended }); -export default [{ +export default [ + { ignores: [ - "**/.github", - "**/.vscode", - "**/build", - "**/coverage", - "**/node_modules", - "src/types", - "docs/Schema.md", + "**/.github", + "**/.vscode", + "**/build", + "**/coverage", + "**/node_modules", + "src/types", + "docs/Schema.md", ], -}, ...compat.extends("eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"), { + }, + ...compat.extends( + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "prettier", + ), + { plugins: { - "@typescript-eslint": typescriptEslint, - tsdoc, - "import": fixupPluginRules(_import), + "@typescript-eslint": typescriptEslint, + tsdoc, + import: fixupPluginRules(_import), }, languageOptions: { - env: { - node: true, + parser: tsParser, + globals: { + ...globals.node, + }, }, - parser: tsParser, -} -, - rules: { - "no-restricted-imports": ["error", { - patterns: ["**/src/**"], - }], + "no-restricted-imports": [ + "error", + { + patterns: ["**/src/**"], + }, + ], - "import/no-duplicates": "error", - "tsdoc/syntax": "error", - "@typescript-eslint/ban-ts-comment": "error", - "@typescript-eslint/ban-types": "error", - "@typescript-eslint/no-duplicate-enum-values": "error", - "@typescript-eslint/no-explicit-any": "warn", - "@typescript-eslint/no-non-null-asserted-optional-chain": "error", - "@typescript-eslint/no-non-null-assertion": "error", - "@typescript-eslint/no-var-requires": "error", + "import/no-duplicates": "error", + "tsdoc/syntax": "error", + "@typescript-eslint/ban-ts-comment": "error", + // "@typescript-eslint/ban-types": "error", + "@typescript-eslint/no-duplicate-enum-values": "error", + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-non-null-asserted-optional-chain": "error", + "@typescript-eslint/no-non-null-assertion": "error", + "@typescript-eslint/no-var-requires": "error", }, -}, { + }, + { files: ["**/*.ts"], languageOptions: { - parser: tsParser, - ecmaVersion: "latest", - sourceType: "module", + parser: tsParser, + ecmaVersion: "latest", + sourceType: "module", - parserOptions: { - project: "./tsconfig.json", - tsconfigRootDir: ".", - }, + parserOptions: { + project: "./tsconfig.json", + tsconfigRootDir: ".", + }, }, rules: { - "@typescript-eslint/array-type": "error", - "@typescript-eslint/consistent-type-assertions": "error", - "@typescript-eslint/consistent-type-imports": "error", - "@typescript-eslint/explicit-function-return-type": "error", + "@typescript-eslint/array-type": "error", + "@typescript-eslint/consistent-type-assertions": "error", + "@typescript-eslint/consistent-type-imports": "error", + "@typescript-eslint/explicit-function-return-type": "error", - "@typescript-eslint/naming-convention": ["error", { - selector: "interface", - format: ["PascalCase"], - prefix: ["Interface", "TestInterface"], - }, { - selector: ["typeAlias", "typeLike", "enum"], - format: ["PascalCase"], - }, { - selector: "typeParameter", - format: ["PascalCase"], - prefix: ["T"], - }, { - selector: "variable", - format: ["camelCase", "UPPER_CASE"], - leadingUnderscore: "allow", - }, { - selector: "parameter", - format: ["camelCase"], - leadingUnderscore: "allow", - }, { - selector: "function", - format: ["camelCase"], - }, { - selector: "memberLike", - modifiers: ["private"], - format: ["camelCase"], - leadingUnderscore: "require", - }, { - selector: "variable", - modifiers: ["exported"], - format: null, - }], + "@typescript-eslint/naming-convention": [ + "error", + { + selector: "interface", + format: ["PascalCase"], + prefix: ["Interface", "TestInterface"], + }, + { + selector: ["typeAlias", "typeLike", "enum"], + format: ["PascalCase"], + }, + { + selector: "typeParameter", + format: ["PascalCase"], + prefix: ["T"], + }, + { + selector: "variable", + format: ["camelCase", "UPPER_CASE"], + leadingUnderscore: "allow", + }, + { + selector: "parameter", + format: ["camelCase"], + leadingUnderscore: "allow", + }, + { + selector: "function", + format: ["camelCase"], + }, + { + selector: "memberLike", + modifiers: ["private"], + format: ["camelCase"], + leadingUnderscore: "require", + }, + { + selector: "variable", + modifiers: ["exported"], + format: null, + }, + ], }, -}, { + }, + { files: ["./src/typeDefs/**/*.ts"], processor: "@graphql-eslint/graphql", -}, { + }, + { files: ["./src/typeDefs/**/*.graphql"], plugins: { - "@graphql-eslint": graphqlEslint, + "@graphql-eslint": graphqlEslint, }, languageOptions: { - parser: parser, + parser: graphqlEslint.parser, }, -}, { + }, + { files: ["tests/**/*"], rules: { - "no-restricted-imports": "off", + "no-restricted-imports": "off", }, -}, { + }, + { files: ["./src/index.ts", "./src/utilities/copyToClipboard.ts"], rules: { - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-empty-function": "off", }, -}]; \ No newline at end of file + }, +]; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 9882cd9f78..45d8bb3b15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -100,6 +100,7 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-tsdoc": "^0.3.0", "get-graphql-schema": "^2.1.2", + "globals": "^15.11.0", "graphql-markdown": "^7.1.0", "husky": "^9.1.5", "lint-staged": "^15.2.10", @@ -2468,6 +2469,7 @@ "resolved": "https://registry.npmjs.org/@graphql-eslint/eslint-plugin/-/eslint-plugin-3.20.1.tgz", "integrity": "sha512-RbwVlz1gcYG62sECR1u0XqMh8w5e5XMCCZoMvPQ3nJzEBCTfXLGX727GBoRmSvY1x4gJmqNZ1lsOX7lZY14RIw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.18.6", "@graphql-tools/code-file-loader": "^7.3.6", @@ -10156,6 +10158,19 @@ "node": "*" } }, + "node_modules/globals": { + "version": "15.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.11.0.tgz", + "integrity": "sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globalthis": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", diff --git a/package.json b/package.json index 70eded2fa8..29730f6838 100644 --- a/package.json +++ b/package.json @@ -138,6 +138,7 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-tsdoc": "^0.3.0", "get-graphql-schema": "^2.1.2", + "globals": "^15.11.0", "graphql-markdown": "^7.1.0", "husky": "^9.1.5", "lint-staged": "^15.2.10", diff --git a/src/resolvers/Mutation/sendMembershipRequest.ts b/src/resolvers/Mutation/sendMembershipRequest.ts index 1122234111..9b74c49f0b 100644 --- a/src/resolvers/Mutation/sendMembershipRequest.ts +++ b/src/resolvers/Mutation/sendMembershipRequest.ts @@ -103,9 +103,6 @@ export const sendMembershipRequest: MutationResolvers["sendMembershipRequest"] = user: context.userId, organization: organization._id, }); - - // console.log("Membership request already exists:", membershipRequestExists); - if (membershipRequestExists) { // Check if the request is already in the user's document if (!user.membershipRequests.includes(membershipRequestExists._id)) { @@ -154,7 +151,7 @@ export const sendMembershipRequest: MutationResolvers["sendMembershipRequest"] = } // Updating User - const updateResult = await User.findByIdAndUpdate( + await User.findByIdAndUpdate( context.userId, { $push: { @@ -164,9 +161,5 @@ export const sendMembershipRequest: MutationResolvers["sendMembershipRequest"] = { new: true, runValidators: true }, ); - if (!updateResult) { - throw new Error("Failed to update user with membership request"); - } - return createdMembershipRequest.toObject(); }; diff --git a/tests/resolvers/Mutation/checkUserNull.spec.ts b/tests/resolvers/Mutation/checkUserNull.spec.ts new file mode 100644 index 0000000000..7e80b9c469 --- /dev/null +++ b/tests/resolvers/Mutation/checkUserNull.spec.ts @@ -0,0 +1,88 @@ +import type mongoose from "mongoose"; +import { Organization, User } from "../../../src/models"; +import type { MutationSendMembershipRequestArgs } from "../../../src/types/generatedGraphQLTypes"; +import { connect, disconnect } from "../../helpers/db"; +import { nanoid } from "nanoid"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { sendMembershipRequest as sendMembershipRequestResolver } from "../../../src/resolvers/Mutation/sendMembershipRequest"; +import type { + TestOrganizationType, + TestUserType, +} from "../../helpers/userAndOrg"; +import { createTestUser } from "../../helpers/userAndOrg"; +import { Types } from "mongoose"; +import { fail } from "assert"; + +let testUser: TestUserType; +let testOrganization: TestOrganizationType; +let MONGOOSE_INSTANCE: typeof mongoose; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + + testUser = await createTestUser(); + + testOrganization = await Organization.create({ + name: `name${nanoid().toLowerCase()}`, + description: `desc${nanoid().toLowerCase()}`, + isPublic: true, + creatorId: testUser?._id, + members: [testUser?._id], + visibleInSearch: true, + }); + + const { requestContext } = await import("../../../src/libraries"); + vi.spyOn(requestContext, "translate").mockImplementation( + (message) => message, + ); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Mutation -> sendMembershipRequest", () => { + it(`throws NotFoundError if no user exists with _id === context.userId when sending membership request`, async () => { + const args: MutationSendMembershipRequestArgs = { + organizationId: testOrganization?.id, + }; + + const context = { + userId: new Types.ObjectId().toString(), + }; + + // Mock User.findById to return null + vi.spyOn(User, "findById").mockResolvedValueOnce(null); + + try { + await sendMembershipRequestResolver?.({}, args, context); + // If the resolver doesn't throw, fail the test + fail("Expected an error to be thrown"); + } catch (error: unknown) { + expect(error).toBeInstanceOf(Error); + if (error instanceof Error) { + expect(error.message).toMatch(/user not found|user.notFound/i); + + expect(error).toHaveProperty("message"); + + if ("code" in error) expect(error).toHaveProperty("code"); + if ("param" in error) expect(error).toHaveProperty("param"); + + if ("extensions" in error) { + const errorWithExtensions = error as Error & { + extensions?: { code?: string; param?: string }; + }; + expect(errorWithExtensions.extensions).toBeDefined(); + if (errorWithExtensions.extensions) { + if ("code" in errorWithExtensions.extensions) + expect(errorWithExtensions.extensions).toHaveProperty("code"); + if ("param" in errorWithExtensions.extensions) + expect(errorWithExtensions.extensions).toHaveProperty("param"); + } + } + } else { + fail("Expected error to be an instance of Error"); + } + } + }); +});