Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test: src/graphql/types/Community/updater.ts #3110

69 changes: 69 additions & 0 deletions src/graphql/types/Community/Community.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,79 @@
import type { communitiesTable } from "~/src/drizzle/tables/communities";
import { builder } from "~/src/graphql/builder";
import { TalawaGraphQLError } from "~/src/utilities/TalawaGraphQLError";
import type { GraphQLContext } from "../../context";
import type { User } from "../User/User";

export type Community = typeof communitiesTable.$inferSelect;

export const Community = builder.objectRef<Community>("Community");

export type CommunityResolvers = {
updater: (
parent: Community,
_args: unknown,
context: GraphQLContext,
) => Promise<User | null>;
};

export const CommunityResolver: CommunityResolvers = {
updater: async (parent, _args, context) => {
try {
if (!context.currentClient.isAuthenticated) {
throw new TalawaGraphQLError({
message: "User is not authenticated",
extensions: { code: "unauthenticated" },
});
}

NishantSinghhhhh marked this conversation as resolved.
Show resolved Hide resolved
if (!parent.updaterId) {
return null;
}
NishantSinghhhhh marked this conversation as resolved.
Show resolved Hide resolved

const updaterId = parent.updaterId;

const existingUser =
await context.drizzleClient.query.usersTable.findFirst({
where: (users, { eq }) => eq(users.id, updaterId), // Must use updaterId here
});

if (existingUser === undefined) {
context.log.warn(`No user found for updaterId: ${updaterId}`);
throw new TalawaGraphQLError({
message: "Updater user not found",
extensions: {
code: "arguments_associated_resources_not_found",
issues: [{ argumentPath: ["updaterId"] }],
},
});
}

const updater = await context.drizzleClient.query.usersTable.findFirst({
where: (users, { eq, and, isNull }) =>
parent.updaterId ? eq(users.id, parent.updaterId) : isNull(users.id),

Check warning on line 53 in src/graphql/types/Community/Community.ts

View check run for this annotation

Codecov / codecov/patch

src/graphql/types/Community/Community.ts#L53

Added line #L53 was not covered by tests
});

NishantSinghhhhh marked this conversation as resolved.
Show resolved Hide resolved
if (!updater) {
context.log.warn(`No user found for updaterId: ${parent.updaterId}`);
throw new TalawaGraphQLError({
message: "Updater user not found",
extensions: {
code: "arguments_associated_resources_not_found",
issues: [{ argumentPath: ["updaterId"] }],
},
});

Check warning on line 64 in src/graphql/types/Community/Community.ts

View check run for this annotation

Codecov / codecov/patch

src/graphql/types/Community/Community.ts#L57-L64

Added lines #L57 - L64 were not covered by tests
NishantSinghhhhh marked this conversation as resolved.
Show resolved Hide resolved
}

return updater;
} catch (error) {
context.log.error("Database error in community updater resolver", {
error,
});
throw error;
}
},
};

Community.implement({
description:
"Communitys are controlled spaces of collections of users who associate with the purpose those communities exist for.",
Expand Down
302 changes: 302 additions & 0 deletions test/graphql/types/Community/updater.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
import type { FastifyBaseLogger } from "fastify";
import type { Client as MinioClient } from "minio";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { Community } from "~/src/graphql/types/Community/Community";
import { CommunityResolver } from "~/src/graphql/types/Community/Community";
import type { User } from "~/src/graphql/types/User/User";
import { TalawaGraphQLError } from "~/src/utilities/TalawaGraphQLError";
import type { GraphQLContext } from "../../../../src/graphql/context";
import { createMockLogger } from "../../../utilities/mockLogger";

type DeepPartial<T> = Partial<T>;
NishantSinghhhhh marked this conversation as resolved.
Show resolved Hide resolved

type PubSubEvents = {
COMMUNITY_CREATED: { id: string };
POST_CREATED: { id: string };
};

interface TestContext extends Omit<GraphQLContext, "log"> {
drizzleClient: {
query: {
usersTable: {
findFirst: ReturnType<typeof vi.fn>;
};
};
} & GraphQLContext["drizzleClient"];
log: FastifyBaseLogger;
}

const createMockPubSub = () => ({
publish: vi.fn().mockImplementation(
(
event: {
topic: keyof PubSubEvents;
payload: PubSubEvents[keyof PubSubEvents];
},
callback?: () => void,
) => {
if (callback) callback();
return;
},
),
subscribe: vi.fn(),
asyncIterator: vi.fn(),
});
NishantSinghhhhh marked this conversation as resolved.
Show resolved Hide resolved

describe("Community Resolver - Updater Field", () => {
let ctx: TestContext;
let mockUser: DeepPartial<User>;
let mockCommunity: Community;

beforeEach(() => {
mockUser = {
id: "123",
name: "John Doe",
role: "administrator",
createdAt: new Date(),
updatedAt: null,
};
NishantSinghhhhh marked this conversation as resolved.
Show resolved Hide resolved

mockCommunity = {
id: "community-123",
name: "Test Community",
createdAt: new Date(),
updatedAt: new Date(),
updaterId: "456",
facebookURL: null,
githubURL: null,
inactivityTimeoutDuration: null,
instagramURL: null,
linkedinURL: null,
logoMimeType: null,
logoName: null,
redditURL: null,
slackURL: null,
websiteURL: null,
xURL: null,
youtubeURL: null,
};
NishantSinghhhhh marked this conversation as resolved.
Show resolved Hide resolved

const mockLogger = createMockLogger();

ctx = {
drizzleClient: {
query: {
usersTable: {
findFirst: vi.fn().mockResolvedValue(mockUser),
},
},
} as unknown as TestContext["drizzleClient"],
log: mockLogger,
pubsub: createMockPubSub(),
envConfig: {
API_BASE_URL: "http://localhost:3000",
},
jwt: {
sign: vi.fn().mockReturnValue("mock-token"),
},
minio: {
bucketName: "talawa",
client: {
listBuckets: vi.fn(),
putObject: vi.fn(),
getObject: vi.fn(),
} as unknown as MinioClient,
},
currentClient: {
isAuthenticated: true,
user: {
id: "123", // Ensure this is always set
},
},
};
});

it("should return null when updaterId is null", async () => {
const nullUpdaterCommunity = {
...mockCommunity,
updaterId: null,
};

const result = await CommunityResolver.updater(
nullUpdaterCommunity,
{},
ctx,
);
expect(result).toBeNull();
});

it("should throw unauthenticated error", async () => {
ctx.currentClient.isAuthenticated = false;

await expect(
CommunityResolver.updater(mockCommunity, {}, ctx),
).rejects.toThrow(
new TalawaGraphQLError({
message: "User is not authenticated",
extensions: { code: "unauthenticated" },
}),
);
});

it("should make correct database queries with expected parameters", async () => {
const updaterUser = {
id: "456",
name: "Jane Updater",
role: "user",
createdAt: new Date(),
updatedAt: null,
};

NishantSinghhhhh marked this conversation as resolved.
Show resolved Hide resolved
ctx.drizzleClient.query.usersTable.findFirst
.mockResolvedValueOnce(mockUser)
.mockResolvedValueOnce(updaterUser);

await CommunityResolver.updater(mockCommunity, {}, ctx);

expect(ctx.drizzleClient.query.usersTable.findFirst).toHaveBeenCalledWith({
where: expect.any(Function),
});
expect(ctx.drizzleClient.query.usersTable.findFirst).toHaveBeenCalledTimes(
2,
);
});

it("should successfully return updater user when all conditions are met", async () => {
const updaterUser = {
id: "456",
name: "Jane Updater",
role: "user",
createdAt: new Date(),
updatedAt: null,
};

ctx.drizzleClient.query.usersTable.findFirst
.mockResolvedValueOnce(mockUser)
.mockResolvedValueOnce(updaterUser);

const result = await CommunityResolver.updater(mockCommunity, {}, ctx);

expect(result).toEqual(updaterUser);
expect(ctx.drizzleClient.query.usersTable.findFirst).toHaveBeenCalledTimes(
2,
);
});

it("should correctly query the database for current user and updater user", async () => {
const updaterUser = {
id: "456",
name: "Jane Updater",
role: "user",
createdAt: new Date(),
updatedAt: null,
};

ctx.drizzleClient.query.usersTable.findFirst
.mockResolvedValueOnce(mockUser) // First call for current user
.mockResolvedValueOnce(updaterUser); // Second call for updater user

await CommunityResolver.updater(mockCommunity, {}, ctx);

expect(ctx.drizzleClient.query.usersTable.findFirst).toHaveBeenCalledTimes(
2,
);
});
NishantSinghhhhh marked this conversation as resolved.
Show resolved Hide resolved

it("should log a warning when an updater ID exists but no user is found", async () => {
ctx.drizzleClient.query.usersTable.findFirst.mockResolvedValue(undefined);

await expect(
CommunityResolver.updater(mockCommunity, {}, ctx),
).rejects.toThrowError(
new TalawaGraphQLError({
message: "Updater user not found",
extensions: {
code: "arguments_associated_resources_not_found",
issues: [{ argumentPath: ["updaterId"] }],
},
}),
);

expect(ctx.log.warn).toHaveBeenCalledWith(
`No user found for updaterId: ${mockCommunity.updaterId}`,
);
});
NishantSinghhhhh marked this conversation as resolved.
Show resolved Hide resolved
it("should handle database errors gracefully", async () => {
const dbError = new Error("Database connection failed");

ctx.drizzleClient.query.usersTable.findFirst
.mockRejectedValueOnce(dbError)
.mockResolvedValueOnce(mockUser);

const logErrorSpy = vi.spyOn(ctx.log, "error");

await expect(
CommunityResolver.updater(mockCommunity, {}, ctx),
).rejects.toThrow(dbError);

expect(logErrorSpy).toHaveBeenCalledWith(
"Database error in community updater resolver",
{ error: dbError },
);
});
NishantSinghhhhh marked this conversation as resolved.
Show resolved Hide resolved

it("should fetch different user when updaterId doesn't match current user", async () => {
const differentUpdaterCommunity = {
...mockCommunity,
updaterId: "different-id-789",
};

const differentUser: DeepPartial<User> = {
...mockUser,
id: "different-id-789",
name: "Jane Smith",
};

ctx.drizzleClient.query.usersTable.findFirst
.mockResolvedValueOnce(mockUser)
.mockResolvedValueOnce(differentUser);
const result = await CommunityResolver.updater(
differentUpdaterCommunity,
{},
ctx,
);

expect(result).toEqual(differentUser);

expect(ctx.drizzleClient.query.usersTable.findFirst).toHaveBeenCalledTimes(
2,
);

expect(
ctx.drizzleClient.query.usersTable.findFirst,
).toHaveBeenNthCalledWith(2, {
where: expect.any(Function),
});
});

it("should log warning and throw error when updater is not found", async () => {
ctx.drizzleClient.query.usersTable.findFirst.mockResolvedValue(undefined);

const testCommunity = {
...mockCommunity,
updaterId: "non-existent-id",
};

await expect(
CommunityResolver.updater(testCommunity, {}, ctx),
).rejects.toThrow(
new TalawaGraphQLError({
message: "Updater user not found",
extensions: {
code: "arguments_associated_resources_not_found",
issues: [{ argumentPath: ["updaterId"] }],
},
}),
);

expect(ctx.log.warn).toHaveBeenCalledWith(
`No user found for updaterId: ${testCommunity.updaterId}`,
);
});
NishantSinghhhhh marked this conversation as resolved.
Show resolved Hide resolved
});
NishantSinghhhhh marked this conversation as resolved.
Show resolved Hide resolved
Loading
Loading