diff --git a/schema.graphql b/schema.graphql index c5a1c8d502..c55d48ef34 100644 --- a/schema.graphql +++ b/schema.graphql @@ -1514,6 +1514,8 @@ type Query { getNoteById(id: ID!): Note! getPledgesByUserId(orderBy: PledgeOrderByInput, userId: ID!, where: PledgeWhereInput): [FundraisingCampaignPledge] getPlugins: [Plugin] + getUserTag(id: ID!): UserTag + getUserTagAncestors(id: ID!): [UserTag] getVenueByOrgId(first: Int, orderBy: VenueOrderByInput, orgId: ID!, skip: Int, where: VenueWhereInput): [Venue] getlanguage(lang_code: String!): [Translation] groupChatById(id: ID!): GroupChat @@ -1799,9 +1801,9 @@ input UpdateUserPasswordInput { } input UpdateUserTagInput { - _id: ID! name: String! - tagColor: String! + tagColor: String + tagId: ID! } scalar Upload @@ -1939,7 +1941,7 @@ type UserTag { type UserTagsConnection { edges: [UserTagsConnectionEdge!]! pageInfo: DefaultConnectionPageInfo! - totalCount: PositiveInt + totalCount: Int } """A default connection edge on the UserTag type for UserTagsConnection.""" @@ -1987,6 +1989,7 @@ input UserWhereInput { type UsersConnection { edges: [UsersConnectionEdge!]! pageInfo: DefaultConnectionPageInfo! + totalCount: Int } """A default connection edge on the User type for UsersConnection.""" diff --git a/src/resolvers/Mutation/assignUserTag.ts b/src/resolvers/Mutation/assignUserTag.ts index 37d4628d3c..ee2186642d 100644 --- a/src/resolvers/Mutation/assignUserTag.ts +++ b/src/resolvers/Mutation/assignUserTag.ts @@ -152,10 +152,32 @@ export const assignUserTag: MutationResolvers["assignUserTag"] = async ( ); } - // Assign the tag - await TagUser.create({ - ...args.input, - }); + // assign all the ancestor tags + const allAncestorTags = [tag._id]; + let currentTag = tag; + while (currentTag?.parentTagId) { + const currentParentTag = await OrganizationTagUser.findOne({ + _id: currentTag.parentTagId, + }).lean(); + + if (currentParentTag) { + allAncestorTags.push(currentParentTag?._id); + currentTag = currentParentTag; + } + } + + const assigneeId = args.input.userId; + + const tagUserDocs = allAncestorTags.map((tagId) => ({ + updateOne: { + filter: { userId: assigneeId, tagId }, + update: { $setOnInsert: { userId: assigneeId, tagId } }, + upsert: true, + setDefaultsOnInsert: true, + }, + })); + + await TagUser.bulkWrite(tagUserDocs); return requestUser; }; diff --git a/src/resolvers/Mutation/unassignUserTag.ts b/src/resolvers/Mutation/unassignUserTag.ts index 120da46f15..cd19f6d891 100644 --- a/src/resolvers/Mutation/unassignUserTag.ts +++ b/src/resolvers/Mutation/unassignUserTag.ts @@ -145,9 +145,36 @@ export const unassignUserTag: MutationResolvers["unassignUserTag"] = async ( ); } + // Get all the child tags of the current tag (including itself) + // on the OrganizationTagUser model + // The following implementation makes number of queries = max depth of nesting in the tag provided + let allTagIds: string[] = []; + let currentParents = [tag._id.toString()]; + + while (currentParents.length) { + allTagIds = allTagIds.concat(currentParents); + const foundTags = await OrganizationTagUser.find( + { + organizationId: tag.organizationId, + parentTagId: { + $in: currentParents, + }, + }, + { + _id: 1, + }, + ); + currentParents = foundTags + .map((tag) => tag._id.toString()) + .filter((id: string | null) => id); + } + // Unassign the tag - await TagUser.deleteOne({ - ...args.input, + await TagUser.deleteMany({ + tagId: { + $in: allTagIds, + }, + userId: args.input.userId, }); return requestUser; diff --git a/src/resolvers/Mutation/updateUserTag.ts b/src/resolvers/Mutation/updateUserTag.ts index 05513c0716..794ca3fb15 100644 --- a/src/resolvers/Mutation/updateUserTag.ts +++ b/src/resolvers/Mutation/updateUserTag.ts @@ -74,7 +74,7 @@ export const updateUserTag: MutationResolvers["updateUserTag"] = async ( // Get the tag object const existingTag = await OrganizationTagUser.findOne({ - _id: args.input._id, + _id: args.input.tagId, }).lean(); if (!existingTag) { @@ -130,7 +130,7 @@ export const updateUserTag: MutationResolvers["updateUserTag"] = async ( // Update the title of the tag and return it return await OrganizationTagUser.findOneAndUpdate( { - _id: args.input._id, + _id: args.input.tagId, }, { name: args.input.name, diff --git a/src/resolvers/Organization/userTags.ts b/src/resolvers/Organization/userTags.ts index 6ccdf71d5b..2b4160cf36 100644 --- a/src/resolvers/Organization/userTags.ts +++ b/src/resolvers/Organization/userTags.ts @@ -72,6 +72,7 @@ export const userTags: OrganizationResolvers["userTags"] = async ( OrganizationTagUser.find({ ...filter, organizationId: parent._id, + parentTagId: null, }) .sort(sort) .limit(parsedArgs.limit) diff --git a/src/resolvers/Query/getUserTag.ts b/src/resolvers/Query/getUserTag.ts new file mode 100644 index 0000000000..cb6e5cfa1c --- /dev/null +++ b/src/resolvers/Query/getUserTag.ts @@ -0,0 +1,21 @@ +import { OrganizationTagUser } from "../../models"; +import { errors, requestContext } from "../../libraries"; +import type { QueryResolvers } from "../../types/generatedGraphQLTypes"; +import { TAG_NOT_FOUND } from "../../constants"; + +export const getUserTag: QueryResolvers["getUserTag"] = async ( + _parent, + args, +) => { + const userTag = await OrganizationTagUser.findById(args.id).lean(); + + if (!userTag) { + throw new errors.NotFoundError( + requestContext.translate(TAG_NOT_FOUND.MESSAGE), + TAG_NOT_FOUND.CODE, + TAG_NOT_FOUND.PARAM, + ); + } + + return userTag; +}; diff --git a/src/resolvers/Query/getUserTagAncestors.ts b/src/resolvers/Query/getUserTagAncestors.ts new file mode 100644 index 0000000000..eb4b3fcb27 --- /dev/null +++ b/src/resolvers/Query/getUserTagAncestors.ts @@ -0,0 +1,32 @@ +import { OrganizationTagUser } from "../../models"; +import { errors, requestContext } from "../../libraries"; +import type { QueryResolvers } from "../../types/generatedGraphQLTypes"; +import { TAG_NOT_FOUND } from "../../constants"; + +export const getUserTagAncestors: QueryResolvers["getUserTagAncestors"] = + async (_parent, args) => { + let currentTag = await OrganizationTagUser.findById(args.id).lean(); + + const tagAncestors = [currentTag]; + + while (currentTag?.parentTagId) { + const currentParent = await OrganizationTagUser.findById( + currentTag.parentTagId, + ).lean(); + + if (currentParent) { + tagAncestors.push(currentParent); + currentTag = currentParent; + } + } + + if (!currentTag) { + throw new errors.NotFoundError( + requestContext.translate(TAG_NOT_FOUND.MESSAGE), + TAG_NOT_FOUND.CODE, + TAG_NOT_FOUND.PARAM, + ); + } + + return tagAncestors.reverse(); + }; diff --git a/src/resolvers/Query/index.ts b/src/resolvers/Query/index.ts index 8b1410ad35..fc878303d8 100644 --- a/src/resolvers/Query/index.ts +++ b/src/resolvers/Query/index.ts @@ -34,6 +34,8 @@ import { getFundraisingCampaigns } from "./getFundraisingCampaigns"; import { getPledgesByUserId } from "./getPledgesByUserId"; import { getPlugins } from "./getPlugins"; import { getlanguage } from "./getlanguage"; +import { getUserTag } from "./getUserTag"; +import { getUserTagAncestors } from "./getUserTagAncestors"; import { me } from "./me"; import { myLanguage } from "./myLanguage"; import { organizations } from "./organizations"; @@ -84,6 +86,8 @@ export const Query: QueryResolvers = { getNoteById, getlanguage, getPlugins, + getUserTag, + getUserTagAncestors, isSampleOrganization, me, myLanguage, diff --git a/src/typeDefs/inputs.ts b/src/typeDefs/inputs.ts index ac5a99320c..043eaacfa3 100644 --- a/src/typeDefs/inputs.ts +++ b/src/typeDefs/inputs.ts @@ -490,8 +490,8 @@ export const inputs = gql` } input UpdateUserTagInput { - _id: ID! - tagColor: String! + tagId: ID! + tagColor: String name: String! } diff --git a/src/typeDefs/queries.ts b/src/typeDefs/queries.ts index bea4e3a501..f1a51bca0a 100644 --- a/src/typeDefs/queries.ts +++ b/src/typeDefs/queries.ts @@ -125,6 +125,10 @@ export const queries = gql` getNoteById(id: ID!): Note! + getUserTag(id: ID!): UserTag + + getUserTagAncestors(id: ID!): [UserTag] + getAllNotesForAgendaItem(agendaItemId: ID!): [Note] advertisementsConnection( diff --git a/src/typeDefs/types.ts b/src/typeDefs/types.ts index 76796c96f0..16fc7783f8 100644 --- a/src/typeDefs/types.ts +++ b/src/typeDefs/types.ts @@ -742,7 +742,7 @@ export const types = gql` type UserTagsConnection { edges: [UserTagsConnectionEdge!]! pageInfo: DefaultConnectionPageInfo! - totalCount: PositiveInt + totalCount: Int } """ @@ -759,6 +759,7 @@ export const types = gql` type UsersConnection { edges: [UsersConnectionEdge!]! pageInfo: DefaultConnectionPageInfo! + totalCount: Int } """ diff --git a/src/types/generatedGraphQLTypes.ts b/src/types/generatedGraphQLTypes.ts index 33399964aa..bc7cb68800 100644 --- a/src/types/generatedGraphQLTypes.ts +++ b/src/types/generatedGraphQLTypes.ts @@ -2306,6 +2306,8 @@ export type Query = { getNoteById: Note; getPledgesByUserId?: Maybe>>; getPlugins?: Maybe>>; + getUserTag?: Maybe; + getUserTagAncestors?: Maybe>>; getVenueByOrgId?: Maybe>>; getlanguage?: Maybe>>; groupChatById?: Maybe; @@ -2521,6 +2523,16 @@ export type QueryGetPledgesByUserIdArgs = { }; +export type QueryGetUserTagArgs = { + id: Scalars['ID']['input']; +}; + + +export type QueryGetUserTagAncestorsArgs = { + id: Scalars['ID']['input']; +}; + + export type QueryGetVenueByOrgIdArgs = { first?: InputMaybe; orderBy?: InputMaybe; @@ -2908,9 +2920,9 @@ export type UpdateUserPasswordInput = { }; export type UpdateUserTagInput = { - _id: Scalars['ID']['input']; name: Scalars['String']['input']; - tagColor: Scalars['String']['input']; + tagColor?: InputMaybe; + tagId: Scalars['ID']['input']; }; export type User = { @@ -3084,7 +3096,7 @@ export type UserTagsConnection = { __typename?: 'UserTagsConnection'; edges: Array; pageInfo: DefaultConnectionPageInfo; - totalCount?: Maybe; + totalCount?: Maybe; }; /** A default connection edge on the UserTag type for UserTagsConnection. */ @@ -3133,6 +3145,7 @@ export type UsersConnection = { __typename?: 'UsersConnection'; edges: Array; pageInfo: DefaultConnectionPageInfo; + totalCount?: Maybe; }; /** A default connection edge on the User type for UsersConnection. */ @@ -4600,6 +4613,8 @@ export type QueryResolvers>; getPledgesByUserId?: Resolver>>, ParentType, ContextType, RequireFields>; getPlugins?: Resolver>>, ParentType, ContextType>; + getUserTag?: Resolver, ParentType, ContextType, RequireFields>; + getUserTagAncestors?: Resolver>>, ParentType, ContextType, RequireFields>; getVenueByOrgId?: Resolver>>, ParentType, ContextType, RequireFields>; getlanguage?: Resolver>>, ParentType, ContextType, RequireFields>; groupChatById?: Resolver, ParentType, ContextType, RequireFields>; @@ -4784,7 +4799,7 @@ export type UserTagResolvers = { edges?: Resolver, ParentType, ContextType>; pageInfo?: Resolver; - totalCount?: Resolver, ParentType, ContextType>; + totalCount?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -4797,6 +4812,7 @@ export type UserTagsConnectionEdgeResolvers = { edges?: Resolver, ParentType, ContextType>; pageInfo?: Resolver; + totalCount?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; diff --git a/tests/resolvers/Mutation/assignUserTag.spec.ts b/tests/resolvers/Mutation/assignUserTag.spec.ts index 2b5d428144..92281363d2 100644 --- a/tests/resolvers/Mutation/assignUserTag.spec.ts +++ b/tests/resolvers/Mutation/assignUserTag.spec.ts @@ -22,19 +22,26 @@ import { } from "../../../src/constants"; import { AppUserProfile, TagUser } from "../../../src/models"; import type { TestUserTagType } from "../../helpers/tags"; -import { createRootTagWithOrg } from "../../helpers/tags"; +import { + createRootTagWithOrg, + createTwoLevelTagsWithOrg, +} from "../../helpers/tags"; import type { TestUserType } from "../../helpers/userAndOrg"; import { createTestUser } from "../../helpers/userAndOrg"; let MONGOOSE_INSTANCE: typeof mongoose; let adminUser: TestUserType; +let adminUser2: TestUserType; +let testTag2: TestUserTagType; let testTag: TestUserTagType; +let testSubTag1: TestUserTagType; let randomUser: TestUserType; beforeAll(async () => { MONGOOSE_INSTANCE = await connect(); - [adminUser, , testTag] = await createRootTagWithOrg(); + [adminUser, , [testTag, testSubTag1]] = await createTwoLevelTagsWithOrg(); + [adminUser2, , testTag2] = await createRootTagWithOrg(); randomUser = await createTestUser(); }); @@ -210,10 +217,36 @@ describe("resolvers -> Mutation -> assignUserTag", () => { }); it(`Tag assign should be successful and the user who has been assigned the tag is returned`, async () => { + const args: MutationAssignUserTagArgs = { + input: { + userId: adminUser2?._id, + tagId: testTag2?._id.toString() ?? "", + }, + }; + const context = { + userId: adminUser2?._id, + }; + + const { assignUserTag: assignUserTagResolver } = await import( + "../../../src/resolvers/Mutation/assignUserTag" + ); + + const payload = await assignUserTagResolver?.({}, args, context); + + expect(payload?._id.toString()).toEqual(adminUser2?._id.toString()); + + const tagAssigned = await TagUser.exists({ + ...args.input, + }); + + expect(tagAssigned).toBeTruthy(); + }); + + it(`Should assign all the ancestor tags and returns the user that is assigned`, async () => { const args: MutationAssignUserTagArgs = { input: { userId: adminUser?._id, - tagId: testTag?._id.toString() ?? "", + tagId: testSubTag1?._id.toString() ?? "", }, }; const context = { @@ -228,11 +261,17 @@ describe("resolvers -> Mutation -> assignUserTag", () => { expect(payload?._id.toString()).toEqual(adminUser?._id.toString()); - const tagAssigned = await TagUser.exists({ + const subTagAssigned = await TagUser.exists({ ...args.input, }); - expect(tagAssigned).toBeTruthy(); + const ancestorTagAssigned = await TagUser.exists({ + ...args.input, + tagId: testTag?._id.toString() ?? "", + }); + + expect(subTagAssigned).toBeTruthy(); + expect(ancestorTagAssigned).toBeTruthy(); }); it(`Throws USER_ALREADY_HAS_TAG error if tag with _id === args.input.tagId is already assigned to user with _id === args.input.userId`, async () => { diff --git a/tests/resolvers/Mutation/unassignUserTag.spec.ts b/tests/resolvers/Mutation/unassignUserTag.spec.ts index 5a6ea938d7..16ce061c8f 100644 --- a/tests/resolvers/Mutation/unassignUserTag.spec.ts +++ b/tests/resolvers/Mutation/unassignUserTag.spec.ts @@ -21,7 +21,7 @@ import { } from "../../../src/constants"; import { AppUserProfile, TagUser } from "../../../src/models"; import type { TestUserTagType } from "../../helpers/tags"; -import { createRootTagWithOrg } from "../../helpers/tags"; +import { createTwoLevelTagsWithOrg } from "../../helpers/tags"; import type { TestUserType } from "../../helpers/userAndOrg"; import { createTestUser } from "../../helpers/userAndOrg"; @@ -29,11 +29,12 @@ let MONGOOSE_INSTANCE: typeof mongoose; let adminUser: TestUserType; let testTag: TestUserTagType; +let testSubTag1: TestUserTagType; let randomUser: TestUserType; beforeAll(async () => { MONGOOSE_INSTANCE = await connect(); - [adminUser, , testTag] = await createRootTagWithOrg(); + [adminUser, , [testTag, testSubTag1]] = await createTwoLevelTagsWithOrg(); randomUser = await createTestUser(); }); @@ -244,6 +245,56 @@ describe("resolvers -> Mutation -> unassignUserTag", () => { expect(tagAssigned).toBeFalsy(); }); + + it(`should unassign all the child tags and decendent tags of a parent tag and return the user`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + vi.spyOn(requestContext, "translate").mockImplementationOnce( + (message) => `Translated ${message}`, + ); + + const args: MutationUnassignUserTagArgs = { + input: { + userId: adminUser?._id, + tagId: testTag ? testTag._id.toString() : "", + }, + }; + const context = { + userId: adminUser?._id, + }; + + // Assign the parent and sub tag to the user + await TagUser.create({ + ...args.input, + }); + + await TagUser.create({ + ...args.input, + tagId: testSubTag1 ? testSubTag1._id.toString() : "", + }); + + // Test the unassignUserTag resolver + const { unassignUserTag: unassignUserTagResolver } = await import( + "../../../src/resolvers/Mutation/unassignUserTag" + ); + + const payload = await unassignUserTagResolver?.({}, args, context); + + expect(payload?._id.toString()).toEqual(adminUser?._id.toString()); + + const tagAssigned = await TagUser.exists({ + ...args.input, + }); + + const subTagAssigned = await TagUser.exists({ + ...args.input, + tagId: testSubTag1 ? testSubTag1._id.toString() : "", + }); + + expect(tagAssigned).toBeFalsy(); + expect(subTagAssigned).toBeFalsy(); + }); + it("throws an error if the user does not have appUserProfile", async () => { const { requestContext } = await import("../../../src/libraries"); const spy = vi diff --git a/tests/resolvers/Mutation/updateUserTag.spec.ts b/tests/resolvers/Mutation/updateUserTag.spec.ts index 60f2099498..87280b4bc3 100644 --- a/tests/resolvers/Mutation/updateUserTag.spec.ts +++ b/tests/resolvers/Mutation/updateUserTag.spec.ts @@ -60,7 +60,7 @@ describe("resolvers -> Mutation -> updateUserTag", () => { const args: MutationUpdateUserTagArgs = { input: { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - _id: testTag!._id.toString(), + tagId: testTag!._id.toString(), name: "NewName", tagColor: "#000000", }, @@ -92,7 +92,7 @@ describe("resolvers -> Mutation -> updateUserTag", () => { try { const args: MutationUpdateUserTagArgs = { input: { - _id: new Types.ObjectId().toString(), + tagId: new Types.ObjectId().toString(), name: "NewName", tagColor: "#000000", }, @@ -125,7 +125,7 @@ describe("resolvers -> Mutation -> updateUserTag", () => { const args: MutationUpdateUserTagArgs = { input: { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - _id: testTag!._id.toString(), + tagId: testTag!._id.toString(), name: "NewName", tagColor: "#000000", }, @@ -162,7 +162,7 @@ describe("resolvers -> Mutation -> updateUserTag", () => { const args: MutationUpdateUserTagArgs = { input: { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - _id: testTag!._id.toString(), + tagId: testTag!._id.toString(), // eslint-disable-next-line @typescript-eslint/no-non-null-assertion name: testTag!.name, tagColor: "#000000", @@ -198,7 +198,7 @@ describe("resolvers -> Mutation -> updateUserTag", () => { const args: MutationUpdateUserTagArgs = { input: { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - _id: testTag!._id.toString(), + tagId: testTag!._id.toString(), // eslint-disable-next-line @typescript-eslint/no-non-null-assertion name: testTag2!.name, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -226,7 +226,7 @@ describe("resolvers -> Mutation -> updateUserTag", () => { const args: MutationUpdateUserTagArgs = { input: { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - _id: testTag!._id.toString(), + tagId: testTag!._id.toString(), name: "NewName", tagColor: "#000000", }, diff --git a/tests/resolvers/Query/getUserTag.spec.ts b/tests/resolvers/Query/getUserTag.spec.ts new file mode 100644 index 0000000000..f81f51d960 --- /dev/null +++ b/tests/resolvers/Query/getUserTag.spec.ts @@ -0,0 +1,56 @@ +import "dotenv/config"; +import type mongoose from "mongoose"; +import { Types } from "mongoose"; +import { connect, disconnect } from "../../helpers/db"; + +import { getUserTag as getUserTagResolver } from "../../../src/resolvers/Query/getUserTag"; +import type { QueryGetUserTagArgs } from "../../../src/types/generatedGraphQLTypes"; +import { beforeAll, afterAll, describe, it, expect, vi } from "vitest"; +import { createRootTagWithOrg, type TestUserTagType } from "../../helpers/tags"; +import { TAG_NOT_FOUND } from "../../../src/constants"; + +let MONGOOSE_INSTANCE: typeof mongoose; + +let testTag: TestUserTagType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + [, , testTag] = await createRootTagWithOrg(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Query -> getUserTagAncestors", () => { + it(`throws NotFoundError if no userTag exists with _id === args.id`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: QueryGetUserTagArgs = { + id: new Types.ObjectId().toString(), + }; + + await getUserTagResolver?.({}, args, {}); + } catch (error: unknown) { + expect(spy).toHaveBeenLastCalledWith(TAG_NOT_FOUND.MESSAGE); + expect((error as Error).message).toEqual( + `Translated ${TAG_NOT_FOUND.MESSAGE}`, + ); + } + }); + + it(`returns the tag with _id === args.id`, async () => { + const args: QueryGetUserTagArgs = { + id: testTag?._id.toString() ?? "", + }; + + const getUserTagAncestorsPayload = await getUserTagResolver?.({}, args, {}); + + expect(getUserTagAncestorsPayload).toEqual(testTag); + }); +}); diff --git a/tests/resolvers/Query/getUserTagAncestors.spec.ts b/tests/resolvers/Query/getUserTagAncestors.spec.ts new file mode 100644 index 0000000000..2a3ee00143 --- /dev/null +++ b/tests/resolvers/Query/getUserTagAncestors.spec.ts @@ -0,0 +1,64 @@ +import "dotenv/config"; +import type mongoose from "mongoose"; +import { Types } from "mongoose"; +import { connect, disconnect } from "../../helpers/db"; + +import { getUserTagAncestors as getUserTagAncestorsResolver } from "../../../src/resolvers/Query/getUserTagAncestors"; +import type { QueryGetUserTagAncestorsArgs } from "../../../src/types/generatedGraphQLTypes"; +import { beforeAll, afterAll, describe, it, expect, vi } from "vitest"; +import { + createTwoLevelTagsWithOrg, + type TestUserTagType, +} from "../../helpers/tags"; +import { TAG_NOT_FOUND } from "../../../src/constants"; + +let MONGOOSE_INSTANCE: typeof mongoose; + +let testTag: TestUserTagType; +let testSubTag1: TestUserTagType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + [, , [testTag, testSubTag1]] = await createTwoLevelTagsWithOrg(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Query -> getUserTagAncestors", () => { + it(`throws NotFoundError if no userTag exists with _id === args.id`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: QueryGetUserTagAncestorsArgs = { + id: new Types.ObjectId().toString(), + }; + + await getUserTagAncestorsResolver?.({}, args, {}); + } catch (error: unknown) { + expect(spy).toHaveBeenLastCalledWith(TAG_NOT_FOUND.MESSAGE); + expect((error as Error).message).toEqual( + `Translated ${TAG_NOT_FOUND.MESSAGE}`, + ); + } + }); + + it(`returns the list of all the ancestor tags for a tag with _id === args.id`, async () => { + const args: QueryGetUserTagAncestorsArgs = { + id: testSubTag1?._id.toString() ?? "", + }; + + const getUserTagAncestorsPayload = await getUserTagAncestorsResolver?.( + {}, + args, + {}, + ); + + expect(getUserTagAncestorsPayload).toEqual([testTag, testSubTag1]); + }); +});