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

Code coverage for /src/graphql/types/Query/fund.ts #3166

Merged

Conversation

yugal07
Copy link

@yugal07 yugal07 commented Feb 7, 2025

What kind of change does this PR introduce?

Code coverage

Issue Number:

Fixes #3052

Summary

Added test cases for the file fund.ts

Does this PR introduce a breaking change?
No

Checklist

CodeRabbit AI Review

  • I have reviewed and addressed all critical issues flagged by CodeRabbit AI
  • I have implemented or provided justification for each non-critical suggestion
  • I have documented my reasoning in the PR comments where CodeRabbit AI suggestions were not implemented

Test Coverage

  • I have written tests for all new changes/features
  • I have verified that test coverage meets or exceeds 95%
  • I have run the test suite locally and all tests pass

Other information

Have you read the contributing guide?

Summary by CodeRabbit

  • New Features

    • Introduced new GraphQL queries and mutations for managing funds and organizations, including creation and deletion functionalities.
  • Tests

    • Added comprehensive tests to validate fund query behavior under various conditions, including error handling and access control.
    • Enhanced test coverage for fund creation, ensuring compliance with naming and uniqueness constraints.

Copy link

coderabbitai bot commented Feb 7, 2025

Walkthrough

This pull request introduces a comprehensive suite of tests for the GraphQL fund query. The tests cover multiple error scenarios—including unauthenticated access, non-existent funds, and unauthorized users—as well as successful data retrieval for admins and organization members. In addition, new GraphQL operations (a query and several mutations) have been added in the document nodes and cache declaration files to support fund queries and the creation of organizations, funds, and organization memberships.

Changes

File(s) Change Summary
test/routes/graphql/Query/fund.test.ts Added extensive tests for the fund query covering unauthenticated, non-existent, and unauthorized access scenarios, along with validations for successful data retrieval and fund creation schema constraints.
test/routes/graphql/.../documentNodes.ts
test/routes/graphql/.../gql.tada-cache.d.ts
Introduced new GraphQL operations: Query_fund, Mutation_createOrganization, Mutation_createFund, Mutation_createOrganizationMembership, Mutation_deleteFund, Mutation_deleteOrganization, and Mutation_deleteOrganizationMembership.

Assessment against linked issues

Objective Addressed Explanation
Improve test coverage for fund query logic in src/graphql/types/Query/fund.ts (#3052)

Possibly related issues

Suggested labels

ignore-sensitive-files-pr

Suggested reviewers

  • palisadoes
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Feb 7, 2025

Our Pull Request Approval Process

Thanks for contributing!

Testing Your Code

Remember, your PRs won't be reviewed until these criteria are met:

  1. We don't merge PRs with poor code quality.
    1. Follow coding best practices such that CodeRabbit.ai approves your PR.
  2. We don't merge PRs with failed tests.
    1. When tests fail, click on the Details link to learn more.
    2. Write sufficient tests for your changes (CodeCov Patch Test). Your testing level must be better than the target threshold of the repository
    3. Tests may fail if you edit sensitive files. Ask to add the ignore-sensitive-files-pr label if the edits are necessary.
  3. We cannot merge PRs with conflicting files. These must be fixed.

Our policies make our code better.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Congratulations on making your first PR! 🎊 If you haven't already, check out our Contributing Guidelines and PR Reporting Guidelines to ensure that you are following our guidelines for contributing and creating PR.

Copy link

codecov bot commented Feb 7, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 42.08%. Comparing base (dcd6ad9) to head (cb5ee2b).
Report is 19 commits behind head on develop-postgres.

Additional details and impacted files
@@                 Coverage Diff                  @@
##           develop-postgres    #3166      +/-   ##
====================================================
+ Coverage             39.71%   42.08%   +2.37%     
====================================================
  Files                   455      455              
  Lines                 33553    33647      +94     
  Branches                407      563     +156     
====================================================
+ Hits                  13324    14161     +837     
+ Misses                20229    19486     -743     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dcd6ad9 and 1896247.

📒 Files selected for processing (3)
  • test/routes/graphql/Query/fund.test.ts (1 hunks)
  • test/routes/graphql/documentNodes.ts (1 hunks)
  • test/routes/graphql/gql.tada-cache.d.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Run tests for talawa api
  • GitHub Check: Analyse Code With CodeQL (typescript)
🔇 Additional comments (4)
test/routes/graphql/documentNodes.ts (1)

311-342: LGTM! Well-structured GraphQL operations.

The new operations are well-defined and follow consistent patterns with existing operations in the file.

test/routes/graphql/Query/fund.test.ts (2)

22-128: LGTM! Comprehensive error handling test cases.

The error handling tests cover key scenarios:

  • Unauthenticated access
  • Non-existent fund
  • Unauthorized access for non-members

193-356: LGTM! Thorough schema validation tests.

The validation tests cover:

  • Name length constraints
  • Unique name constraints
  • Required fields
  • UUID validation
  • Field type constraints
test/routes/graphql/gql.tada-cache.d.ts (1)

33-40: LGTM! Well-defined TypeScript types for GraphQL operations.

The type definitions provide proper type safety and are consistent with the GraphQL schema.

Comment on lines 130 to 190
test("returns fund data if user is an admin", async () => {
const adminSignInResult = await mercuriusClient.query(Query_signIn, {
variables: {
input: {
emailAddress: server.envConfig.API_ADMINISTRATOR_USER_EMAIL_ADDRESS,
password: server.envConfig.API_ADMINISTRATOR_USER_PASSWORD,
},
},
});

assertToBeNonNullish(adminSignInResult.data.signIn?.authenticationToken);

const { fundId } = await createFund();

const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
},
variables: {
input: {
id: fundId,
},
},
});

expect(fundResult.errors).toBeUndefined();
expect(fundResult.data.fund).toEqual(
expect.objectContaining({
id: fundId,
isTaxDeductible: expect.any(Boolean),
name: expect.any(String),
}),
);
});

test("returns fund data if user is organization member", async () => {
const regularUserResult = await createRegularUser();
const { fundId, orgId } = await createFund();

await addUserToOrg(regularUserResult.userId, orgId);

const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${regularUserResult.authToken}`,
},
variables: {
input: {
id: fundId,
},
},
});

expect(fundResult.errors).toBeUndefined();
expect(fundResult.data.fund).toEqual(
expect.objectContaining({
id: fundId,
isTaxDeductible: expect.any(Boolean),
name: expect.any(String),
}),
);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider adding edge cases for successful fund retrieval.

While the basic success cases are covered, consider adding tests for:

  • Funds with null/empty fields
  • Funds with maximum length names
  • Multiple funds in the same organization

Comment on lines 359 to 475
},
variables: {
input: {
emailAddress: `email${faker.string.uuid()}@test.com`,
password: "password123",
role: "regular",
name: "Test User",
isEmailAddressVerified: false,
},
},
});

assertToBeNonNullish(userResult.data?.createUser?.authenticationToken);
assertToBeNonNullish(userResult.data?.createUser?.user?.id);

return {
authToken: userResult.data.createUser.authenticationToken,
userId: userResult.data.createUser.user.id,
};
}

async function createFund() {
// First sign in as admin to create org and fund
const adminSignInResult = await mercuriusClient.query(Query_signIn, {
variables: {
input: {
emailAddress: server.envConfig.API_ADMINISTRATOR_USER_EMAIL_ADDRESS,
password: server.envConfig.API_ADMINISTRATOR_USER_PASSWORD,
},
},
});

assertToBeNonNullish(adminSignInResult.data.signIn?.authenticationToken);

// Create organization
const createOrgResult = await mercuriusClient.mutate(
Mutation_createOrganization,
{
headers: {
authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
},
variables: {
input: {
name: `Org ${faker.string.uuid()}`,
countryCode: "us",
},
},
},
);

assertToBeNonNullish(createOrgResult.data?.createOrganization?.id);
const orgId = createOrgResult.data.createOrganization.id;

// Create fund
const createFundResult = await mercuriusClient.mutate(Mutation_createFund, {
headers: {
authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
},
variables: {
input: {
name: `Fund ${faker.string.uuid()}`,
organizationId: orgId,
isTaxDeductible: false,
},
},
});

assertToBeNonNullish(createFundResult.data?.createFund?.id);

return {
fundId: createFundResult.data.createFund.id,
orgId,
};
}

async function addUserToOrg(userId: string, orgId: string) {
const adminSignInResult = await mercuriusClient.query(Query_signIn, {
variables: {
input: {
emailAddress: server.envConfig.API_ADMINISTRATOR_USER_EMAIL_ADDRESS,
password: server.envConfig.API_ADMINISTRATOR_USER_PASSWORD,
},
},
});

assertToBeNonNullish(adminSignInResult.data.signIn?.authenticationToken);

await mercuriusClient.mutate(Mutation_createOrganizationMembership, {
headers: {
authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
},
variables: {
input: {
memberId: userId,
organizationId: orgId,
role: "regular",
},
},
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider enhancing helper functions for better reusability.

The helper functions could be improved by:

  • Adding error handling for failed operations
  • Supporting custom input parameters
  • Adding cleanup functions to remove test data

Copy link
Contributor

@palisadoes palisadoes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Please make code rabbit.ai approve your work
  2. Ensure you get 100% test code coverage

Copy link

github-actions bot commented Feb 9, 2025

Our Pull Request Approval Process

Thanks for contributing!

Testing Your Code

Remember, your PRs won't be reviewed until these criteria are met:

  1. We don't merge PRs with poor code quality.
    1. Follow coding best practices such that CodeRabbit.ai approves your PR.
  2. We don't merge PRs with failed tests.
    1. When tests fail, click on the Details link to learn more.
    2. Write sufficient tests for your changes (CodeCov Patch Test). Your testing level must be better than the target threshold of the repository
    3. Tests may fail if you edit sensitive files. Ask to add the ignore-sensitive-files-pr label if the edits are necessary.
  3. We cannot merge PRs with conflicting files. These must be fixed.

Our policies make our code better.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
test/routes/graphql/Query/fund.test.ts (1)

516-632: ⚠️ Potential issue

Remove duplicate helper functions and enhance error handling.

  1. The helper functions are duplicated:

    • createRegularUser: Lines 161-187 and 516-552
    • createFund: Lines 189-231 and 554-606
    • addUserToOrg: Lines 233-248 and 608-632
  2. Consider enhancing the helper functions with:

    • Error handling for failed operations
    • Cleanup functions to remove test data
    • Retry logic for flaky operations
    • Logging for debugging
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1896247 and 370803c.

📒 Files selected for processing (1)
  • test/routes/graphql/Query/fund.test.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyse Code With CodeQL (typescript)

Comment on lines 288 to 321
test("returns fund data if user is an admin", async () => {
const adminSignInResult = await mercuriusClient.query(Query_signIn, {
variables: {
input: {
emailAddress: server.envConfig.API_ADMINISTRATOR_USER_EMAIL_ADDRESS,
password: server.envConfig.API_ADMINISTRATOR_USER_PASSWORD,
},
},
});

assertToBeNonNullish(adminSignInResult.data.signIn?.authenticationToken);

const { fundId } = await createFund();

const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
},
variables: {
input: {
id: fundId,
},
},
});

expect(fundResult.errors).toBeUndefined();
expect(fundResult.data.fund).toEqual(
expect.objectContaining({
id: fundId,
isTaxDeductible: expect.any(Boolean),
name: expect.any(String),
}),
);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove duplicate test case.

This test case is a duplicate of the one at lines 107-130. Consider removing this redundant test to maintain a cleaner test suite.

Comment on lines 323 to 348
test("returns fund data if user is organization member", async () => {
const regularUserResult = await createRegularUser();
const { fundId, orgId } = await createFund();

await addUserToOrg(regularUserResult.userId, orgId);

const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${regularUserResult.authToken}`,
},
variables: {
input: {
id: fundId,
},
},
});

expect(fundResult.errors).toBeUndefined();
expect(fundResult.data.fund).toEqual(
expect.objectContaining({
id: fundId,
isTaxDeductible: expect.any(Boolean),
name: expect.any(String),
}),
);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove duplicate test case.

This test case is a duplicate of the one at lines 132-157. Consider removing this redundant test to maintain a cleaner test suite.

Comment on lines 350 to 513
input: {
name: fundName,
organizationId: orgId,
isTaxDeductible: false,
},
},
});

assertToBeNonNullish(fund1Result.data?.createFund?.id);

const fund2Result = await mercuriusClient.mutate(Mutation_createFund, {
headers: {
authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
},
variables: {
input: {
name: fundName,
organizationId: orgId,
isTaxDeductible: true,
},
},
});

expect(fund2Result.errors).toBeDefined();
expect(fund2Result.errors?.[0]?.extensions?.code).toBe(
"forbidden_action_on_arguments_associated_resources",
);
});

test("verifies UUID generation for fund id", async () => {
const { fundId } = await createFund();
expect(fundId).toMatch(
/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
);
});
});

test("validates required fund fields", () => {
const validInput = {
name: "Test Fund",
isTaxDeductible: false,
organizationId: faker.string.uuid(),
};

// Test missing name
const missingName = { ...validInput, name: undefined };
expect(fundsTableInsertSchema.safeParse(missingName).success).toBe(false);

// Test missing isTaxDeductible
const missingTaxStatus = { ...validInput, isTaxDeductible: undefined };
expect(fundsTableInsertSchema.safeParse(missingTaxStatus).success).toBe(
false,
);

// Test missing organizationId
const missingOrgId = { ...validInput, organizationId: undefined };
expect(fundsTableInsertSchema.safeParse(missingOrgId).success).toBe(false);

// Valid data should pass
expect(fundsTableInsertSchema.safeParse(validInput).success).toBe(true);
});

test("validates fund field constraints", () => {
const validInput = {
name: "Test Fund",
isTaxDeductible: false,
organizationId: faker.string.uuid(),
};

// Test name constraints
expect(
fundsTableInsertSchema.safeParse({
...validInput,
name: "",
}).success,
).toBe(false);

expect(
fundsTableInsertSchema.safeParse({
...validInput,
name: "a".repeat(257),
}).success,
).toBe(false);

// Test isTaxDeductible must be boolean
expect(
fundsTableInsertSchema.safeParse({
...validInput,
isTaxDeductible: "true",
}).success,
).toBe(false);

// Test organizationId must be UUID
expect(
fundsTableInsertSchema.safeParse({
...validInput,
organizationId: "invalid-uuid",
}).success,
).toBe(false);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider adding more edge cases for fund name validation.

While the current validation tests are good, consider adding test cases for:

  • Fund names with special characters
  • Fund names with leading/trailing spaces
  • Fund names with only whitespace characters
  • Unicode characters in fund names
 test("validates fund name length constraints", () => {
   const tooShortName = "";
   const tooLongName = "a".repeat(257);
   const validName = "Test Fund";
+  const specialCharsName = "Fund #1 @Special!";
+  const whitespaceOnlyName = "   ";
+  const unicodeName = "Fund 🚀 测试";
+  const spacePaddedName = "  Test Fund  ";

   // ... existing tests ...

+  // Test special characters
+  const specialCharsResult = fundsTableInsertSchema.safeParse({
+    name: specialCharsName,
+    isTaxDeductible: false,
+    organizationId: faker.string.uuid(),
+  });
+  expect(specialCharsResult.success).toBe(true);

+  // Test whitespace-only name
+  const whitespaceOnlyResult = fundsTableInsertSchema.safeParse({
+    name: whitespaceOnlyName,
+    isTaxDeductible: false,
+    organizationId: faker.string.uuid(),
+  });
+  expect(whitespaceOnlyResult.success).toBe(false);

+  // Test unicode characters
+  const unicodeResult = fundsTableInsertSchema.safeParse({
+    name: unicodeName,
+    isTaxDeductible: false,
+    organizationId: faker.string.uuid(),
+  });
+  expect(unicodeResult.success).toBe(true);

+  // Test space-padded name
+  const spacePaddedResult = fundsTableInsertSchema.safeParse({
+    name: spacePaddedName,
+    isTaxDeductible: false,
+    organizationId: faker.string.uuid(),
+  });
+  expect(spacePaddedResult.success).toBe(true);
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
suite("Funds schema validation and field behavior", () => {
test("validates fund name length constraints", () => {
const tooShortName = "";
const tooLongName = "a".repeat(257);
const validName = "Test Fund";
const tooShortResult = fundsTableInsertSchema.safeParse({
name: tooShortName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(tooShortResult.success).toBe(false);
const tooLongResult = fundsTableInsertSchema.safeParse({
name: tooLongName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(tooLongResult.success).toBe(false);
const validResult = fundsTableInsertSchema.safeParse({
name: validName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(validResult.success).toBe(true);
});
test("verifies unique constraint on fund name within organization", async () => {
const adminSignInResult = await mercuriusClient.query(Query_signIn, {
variables: {
input: {
emailAddress: server.envConfig.API_ADMINISTRATOR_USER_EMAIL_ADDRESS,
password: server.envConfig.API_ADMINISTRATOR_USER_PASSWORD,
},
},
});
assertToBeNonNullish(adminSignInResult.data.signIn?.authenticationToken);
const createOrgResult = await mercuriusClient.mutate(
Mutation_createOrganization,
{
headers: {
authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
},
variables: {
input: {
name: `Org ${faker.string.uuid()}`,
countryCode: "us",
},
},
},
);
assertToBeNonNullish(createOrgResult.data?.createOrganization?.id);
const orgId = createOrgResult.data.createOrganization.id;
const fundName = `Test Fund ${faker.string.uuid()}`;
const fund1Result = await mercuriusClient.mutate(Mutation_createFund, {
headers: {
authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
},
variables: {
input: {
name: fundName,
organizationId: orgId,
isTaxDeductible: false,
},
},
});
assertToBeNonNullish(fund1Result.data?.createFund?.id);
const fund2Result = await mercuriusClient.mutate(Mutation_createFund, {
headers: {
authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
},
variables: {
input: {
name: fundName,
organizationId: orgId,
isTaxDeductible: true,
},
},
});
expect(fund2Result.errors).toBeDefined();
expect(fund2Result.errors?.[0]?.extensions?.code).toBe(
"forbidden_action_on_arguments_associated_resources",
);
});
test("verifies UUID generation for fund id", async () => {
const { fundId } = await createFund();
expect(fundId).toMatch(
/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
);
});
});
test("validates required fund fields", () => {
const validInput = {
name: "Test Fund",
isTaxDeductible: false,
organizationId: faker.string.uuid(),
};
// Test missing name
const missingName = { ...validInput, name: undefined };
expect(fundsTableInsertSchema.safeParse(missingName).success).toBe(false);
// Test missing isTaxDeductible
const missingTaxStatus = { ...validInput, isTaxDeductible: undefined };
expect(fundsTableInsertSchema.safeParse(missingTaxStatus).success).toBe(
false,
);
// Test missing organizationId
const missingOrgId = { ...validInput, organizationId: undefined };
expect(fundsTableInsertSchema.safeParse(missingOrgId).success).toBe(false);
// Valid data should pass
expect(fundsTableInsertSchema.safeParse(validInput).success).toBe(true);
});
test("validates fund field constraints", () => {
const validInput = {
name: "Test Fund",
isTaxDeductible: false,
organizationId: faker.string.uuid(),
};
// Test name constraints
expect(
fundsTableInsertSchema.safeParse({
...validInput,
name: "",
}).success,
).toBe(false);
expect(
fundsTableInsertSchema.safeParse({
...validInput,
name: "a".repeat(257),
}).success,
).toBe(false);
// Test isTaxDeductible must be boolean
expect(
fundsTableInsertSchema.safeParse({
...validInput,
isTaxDeductible: "true",
}).success,
).toBe(false);
// Test organizationId must be UUID
expect(
fundsTableInsertSchema.safeParse({
...validInput,
organizationId: "invalid-uuid",
}).success,
).toBe(false);
});
suite("Funds schema validation and field behavior", () => {
test("validates fund name length constraints", () => {
const tooShortName = "";
const tooLongName = "a".repeat(257);
const validName = "Test Fund";
const specialCharsName = "Fund #1 @Special!";
const whitespaceOnlyName = " ";
const unicodeName = "Fund 🚀 测试";
const spacePaddedName = " Test Fund ";
const tooShortResult = fundsTableInsertSchema.safeParse({
name: tooShortName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(tooShortResult.success).toBe(false);
const tooLongResult = fundsTableInsertSchema.safeParse({
name: tooLongName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(tooLongResult.success).toBe(false);
const validResult = fundsTableInsertSchema.safeParse({
name: validName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(validResult.success).toBe(true);
// Test special characters
const specialCharsResult = fundsTableInsertSchema.safeParse({
name: specialCharsName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(specialCharsResult.success).toBe(true);
// Test whitespace-only name
const whitespaceOnlyResult = fundsTableInsertSchema.safeParse({
name: whitespaceOnlyName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(whitespaceOnlyResult.success).toBe(false);
// Test unicode characters
const unicodeResult = fundsTableInsertSchema.safeParse({
name: unicodeName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(unicodeResult.success).toBe(true);
// Test space-padded name
const spacePaddedResult = fundsTableInsertSchema.safeParse({
name: spacePaddedName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(spacePaddedResult.success).toBe(true);
});
test("verifies unique constraint on fund name within organization", async () => {
const adminSignInResult = await mercuriusClient.query(Query_signIn, {
variables: {
input: {
emailAddress: server.envConfig.API_ADMINISTRATOR_USER_EMAIL_ADDRESS,
password: server.envConfig.API_ADMINISTRATOR_USER_PASSWORD,
},
},
});
assertToBeNonNullish(adminSignInResult.data.signIn?.authenticationToken);
const createOrgResult = await mercuriusClient.mutate(
Mutation_createOrganization,
{
headers: {
authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
},
variables: {
input: {
name: `Org ${faker.string.uuid()}`,
countryCode: "us",
},
},
},
);
assertToBeNonNullish(createOrgResult.data?.createOrganization?.id);
const orgId = createOrgResult.data.createOrganization.id;
const fundName = `Test Fund ${faker.string.uuid()}`;
const fund1Result = await mercuriusClient.mutate(Mutation_createFund, {
headers: {
authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
},
variables: {
input: {
name: fundName,
organizationId: orgId,
isTaxDeductible: false,
},
},
});
assertToBeNonNullish(fund1Result.data?.createFund?.id);
const fund2Result = await mercuriusClient.mutate(Mutation_createFund, {
headers: {
authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
},
variables: {
input: {
name: fundName,
organizationId: orgId,
isTaxDeductible: true,
},
},
});
expect(fund2Result.errors).toBeDefined();
expect(fund2Result.errors?.[0]?.extensions?.code).toBe(
"forbidden_action_on_arguments_associated_resources",
);
});
test("verifies UUID generation for fund id", async () => {
const { fundId } = await createFund();
expect(fundId).toMatch(
/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
);
});
});
test("validates required fund fields", () => {
const validInput = {
name: "Test Fund",
isTaxDeductible: false,
organizationId: faker.string.uuid(),
};
// Test missing name
const missingName = { ...validInput, name: undefined };
expect(fundsTableInsertSchema.safeParse(missingName).success).toBe(false);
// Test missing isTaxDeductible
const missingTaxStatus = { ...validInput, isTaxDeductible: undefined };
expect(fundsTableInsertSchema.safeParse(missingTaxStatus).success).toBe(false);
// Test missing organizationId
const missingOrgId = { ...validInput, organizationId: undefined };
expect(fundsTableInsertSchema.safeParse(missingOrgId).success).toBe(false);
// Valid data should pass
expect(fundsTableInsertSchema.safeParse(validInput).success).toBe(true);
});
test("validates fund field constraints", () => {
const validInput = {
name: "Test Fund",
isTaxDeductible: false,
organizationId: faker.string.uuid(),
};
// Test name constraints
expect(
fundsTableInsertSchema.safeParse({
...validInput,
name: "",
}).success,
).toBe(false);
expect(
fundsTableInsertSchema.safeParse({
...validInput,
name: "a".repeat(257),
}).success,
).toBe(false);
// Test isTaxDeductible must be boolean
expect(
fundsTableInsertSchema.safeParse({
...validInput,
isTaxDeductible: "true",
}).success,
).toBe(false);
// Test organizationId must be UUID
expect(
fundsTableInsertSchema.safeParse({
...validInput,
organizationId: "invalid-uuid",
}).success,
).toBe(false);
});

Copy link

github-actions bot commented Feb 9, 2025

Our Pull Request Approval Process

Thanks for contributing!

Testing Your Code

Remember, your PRs won't be reviewed until these criteria are met:

  1. We don't merge PRs with poor code quality.
    1. Follow coding best practices such that CodeRabbit.ai approves your PR.
  2. We don't merge PRs with failed tests.
    1. When tests fail, click on the Details link to learn more.
    2. Write sufficient tests for your changes (CodeCov Patch Test). Your testing level must be better than the target threshold of the repository
    3. Tests may fail if you edit sensitive files. Ask to add the ignore-sensitive-files-pr label if the edits are necessary.
  3. We cannot merge PRs with conflicting files. These must be fixed.

Our policies make our code better.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 370803c and aa7dfdc.

📒 Files selected for processing (1)
  • test/routes/graphql/Query/fund.test.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyse Code With CodeQL (typescript)
🔇 Additional comments (3)
test/routes/graphql/Query/fund.test.ts (3)

263-296: Remove duplicate test cases.

These test cases are duplicates of the tests at lines 107-132. Consider removing them to maintain a cleaner test suite.

Also applies to: 298-323


1-24: LGTM! Well-structured test file setup.

The file has a clear structure with proper imports and helper function organization.


531-647: 🧹 Nitpick (assertive)

Enhance helper functions with error handling and cleanup.

The helper functions could be improved by:

  1. Adding error handling for failed operations
  2. Supporting custom input parameters
  3. Adding cleanup functions to remove test data

Here's how to enhance the createRegularUser function:

 async function createRegularUser(options?: {
+  name?: string;
+  role?: string;
+  emailAddress?: string;
+  password?: string;
 }) {
   try {
     const adminSignInResult = await mercuriusClient.query(Query_signIn, {
       variables: {
         input: {
           emailAddress: server.envConfig.API_ADMINISTRATOR_USER_EMAIL_ADDRESS,
           password: server.envConfig.API_ADMINISTRATOR_USER_PASSWORD,
         },
       },
     });

     if (!adminSignInResult.data?.signIn?.authenticationToken) {
+      throw new Error('Failed to get admin auth token');
     }

     const userResult = await mercuriusClient.mutate(Mutation_createUser, {
       headers: {
         authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
       },
       variables: {
         input: {
-          emailAddress: `email${faker.string.uuid()}@test.com`,
-          password: "password123",
-          role: "regular",
-          name: "Test User",
+          emailAddress: options?.emailAddress ?? `email${faker.string.uuid()}@test.com`,
+          password: options?.password ?? "password123",
+          role: options?.role ?? "regular",
+          name: options?.name ?? "Test User",
           isEmailAddressVerified: false,
         },
       },
     });

     if (!userResult.data?.createUser?.authenticationToken || !userResult.data?.createUser?.user?.id) {
+      throw new Error('Failed to create user');
     }

     return {
       authToken: userResult.data.createUser.authenticationToken,
       userId: userResult.data.createUser.user.id,
+      cleanup: async () => {
+        // Add cleanup logic here
+        // e.g., delete the user
+      }
     };
+  } catch (error) {
+    throw new Error(`Failed to create regular user: ${error.message}`);
   }
 }

Likely invalid or redundant comment.

Comment on lines 325 to 391
suite("Funds schema validation and field behavior", () => {
test("validates fund name constraints", () => {
// Test length constraints
const tooShortName = "";
const tooLongName = "a".repeat(257);
const validName = "Test Fund";

// Test special cases
const specialCharsName = "Fund #1 @Special!";
const whitespaceOnlyName = " ";
const unicodeName = "Fund 🚀 测试";
const spacePaddedName = " Test Fund ";

// Test length constraints
const tooShortResult = fundsTableInsertSchema.safeParse({
name: tooShortName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(tooShortResult.success).toBe(false);

const tooLongResult = fundsTableInsertSchema.safeParse({
name: tooLongName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(tooLongResult.success).toBe(false);

const validResult = fundsTableInsertSchema.safeParse({
name: validName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(validResult.success).toBe(true);

// Test special characters
const specialCharsResult = fundsTableInsertSchema.safeParse({
name: specialCharsName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(specialCharsResult.success).toBe(true);

// Test whitespace-only name
const whitespaceOnlyResult = fundsTableInsertSchema.safeParse({
name: whitespaceOnlyName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(whitespaceOnlyResult.success).toBe(true);

// Test unicode characters
const unicodeResult = fundsTableInsertSchema.safeParse({
name: unicodeName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(unicodeResult.success).toBe(true);

// Test space-padded name
const spacePaddedResult = fundsTableInsertSchema.safeParse({
name: spacePaddedName,
isTaxDeductible: false,
organizationId: faker.string.uuid(),
});
expect(spacePaddedResult.success).toBe(true);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider adding validation for trimmed fund names.

While the test cases cover various scenarios including special characters and whitespace, they don't verify if the schema trims whitespace from fund names. This could lead to duplicate funds with the same name but different spacing.

Add a test case to verify trimming behavior:

 // Test space-padded name
 const spacePaddedResult = fundsTableInsertSchema.safeParse({
   name: spacePaddedName,
   isTaxDeductible: false,
   organizationId: faker.string.uuid(),
 });
 expect(spacePaddedResult.success).toBe(true);
+
+// Test trimming behavior if supported
+if (validResult.success) {
+  const trimmedResult = validResult.data;
+  expect(trimmedResult.name).toBe(validName.trim());
+}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 458 to 463
test("verifies UUID generation for fund id", async () => {
const { fundId } = await createFund();
expect(fundId).toMatch(
/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider adding more UUID validation test cases.

The UUID validation test only checks the format. Consider adding tests for:

  • Invalid UUID formats
  • Case sensitivity
  • UUID version validation
 test("verifies UUID generation for fund id", async () => {
   const { fundId } = await createFund();
   expect(fundId).toMatch(
     /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
   );
+  
+  // Test invalid UUID formats
+  const invalidFormats = [
+    'not-a-uuid',
+    '123e4567-e89b-12d3-a456-426614174000', // Invalid version
+    '123e4567-e89b-7-a456-426614174000',    // Missing digits
+  ];
+  
+  for (const invalidId of invalidFormats) {
+    const fundResult = await mercuriusClient.query(Query_fund, {
+      variables: { input: { id: invalidId } },
+    });
+    expect(fundResult.errors).toBeDefined();
+  }
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test("verifies UUID generation for fund id", async () => {
const { fundId } = await createFund();
expect(fundId).toMatch(
/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
);
});
test("verifies UUID generation for fund id", async () => {
const { fundId } = await createFund();
expect(fundId).toMatch(
/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
);
// Test invalid UUID formats
const invalidFormats = [
'not-a-uuid',
'123e4567-e89b-12d3-a456-426614174000', // Invalid version
'123e4567-e89b-7-a456-426614174000', // Missing digits
];
for (const invalidId of invalidFormats) {
const fundResult = await mercuriusClient.query(Query_fund, {
variables: { input: { id: invalidId } },
});
expect(fundResult.errors).toBeDefined();
}
});

Copy link

github-actions bot commented Feb 9, 2025

Our Pull Request Approval Process

Thanks for contributing!

Testing Your Code

Remember, your PRs won't be reviewed until these criteria are met:

  1. We don't merge PRs with poor code quality.
    1. Follow coding best practices such that CodeRabbit.ai approves your PR.
  2. We don't merge PRs with failed tests.
    1. When tests fail, click on the Details link to learn more.
    2. Write sufficient tests for your changes (CodeCov Patch Test). Your testing level must be better than the target threshold of the repository
    3. Tests may fail if you edit sensitive files. Ask to add the ignore-sensitive-files-pr label if the edits are necessary.
  3. We cannot merge PRs with conflicting files. These must be fixed.

Our policies make our code better.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa7dfdc and f91d584.

📒 Files selected for processing (1)
  • test/routes/graphql/Query/fund.test.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Run tests for talawa api
  • GitHub Check: Analyse Code With CodeQL (typescript)
🔇 Additional comments (3)
test/routes/graphql/Query/fund.test.ts (3)

263-296: Remove duplicate test cases.

These test cases are duplicates of the tests at lines 107-132. Remove them to maintain a cleaner test suite and reduce test execution time.

Also applies to: 298-323


609-645: Remove duplicate helper function.

This createRegularUser function is a duplicate of the one at lines 136-162. Remove this duplicate and use the existing function.


37-567: Well-structured test suites with comprehensive coverage!

The test organization and coverage are excellent, including:

  • Clear test descriptions
  • Thorough validation of edge cases
  • Good separation of concerns in test suites

Comment on lines 136 to 162
async function createRegularUser() {
const adminAuthToken = await getAdminAuthToken();

// Create regular user as admin
const userResult = await mercuriusClient.mutate(Mutation_createUser, {
headers: {
authorization: `bearer ${adminAuthToken}`,
},
variables: {
input: {
emailAddress: `email${faker.string.uuid()}@test.com`,
password: "password123",
role: "regular",
name: "Test User",
isEmailAddressVerified: false,
},
},
});

assertToBeNonNullish(userResult.data?.createUser?.authenticationToken);
assertToBeNonNullish(userResult.data?.createUser?.user?.id);

return {
authToken: userResult.data.createUser.authenticationToken,
userId: userResult.data.createUser.user.id,
};
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Enhance helper functions with error handling and cleanup.

The helper functions could be improved by:

  1. Adding error handling for failed operations
  2. Adding cleanup functions to remove test data
  3. Adding retry logic for flaky operations

Here's an example improvement for the createRegularUser function:

 async function createRegularUser() {
+  try {
     const adminAuthToken = await getAdminAuthToken();
 
     // Create regular user as admin
     const userResult = await mercuriusClient.mutate(Mutation_createUser, {
       headers: {
         authorization: `bearer ${adminAuthToken}`,
       },
       variables: {
         input: {
           emailAddress: `email${faker.string.uuid()}@test.com`,
           password: "password123",
           role: "regular",
           name: "Test User",
           isEmailAddressVerified: false,
         },
       },
     });
 
     assertToBeNonNullish(userResult.data?.createUser?.authenticationToken);
     assertToBeNonNullish(userResult.data?.createUser?.user?.id);
 
     return {
       authToken: userResult.data.createUser.authenticationToken,
       userId: userResult.data.createUser.user.id,
+      cleanup: async () => {
+        // Add cleanup logic here
+        // e.g., delete the created user
+      }
     };
+  } catch (error) {
+    console.error('Failed to create regular user:', error);
+    throw error;
+  }
 }

Also applies to: 164-206, 208-223

});
});

suite("Required Feilds", () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Fix typo in suite name.

Correct the suite name from "Required Feilds" to "Required Fields".

-suite("Required Feilds", () => {
+suite("Required Fields", () => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
suite("Required Feilds", () => {
suite("Required Fields", () => {

Copy link

github-actions bot commented Feb 9, 2025

Our Pull Request Approval Process

Thanks for contributing!

Testing Your Code

Remember, your PRs won't be reviewed until these criteria are met:

  1. We don't merge PRs with poor code quality.
    1. Follow coding best practices such that CodeRabbit.ai approves your PR.
  2. We don't merge PRs with failed tests.
    1. When tests fail, click on the Details link to learn more.
    2. Write sufficient tests for your changes (CodeCov Patch Test). Your testing level must be better than the target threshold of the repository
    3. Tests may fail if you edit sensitive files. Ask to add the ignore-sensitive-files-pr label if the edits are necessary.
  3. We cannot merge PRs with conflicting files. These must be fixed.

Our policies make our code better.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

Copy link
Contributor

@palisadoes palisadoes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Please fix the failing tests and make coderabbit.ai approve your work
  2. The code coverage report shows no change in coverage:

Copy link

Our Pull Request Approval Process

Thanks for contributing!

Testing Your Code

Remember, your PRs won't be reviewed until these criteria are met:

  1. We don't merge PRs with poor code quality.
    1. Follow coding best practices such that CodeRabbit.ai approves your PR.
  2. We don't merge PRs with failed tests.
    1. When tests fail, click on the Details link to learn more.
    2. Write sufficient tests for your changes (CodeCov Patch Test). Your testing level must be better than the target threshold of the repository
    3. Tests may fail if you edit sensitive files. Ask to add the ignore-sensitive-files-pr label if the edits are necessary.
  3. We cannot merge PRs with conflicting files. These must be fixed.

Our policies make our code better.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
test/routes/graphql/Query/fund.test.ts (1)

298-358: Move duplicate test cases into their respective suites.

These test cases are duplicates of tests within the "Query field fund" suite. Consider moving them into the appropriate suite to maintain better organization and avoid redundancy.

Apply this diff to reorganize the tests:

-test("returns fund data if user is an admin", async () => {
-  // ... test implementation ...
-});
-
-test("returns fund data if user is organization member", async () => {
-  // ... test implementation ...
-});
+suite("Query field fund", () => {
+  // ... existing tests ...
+
+  test("returns fund data if user is an admin", async () => {
+    // ... test implementation ...
+  });
+
+  test("returns fund data if user is organization member", async () => {
+    // ... test implementation ...
+  });
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

suite("Query field fund", () => {
	// ... existing tests ...

	test("returns fund data if user is an admin", async () => {
		const adminSignInResult = await mercuriusClient.query(Query_signIn, {
			variables: {
				input: {
					emailAddress: server.envConfig.API_ADMINISTRATOR_USER_EMAIL_ADDRESS,
					password: server.envConfig.API_ADMINISTRATOR_USER_PASSWORD,
				},
			},
		});

		assertToBeNonNullish(adminSignInResult.data.signIn?.authenticationToken);

		const { fundId } = await createFund();

		const fundResult = await mercuriusClient.query(Query_fund, {
			headers: {
				authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
			},
			variables: {
				input: {
					id: fundId,
				},
			},
		});

		expect(fundResult.errors).toBeUndefined();
		expect(fundResult.data.fund).toEqual(
			expect.objectContaining({
				id: fundId,
				isTaxDeductible: expect.any(Boolean),
				name: expect.any(String),
			}),
		);
	});

	test("returns fund data if user is organization member", async () => {
		const regularUserResult = await createRegularUser();
		const { fundId, orgId } = await createFund();

		await addUserToOrg(regularUserResult.userId, orgId);

		const fundResult = await mercuriusClient.query(Query_fund, {
			headers: {
				authorization: `bearer ${regularUserResult.authToken}`,
			},
			variables: {
				input: {
					id: fundId,
				},
			},
		});

		expect(fundResult.errors).toBeUndefined();
		expect(fundResult.data.fund).toEqual(
			expect.objectContaining({
				id: fundId,
				isTaxDeductible: expect.any(Boolean),
				name: expect.any(String),
			}),
		);
	});
});
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f91d584 and 854b773.

📒 Files selected for processing (1)
  • test/routes/graphql/Query/fund.test.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Run tests for talawa api
  • GitHub Check: Analyse Code With CodeQL (typescript)
🔇 Additional comments (1)
test/routes/graphql/Query/fund.test.ts (1)

404-409: Verify if whitespace-only fund names should be allowed.

The test expects whitespace-only names to be valid, but this might lead to confusion and display issues. Consider if such names should be rejected or trimmed.

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Search for any validation or display logic related to fund names
ast-grep --pattern 'name: $name'

Length of output: 34


I'm not seeing any evidence of additional validation logic for trimming or rejecting whitespace-only fund names in the codebase. However, it's not clear whether this behavior is intentional. It would be helpful to verify the definition of fundsTableInsertSchema (likely defined using a schema validation library like zod) to see if any trimming or non-empty checks are applied to the "name" field.

Could you please confirm whether the business requirements allow whitespace-only names or if they should be rejected/trimmed? If whitespace-only names are undesired, you might need to add trimming or a custom validation rule to the schema.

Comment on lines 719 to 725
try {
// Add cleanup mutation here when available
// await mercuriusClient.mutate(Mutation_deleteUser, {
// headers: { authorization: `bearer ${adminAuthToken}` },
// variables: { input: { id: userId } }
// });
console.log(`Cleanup: User ${userId} would be deleted here`);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Implement cleanup functionality.

The cleanup functions are defined but not implemented, which could lead to test data accumulation. Consider implementing the cleanup mutations or adding a note about when they will be implemented.

Would you like me to help implement the cleanup mutations or create an issue to track this task?

Comment on lines 657 to 660
const RETRY_OPTIONS = {
maxRetries: 3,
delay: 1000, // ms
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider implementing exponential backoff for retries.

The current retry mechanism uses a fixed delay. Consider implementing exponential backoff to better handle transient failures.

Apply this diff to implement exponential backoff:

 const RETRY_OPTIONS = {
   maxRetries: 3,
-  delay: 1000, // ms
+  initialDelay: 1000, // ms
+  maxDelay: 5000, // ms
 };

Then update the retry function:

 async function retry<T>(
   operation: () => Promise<T>,
   options = RETRY_OPTIONS,
 ): Promise<T> {
   let lastError: Error | undefined;

   for (let attempt = 1; attempt <= options.maxRetries; attempt++) {
     try {
       return await operation();
     } catch (error) {
       lastError = error as Error;
       if (attempt < options.maxRetries) {
-        await new Promise((resolve) => setTimeout(resolve, options.delay));
+        const delay = Math.min(
+          options.initialDelay * Math.pow(2, attempt - 1),
+          options.maxDelay
+        );
+        await new Promise((resolve) => setTimeout(resolve, delay));
         console.warn(`Retry attempt ${attempt} after error:`, error);
       }
     }
   }

   throw lastError;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const RETRY_OPTIONS = {
maxRetries: 3,
delay: 1000, // ms
};
const RETRY_OPTIONS = {
maxRetries: 3,
initialDelay: 1000, // ms
maxDelay: 5000, // ms
};
async function retry<T>(
operation: () => Promise<T>,
options = RETRY_OPTIONS,
): Promise<T> {
let lastError: Error | undefined;
for (let attempt = 1; attempt <= options.maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error as Error;
if (attempt < options.maxRetries) {
const delay = Math.min(
options.initialDelay * Math.pow(2, attempt - 1),
options.maxDelay
);
await new Promise((resolve) => setTimeout(resolve, delay));
console.warn(`Retry attempt ${attempt} after error:`, error);
}
}
}
throw lastError;
}

Copy link

Our Pull Request Approval Process

Thanks for contributing!

Testing Your Code

Remember, your PRs won't be reviewed until these criteria are met:

  1. We don't merge PRs with poor code quality.
    1. Follow coding best practices such that CodeRabbit.ai approves your PR.
  2. We don't merge PRs with failed tests.
    1. When tests fail, click on the Details link to learn more.
    2. Write sufficient tests for your changes (CodeCov Patch Test). Your testing level must be better than the target threshold of the repository
    3. Tests may fail if you edit sensitive files. Ask to add the ignore-sensitive-files-pr label if the edits are necessary.
  3. We cannot merge PRs with conflicting files. These must be fixed.

Our policies make our code better.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 854b773 and d635e1b.

📒 Files selected for processing (1)
  • test/routes/graphql/Query/fund.test.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Run tests for talawa api
  • GitHub Check: Analyse Code With CodeQL (typescript)
🔇 Additional comments (2)
test/routes/graphql/Query/fund.test.ts (2)

39-41: Well-organized test structure!

The test suites are logically organized with clear separation of concerns:

  • Error cases for unauthorized access
  • Success cases for authorized users
  • Schema validation
  • UUID validation
  • Required fields validation

Also applies to: 180-181, 243-244, 383-384, 460-461


243-524: Excellent validation test coverage!

The validation tests thoroughly cover:

  • Name constraints (length, special chars, Unicode)
  • UUID format and version validation
  • Required fields and their constraints

Comment on lines +40 to +178
},
},
});

expect(fundResult.data.fund).toEqual(null);
expect(fundResult.errors).toEqual(
expect.arrayContaining<TalawaGraphQLFormattedError>([
expect.objectContaining<TalawaGraphQLFormattedError>({
extensions: expect.objectContaining<UnauthenticatedExtensions>({
code: "unauthenticated",
}),
message: expect.any(String),
path: ["fund"],
}),
]),
);
});

test("with 'arguments_associated_resources_not_found' extensions code if fund not found", async () => {
const adminSignInResult = await mercuriusClient.query(Query_signIn, {
variables: {
input: {
emailAddress: server.envConfig.API_ADMINISTRATOR_USER_EMAIL_ADDRESS,
password: server.envConfig.API_ADMINISTRATOR_USER_PASSWORD,
},
},
});

assertToBeNonNullish(adminSignInResult.data.signIn?.authenticationToken);

const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
},
variables: {
input: {
id: faker.string.uuid(),
},
},
});

expect(fundResult.data.fund).toEqual(null);
expect(fundResult.errors).toEqual(
expect.arrayContaining<TalawaGraphQLFormattedError>([
expect.objectContaining<TalawaGraphQLFormattedError>({
extensions:
expect.objectContaining<ArgumentsAssociatedResourcesNotFoundExtensions>(
{
code: "arguments_associated_resources_not_found",
issues: [
{
argumentPath: ["input", "id"],
},
],
},
),
message: expect.any(String),
path: ["fund"],
}),
]),
);
});

test("with 'unauthorized_action_on_arguments_associated_resources' if non-admin user is not a member of fund's organization", async () => {
const regularUserResult = await createRegularUser();
const { fundId } = await createFund();

const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${regularUserResult.authToken}`,
},
variables: {
input: {
id: fundId,
},
},
});

expect(fundResult.data.fund).toEqual(null);
expect(fundResult.errors).toEqual(
expect.arrayContaining<TalawaGraphQLFormattedError>([
expect.objectContaining<TalawaGraphQLFormattedError>({
extensions:
expect.objectContaining<UnauthorizedActionOnArgumentsAssociatedResourcesExtensions>(
{
code: "unauthorized_action_on_arguments_associated_resources",
issues: [
{
argumentPath: ["input", "id"],
},
],
},
),
message: expect.any(String),
path: ["fund"],
}),
]),
);
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider adding more error scenarios.

While the error handling is comprehensive, consider adding these edge cases:

  • Malformed authorization token
  • Expired authorization token
  • Rate limiting scenarios

Comment on lines +180 to +240
test("returns fund data if user is organization member", async () => {
const regularUserResult = await createRegularUser();
const { fundId, orgId } = await createFund();

await addUserToOrg(regularUserResult.userId, orgId);

const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${regularUserResult.authToken}`,
},
variables: {
input: {
id: fundId,
},
},
});

expect(fundResult.errors).toBeUndefined();
expect(fundResult.data.fund).toEqual(
expect.objectContaining({
id: fundId,
isTaxDeductible: expect.any(Boolean),
name: expect.any(String),
}),
);
});

test("returns fund data if user is an admin", async () => {
const adminSignInResult = await mercuriusClient.query(Query_signIn, {
variables: {
input: {
emailAddress: server.envConfig.API_ADMINISTRATOR_USER_EMAIL_ADDRESS,
password: server.envConfig.API_ADMINISTRATOR_USER_PASSWORD,
},
},
});

assertToBeNonNullish(adminSignInResult.data.signIn?.authenticationToken);

const { fundId } = await createFund();

const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${adminSignInResult.data.signIn.authenticationToken}`,
},
variables: {
input: {
id: fundId,
},
},
});

expect(fundResult.errors).toBeUndefined();
expect(fundResult.data.fund).toEqual(
expect.objectContaining({
id: fundId,
isTaxDeductible: expect.any(Boolean),
name: expect.any(String),
}),
);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider adding more success scenarios.

While the basic success cases are covered, consider adding tests for:

  • Funds with null/empty optional fields
  • Funds with maximum length names
  • Multiple funds in the same organization

Comment on lines 526 to 753
const orgId = createOrgResult.data.createOrganization.id;

// Create fund
const createFundResult = await mercuriusClient.mutate(
Mutation_createFund,
{
headers: {
authorization: `bearer ${adminAuthToken}`,
},
variables: {
input: {
name: `Fund ${faker.string.uuid()}`,
organizationId: orgId,
isTaxDeductible: false,
},
},
},
);

if (!createFundResult.data?.createFund?.id) {
throw new Error("Failed to create fund: Missing fund ID");
}

const fundId = createFundResult.data.createFund.id;

return {
fundId,
orgId,
cleanup: async () => {
try {
// Add cleanup mutations here when available
// First delete fund, then organization
// await mercuriusClient.mutate(Mutation_deleteFund, {
// headers: { authorization: `bearer ${adminAuthToken}` },
// variables: { input: { id: fundId } }
// });
// await mercuriusClient.mutate(Mutation_deleteOrganization, {
// headers: { authorization: `bearer ${adminAuthToken}` },
// variables: { input: { id: orgId } }
// });
console.log(
`Cleanup: Fund ${fundId} and Org ${orgId} would be deleted here`,
);
} catch (error) {
console.error("Failed to cleanup fund and organization:", error);
throw error;
}
},
};
});
} catch (error) {
console.error("Failed to create fund:", error);
throw error;
}
}

async function addUserToOrg(
userId: string,
orgId: string,
): Promise<{ cleanup: () => Promise<void> }> {
try {
return await retry(async () => {
const adminAuthToken = await getAdminAuthToken();

await mercuriusClient.mutate(Mutation_createOrganizationMembership, {
headers: {
authorization: `bearer ${adminAuthToken}`,
},
variables: {
input: {
memberId: userId,
organizationId: orgId,
role: "regular",
},
},
});

return {
cleanup: async () => {
try {
// Add cleanup mutation here when available
// await mercuriusClient.mutate(Mutation_deleteOrganizationMembership, {
// headers: { authorization: `bearer ${adminAuthToken}` },
// variables: { input: { memberId: userId, organizationId: orgId } }
// });
console.log(
`Cleanup: Membership for user ${userId} in org ${orgId} would be deleted here`,
);
} catch (error) {
console.error("Failed to cleanup organization membership:", error);
throw error;
}
},
};
});
} catch (error) {
console.error("Failed to add user to organization:", error);
throw error;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Implement cleanup functionality.

The cleanup functions are defined but not implemented. This could lead to test data accumulation. Consider implementing the cleanup mutations or adding a note about when they will be implemented.

Would you like me to help implement the cleanup mutations or create an issue to track this task?

Copy link

Our Pull Request Approval Process

Thanks for contributing!

Testing Your Code

Remember, your PRs won't be reviewed until these criteria are met:

  1. We don't merge PRs with poor code quality.
    1. Follow coding best practices such that CodeRabbit.ai approves your PR.
  2. We don't merge PRs with failed tests.
    1. When tests fail, click on the Details link to learn more.
    2. Write sufficient tests for your changes (CodeCov Patch Test). Your testing level must be better than the target threshold of the repository
    3. Tests may fail if you edit sensitive files. Ask to add the ignore-sensitive-files-pr label if the edits are necessary.
  3. We cannot merge PRs with conflicting files. These must be fixed.

Our policies make our code better.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (3)
test/routes/graphql/Query/fund.test.ts (3)

749-757: Implement cleanup functionality.

The cleanup functions are defined but not implemented. This could lead to test data accumulation.

Would you like me to help implement the cleanup mutations or create an issue to track this task?


683-712: Consider implementing exponential backoff for retries.

The current retry mechanism uses a fixed delay. Consider implementing exponential backoff to better handle transient failures.

Apply this diff to implement exponential backoff:

 const RETRY_OPTIONS = {
   maxRetries: 3,
-  initialDelay: 1000, // ms
-  maxDelay: 5000, // ms
+  initialDelay: 100, // ms
+  maxDelay: 1000, // ms
+  backoffFactor: 2,
 };

603-603: Fix typo in suite name.

Correct the suite name from "Required Fields" to "Required Field Validation".

-suite("Required Fields", () => {
+suite("Required Field Validation", () => {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d635e1b and 94f1136.

📒 Files selected for processing (1)
  • test/routes/graphql/Query/fund.test.ts (1 hunks)
🧰 Additional context used
🪛 Gitleaks (8.21.2)
test/routes/graphql/Query/fund.test.ts

209-210: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


210-210: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

🪛 GitHub Check: CodeQL
test/routes/graphql/Query/fund.test.ts

[failure] 184-184: Hard-coded credentials
The hard-coded value "malformed_token" is used as authorization header.


[failure] 210-210: Hard-coded credentials
The hard-coded value "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjF9.DlOeaAiW9JJthDkhcLvMoJqFWZkiKnYAg4gXxHl6HN4" is used as authorization header.

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: CodeQL
  • GitHub Check: Analyse Code With CodeQL (typescript)

Comment on lines 604 to 667
test("validates required fund fields", () => {
const validInput = {
name: "Test Fund",
isTaxDeductible: false,
organizationId: faker.string.uuid(),
};

// Test missing name
const missingName = { ...validInput, name: undefined };
expect(fundsTableInsertSchema.safeParse(missingName).success).toBe(false);

// Test missing isTaxDeductible
const missingTaxStatus = { ...validInput, isTaxDeductible: undefined };
expect(fundsTableInsertSchema.safeParse(missingTaxStatus).success).toBe(
false,
);

// Test missing organizationId
const missingOrgId = { ...validInput, organizationId: undefined };
expect(fundsTableInsertSchema.safeParse(missingOrgId).success).toBe(false);

// Valid data should pass
expect(fundsTableInsertSchema.safeParse(validInput).success).toBe(true);
});

test("validates fund field constraints", () => {
const validInput = {
name: "Test Fund",
isTaxDeductible: false,
organizationId: faker.string.uuid(),
};

// Test name constraints
expect(
fundsTableInsertSchema.safeParse({
...validInput,
name: "",
}).success,
).toBe(false);

expect(
fundsTableInsertSchema.safeParse({
...validInput,
name: "a".repeat(257),
}).success,
).toBe(false);

// Test isTaxDeductible must be boolean
expect(
fundsTableInsertSchema.safeParse({
...validInput,
isTaxDeductible: "true",
}).success,
).toBe(false);

// Test organizationId must be UUID
expect(
fundsTableInsertSchema.safeParse({
...validInput,
organizationId: "invalid-uuid",
}).success,
).toBe(false);
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Enhance test descriptions for clarity.

The test descriptions could be more descriptive to better communicate their purpose.

-test("validates required fund fields", () => {
+test("validates that all required fund fields (name, isTaxDeductible, organizationId) are present", () => {

-test("validates fund field constraints", () => {
+test("validates field constraints: name length, isTaxDeductible type, and organizationId format", () => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test("validates required fund fields", () => {
const validInput = {
name: "Test Fund",
isTaxDeductible: false,
organizationId: faker.string.uuid(),
};
// Test missing name
const missingName = { ...validInput, name: undefined };
expect(fundsTableInsertSchema.safeParse(missingName).success).toBe(false);
// Test missing isTaxDeductible
const missingTaxStatus = { ...validInput, isTaxDeductible: undefined };
expect(fundsTableInsertSchema.safeParse(missingTaxStatus).success).toBe(
false,
);
// Test missing organizationId
const missingOrgId = { ...validInput, organizationId: undefined };
expect(fundsTableInsertSchema.safeParse(missingOrgId).success).toBe(false);
// Valid data should pass
expect(fundsTableInsertSchema.safeParse(validInput).success).toBe(true);
});
test("validates fund field constraints", () => {
const validInput = {
name: "Test Fund",
isTaxDeductible: false,
organizationId: faker.string.uuid(),
};
// Test name constraints
expect(
fundsTableInsertSchema.safeParse({
...validInput,
name: "",
}).success,
).toBe(false);
expect(
fundsTableInsertSchema.safeParse({
...validInput,
name: "a".repeat(257),
}).success,
).toBe(false);
// Test isTaxDeductible must be boolean
expect(
fundsTableInsertSchema.safeParse({
...validInput,
isTaxDeductible: "true",
}).success,
).toBe(false);
// Test organizationId must be UUID
expect(
fundsTableInsertSchema.safeParse({
...validInput,
organizationId: "invalid-uuid",
}).success,
).toBe(false);
});
});
test("validates that all required fund fields (name, isTaxDeductible, organizationId) are present", () => {
const validInput = {
name: "Test Fund",
isTaxDeductible: false,
organizationId: faker.string.uuid(),
};
// Test missing name
const missingName = { ...validInput, name: undefined };
expect(fundsTableInsertSchema.safeParse(missingName).success).toBe(false);
// Test missing isTaxDeductible
const missingTaxStatus = { ...validInput, isTaxDeductible: undefined };
expect(fundsTableInsertSchema.safeParse(missingTaxStatus).success).toBe(false);
// Test missing organizationId
const missingOrgId = { ...validInput, organizationId: undefined };
expect(fundsTableInsertSchema.safeParse(missingOrgId).success).toBe(false);
// Valid data should pass
expect(fundsTableInsertSchema.safeParse(validInput).success).toBe(true);
});
test("validates field constraints: name length, isTaxDeductible type, and organizationId format", () => {
const validInput = {
name: "Test Fund",
isTaxDeductible: false,
organizationId: faker.string.uuid(),
};
// Test name constraints
expect(
fundsTableInsertSchema.safeParse({
...validInput,
name: "",
}).success,
).toBe(false);
expect(
fundsTableInsertSchema.safeParse({
...validInput,
name: "a".repeat(257),
}).success,
).toBe(false);
// Test isTaxDeductible must be boolean
expect(
fundsTableInsertSchema.safeParse({
...validInput,
isTaxDeductible: "true",
}).success,
).toBe(false);
// Test organizationId must be UUID
expect(
fundsTableInsertSchema.safeParse({
...validInput,
organizationId: "invalid-uuid",
}).success,
).toBe(false);
});
});

Comment on lines 180 to 235
test("with 'unauthenticated' extensions code if authorization token is malformed", async () => {
const fundId = faker.string.uuid();
const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
authorization: "malformed_token",
},
variables: {
input: {
id: fundId,
},
},
});

expect(fundResult.data.fund).toEqual(null);
expect(fundResult.errors).toEqual(
expect.arrayContaining<TalawaGraphQLFormattedError>([
expect.objectContaining<TalawaGraphQLFormattedError>({
extensions: expect.objectContaining<UnauthenticatedExtensions>({
code: "unauthenticated",
}),
message: expect.any(String),
path: ["fund"],
}),
]),
);
});

test("with 'unauthenticated' extensions code if authorization token is expired", async () => {
const fundId = faker.string.uuid();
const expiredToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjF9.DlOeaAiW9JJthDkhcLvMoJqFWZkiKnYAg4gXxHl6HN4";

const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${expiredToken}`,
},
variables: {
input: {
id: fundId,
},
},
});

expect(fundResult.data.fund).toEqual(null);
expect(fundResult.errors).toEqual(
expect.arrayContaining<TalawaGraphQLFormattedError>([
expect.objectContaining<TalawaGraphQLFormattedError>({
extensions: expect.objectContaining<UnauthenticatedExtensions>({
code: "unauthenticated",
}),
message: expect.any(String),
path: ["fund"],
}),
]),
);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Document test tokens and their purpose.

The test uses hard-coded tokens for testing error cases. While this is acceptable for tests, it's good practice to document their purpose.

Add comments to explain the test tokens:

 test("with 'unauthenticated' extensions code if authorization token is malformed", async () => {
   const fundId = faker.string.uuid();
   const fundResult = await mercuriusClient.query(Query_fund, {
     headers: {
+      // Using a malformed token to test error handling
       authorization: "malformed_token",
     },
     variables: {
       input: {
         id: fundId,
       },
     },
   });
   // ... rest of the test
 });

 test("with 'unauthenticated' extensions code if authorization token is expired", async () => {
   const fundId = faker.string.uuid();
+  // Using an expired JWT for testing (exp: 1)
   const expiredToken =
     "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjF9.DlOeaAiW9JJthDkhcLvMoJqFWZkiKnYAg4gXxHl6HN4";
   // ... rest of the test
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test("with 'unauthenticated' extensions code if authorization token is malformed", async () => {
const fundId = faker.string.uuid();
const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
authorization: "malformed_token",
},
variables: {
input: {
id: fundId,
},
},
});
expect(fundResult.data.fund).toEqual(null);
expect(fundResult.errors).toEqual(
expect.arrayContaining<TalawaGraphQLFormattedError>([
expect.objectContaining<TalawaGraphQLFormattedError>({
extensions: expect.objectContaining<UnauthenticatedExtensions>({
code: "unauthenticated",
}),
message: expect.any(String),
path: ["fund"],
}),
]),
);
});
test("with 'unauthenticated' extensions code if authorization token is expired", async () => {
const fundId = faker.string.uuid();
const expiredToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjF9.DlOeaAiW9JJthDkhcLvMoJqFWZkiKnYAg4gXxHl6HN4";
const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${expiredToken}`,
},
variables: {
input: {
id: fundId,
},
},
});
expect(fundResult.data.fund).toEqual(null);
expect(fundResult.errors).toEqual(
expect.arrayContaining<TalawaGraphQLFormattedError>([
expect.objectContaining<TalawaGraphQLFormattedError>({
extensions: expect.objectContaining<UnauthenticatedExtensions>({
code: "unauthenticated",
}),
message: expect.any(String),
path: ["fund"],
}),
]),
);
});
test("with 'unauthenticated' extensions code if authorization token is malformed", async () => {
const fundId = faker.string.uuid();
const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
// Using a malformed token to test error handling
authorization: "malformed_token",
},
variables: {
input: {
id: fundId,
},
},
});
expect(fundResult.data.fund).toEqual(null);
expect(fundResult.errors).toEqual(
expect.arrayContaining<TalawaGraphQLFormattedError>([
expect.objectContaining<TalawaGraphQLFormattedError>({
extensions: expect.objectContaining<UnauthenticatedExtensions>({
code: "unauthenticated",
}),
message: expect.any(String),
path: ["fund"],
}),
]),
);
});
test("with 'unauthenticated' extensions code if authorization token is expired", async () => {
const fundId = faker.string.uuid();
// Using an expired JWT for testing (exp: 1)
const expiredToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjF9.DlOeaAiW9JJthDkhcLvMoJqFWZkiKnYAg4gXxHl6HN4";
const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${expiredToken}`,
},
variables: {
input: {
id: fundId,
},
},
});
expect(fundResult.data.fund).toEqual(null);
expect(fundResult.errors).toEqual(
expect.arrayContaining<TalawaGraphQLFormattedError>([
expect.objectContaining<TalawaGraphQLFormattedError>({
extensions: expect.objectContaining<UnauthenticatedExtensions>({
code: "unauthenticated",
}),
message: expect.any(String),
path: ["fund"],
}),
]),
);
});
🧰 Tools
🪛 Gitleaks (8.21.2)

209-210: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


210-210: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

🪛 GitHub Check: CodeQL

[failure] 184-184: Hard-coded credentials
The hard-coded value "malformed_token" is used as authorization header.


[failure] 210-210: Hard-coded credentials
The hard-coded value "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjF9.DlOeaAiW9JJthDkhcLvMoJqFWZkiKnYAg4gXxHl6HN4" is used as authorization header.

Comment on lines +325 to +340
test("returns fund with expected fields", async () => {
const adminAuthToken = await getAdminAuthToken();
const { fundId } = await createFund();

const fundResult = await mercuriusClient.query(Query_fund, {
headers: { authorization: `bearer ${adminAuthToken}` },
variables: { input: { id: fundId } },
});

expect(fundResult.errors).toBeUndefined();
expect(fundResult.data?.fund).toMatchObject({
id: fundId,
isTaxDeductible: false,
name: expect.any(String),
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Enhance fund fields test coverage.

The current test only verifies basic fields. Consider adding tests for:

  • Null/empty optional fields
  • Fields with special characters
  • Fields with maximum length values
 test("returns fund with expected fields", async () => {
   const adminAuthToken = await getAdminAuthToken();
-  const { fundId } = await createFund();
+  
+  // Create funds with various field values
+  const funds = await Promise.all([
+    createFund(), // Default fund
+    createFundWithMaxLengthName(),
+    createFundWithSpecialChars(),
+  ]);
 
-  const fundResult = await mercuriusClient.query(Query_fund, {
-    headers: { authorization: `bearer ${adminAuthToken}` },
-    variables: { input: { id: fundId } },
-  });
+  // Test each fund
+  for (const { fundId } of funds) {
+    const fundResult = await mercuriusClient.query(Query_fund, {
+      headers: { authorization: `bearer ${adminAuthToken}` },
+      variables: { input: { id: fundId } },
+    });
 
-  expect(fundResult.errors).toBeUndefined();
-  expect(fundResult.data?.fund).toMatchObject({
-    id: fundId,
-    isTaxDeductible: false,
-    name: expect.any(String),
-  });
+    expect(fundResult.errors).toBeUndefined();
+    expect(fundResult.data?.fund).toMatchObject({
+      id: expect.any(String),
+      isTaxDeductible: expect.any(Boolean),
+      name: expect.any(String),
+    });
+  }
 });

Committable suggestion skipped: line range outside the PR's diff.

Copy link

Our Pull Request Approval Process

Thanks for contributing!

Testing Your Code

Remember, your PRs won't be reviewed until these criteria are met:

  1. We don't merge PRs with poor code quality.
    1. Follow coding best practices such that CodeRabbit.ai approves your PR.
  2. We don't merge PRs with failed tests.
    1. When tests fail, click on the Details link to learn more.
    2. Write sufficient tests for your changes (CodeCov Patch Test). Your testing level must be better than the target threshold of the repository
    3. Tests may fail if you edit sensitive files. Ask to add the ignore-sensitive-files-pr label if the edits are necessary.
  3. We cannot merge PRs with conflicting files. These must be fixed.

Our policies make our code better.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

@palisadoes
Copy link
Contributor

Please fix the failing tests

Copy link

Our Pull Request Approval Process

Thanks for contributing!

Testing Your Code

Remember, your PRs won't be reviewed until these criteria are met:

  1. We don't merge PRs with poor code quality.
    1. Follow coding best practices such that CodeRabbit.ai approves your PR.
  2. We don't merge PRs with failed tests.
    1. When tests fail, click on the Details link to learn more.
    2. Write sufficient tests for your changes (CodeCov Patch Test). Your testing level must be better than the target threshold of the repository
    3. Tests may fail if you edit sensitive files. Ask to add the ignore-sensitive-files-pr label if the edits are necessary.
  3. We cannot merge PRs with conflicting files. These must be fixed.

Our policies make our code better.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb707d7 and 5d584f7.

📒 Files selected for processing (3)
  • test/routes/graphql/Query/fund.test.ts (1 hunks)
  • test/routes/graphql/documentNodes.ts (1 hunks)
  • test/routes/graphql/gql.tada-cache.d.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Run tests for talawa api
  • GitHub Check: Analyse Code With CodeQL (typescript)
🔇 Additional comments (25)
test/routes/graphql/gql.tada-cache.d.ts (5)

33-34: Good addition of Query_fund.
Nicely typed return object for the query. Just ensure that the consumer of this query properly handles the possibility of isTaxDeductible or name being null.


35-36: Mutation_createOrganization looks consistent.
The returned fields accurately reflect the creation response. Verify whether additional organization fields (like address or metadata) need to be returned for your use case.


37-38: Mutation_createFund is aligned, but double-check nullability.
Be sure to confirm whether isTaxDeductible can be null in every case or if it should always be a boolean. This helps avoid confusion in front-end handling.


41-42: Deletion mutation for Fund looks good.
The typed structure lines up with the rest of the schema for fund deletion.


45-46: Deletion mutation for OrganizationMembership is appropriate.
The typed structure for returning id, name, and countryCode is consistent with the GraphQL definitions.

test/routes/graphql/documentNodes.ts (5)

310-317: New Query_fund definition is consistent.
Fetching id, isTaxDeductible, and name aligns with the usage in tests.


328-336: Mutation_createFund is coherent with the fund structure.
Ensure consumers of this mutation handle partial or null values gracefully.


344-352: Mutation_deleteFund is appropriately typed.
No issues spotted. The name, id, and isTaxDeductible fields match the new fund schema.


353-361: Mutation_deleteOrganization is straightforward.
Looks good for confirming which organization was removed.


362-370: Mutation_deleteOrganizationMembership.
The returned fields (id, name, countryCode) appear to align with your membership object shape.

test/routes/graphql/Query/fund.test.ts (15)

1-28: Imports and type references are well-organized.
They provide good modularity without extraneous references.


29-41: getAdminAuthToken function ensures admin credentials are fetched just-in-time.
This is a good approach to reduce repeated code, but verify that environment variables exist and are validated.


43-182: Robust negative test coverage in 'Query field fund' suite.
You're checking multiple error states (unauthenticated, resource not found, unauthorized, etc.). This thoroughly ensures correct GraphQL error handling.


184-214: Malformed authorization token test.
Generating a random invalid token is a secure practice to avoid storing real secrets in tests.


216-253: Expired token test logic is clear.
Constructing an expired JWT payload in-line is a neat approach to test exact expiration times.


281-306: Fund retrieval tests validate membership-based access.
They confirm correct fund data for properly authorized regular users. Nicely done.


308-341: Admin-based fund retrieval is similarly validated.
Ensures that the admin role bypasses membership constraints.


343-358: Fund fields verification test.
Checks essential properties of the fund object. This complements earlier negative tests nicely.


360-401: Maximum length fund name test.
Great approach to validate boundary conditions around name length.


404-476: Fund name constraints and trimming checks.
Good coverage of short, long, special chars, whitespace, and trimming. This ensures robust validation logic.


478-541: Unique constraint on fund name.
Properly tests duplicates within the same organization. Confirm that the forbidden_action_on_arguments_associated_resources error aligns with your desired semantic for duplicates.


544-619: UUID Validation suite thoroughly checks multiple scenarios.
Covers invalid formats, case sensitivity, and incorrect versions. Excellent coverage for your new v7 approach.


621-646: Required fields test.
Validates that all fields are present and flags any omissions. This is consistent with your schema-level requirements.


647-684: Additional field constraints test.
Ensures invalid types and constraints are rejected. This pairs well with the earlier name constraint checks.


687-933: Helper functions for test setup and cleanup.
Exponential backoff in retry is a nice addition. The createRegularUser, createFund, and addUserToOrg patterns promote DRY code.

Comment on lines +39 to +40
"mutation Mutation_createOrganizationMembership($input: MutationCreateOrganizationMembershipInput!) {\n createOrganizationMembership(input: $input) {\n id\n }\n }":
TadaDocumentNode<{ createOrganizationMembership: { id: string; } | null; }, { input: { role?: "administrator" | "regular" | null | undefined; organizationId: string; memberId: string; }; }, void>;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider returning more membership fields if needed.
Currently, the membership payload only includes id. If your tests or UX require more membership info (like roles, invitation status, etc.), you might expand this response.

Comment on lines +43 to +44
"mutation Mutation_deleteOrganization($input: MutationDeleteOrganizationInput!) {\n deleteOrganization(input: $input) {\n id\n name\n countryCode\n }\n}":
TadaDocumentNode<{ deleteOrganization: { id: string; name: string | null; countryCode: "at" | "pg" | "ad" | "ae" | "af" | "ag" | "ai" | "al" | "am" | "ao" | "aq" | "ar" | "as" | "au" | "aw" | "ax" | "az" | "ba" | "bb" | "bd" | "be" | "bf" | "bg" | "bh" | "bi" | "bj" | "bl" | "bm" | "bn" | "bo" | "bq" | "br" | "bs" | "bt" | "bv" | "bw" | "by" | "bz" | "ca" | "cc" | "cd" | "cf" | "cg" | "ch" | "ci" | "ck" | "cl" | "cm" | "cn" | "co" | "cr" | "cu" | "cv" | "cw" | "cx" | "cy" | "cz" | "de" | "dj" | "dk" | "dm" | "do" | "dz" | "ec" | "ee" | "eg" | "eh" | "er" | "es" | "et" | "fi" | "fj" | "fk" | "fm" | "fo" | "fr" | "ga" | "gb" | "gd" | "ge" | "gf" | "gg" | "gh" | "gi" | "gl" | "gm" | "gn" | "gp" | "gq" | "gr" | "gs" | "gt" | "gu" | "gw" | "gy" | "hk" | "hm" | "hn" | "hr" | "ht" | "hu" | "id" | "ie" | "il" | "im" | "in" | "io" | "iq" | "ir" | "is" | "it" | "je" | "jm" | "jo" | "jp" | "ke" | "kg" | "kh" | "ki" | "km" | "kn" | "kp" | "kr" | "kw" | "ky" | "kz" | "la" | "lb" | "lc" | "li" | "lk" | "lr" | "ls" | "lt" | "lu" | "lv" | "ly" | "ma" | "mc" | "md" | "me" | "mf" | "mg" | "mh" | "mk" | "ml" | "mm" | "mn" | "mo" | "mp" | "mq" | "mr" | "ms" | "mt" | "mu" | "mv" | "mw" | "mx" | "my" | "mz" | "na" | "nc" | "ne" | "nf" | "ng" | "ni" | "nl" | "no" | "np" | "nr" | "nu" | "nz" | "om" | "pa" | "pe" | "pf" | "ph" | "pk" | "pl" | "pm" | "pn" | "pr" | "ps" | "pt" | "pw" | "py" | "qa" | "re" | "ro" | "rs" | "ru" | "rw" | "sa" | "sb" | "sc" | "sd" | "se" | "sg" | "sh" | "si" | "sj" | "sk" | "sl" | "sm" | "sn" | "so" | "sr" | "ss" | "st" | "sv" | "sx" | "sy" | "sz" | "tc" | "td" | "tf" | "tg" | "th" | "tj" | "tk" | "tl" | "tm" | "tn" | "to" | "tr" | "tt" | "tv" | "tw" | "tz" | "ua" | "ug" | "um" | "us" | "uy" | "uz" | "va" | "vc" | "ve" | "vg" | "vi" | "vn" | "vu" | "wf" | "ws" | "ye" | "yt" | "za" | "zm" | "zw" | null; } | null; }, { input: { id: string; }; }, void>;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Verify that only these fields are needed after deleting the Organization.
Sometimes, you may want to confirm success or keep track of timestamps. As it stands, this is fine if minimal info is sufficient.

Comment on lines +319 to +327
export const Mutation_createOrganization =
gql(`mutation Mutation_createOrganization($input: MutationCreateOrganizationInput!) {
createOrganization(input: $input) {
id
name
countryCode
}
}`);

Copy link

@coderabbitai coderabbitai bot Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Mutation_createOrganization is well-defined.
The new organization creation fields match typical organization data. Consider whether you need additional organization metadata (like addressLine1, addressLine2) returned.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for it

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yugal07 Understood, we'll keep it as is with the current fields.


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines +337 to +343
export const Mutation_createOrganizationMembership =
gql(`mutation Mutation_createOrganizationMembership($input: MutationCreateOrganizationMembershipInput!) {
createOrganizationMembership(input: $input) {
id
}
}`);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Mutation_createOrganizationMembership has a minimal response.
You may eventually want to return the organization name or membership role for clarity, but this is sufficient for basic creation flow.

Comment on lines 255 to 279
test("with 'too_many_requests' extensions code when rate limit is exceeded", async () => {
const fundId = faker.string.uuid();
const adminAuthToken = await getAdminAuthToken();

const results = await Promise.all(
Array.from({ length: 10 }, () =>
mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${adminAuthToken}`,
},
variables: {
input: {
id: fundId,
},
},
}),
),
);

const lastResult = results.at(-1);
expect(lastResult?.errors).toBeDefined();
expect(lastResult?.errors?.[0]?.extensions?.code).toBe(
"arguments_associated_resources_not_found",
);
});
Copy link

@coderabbitai coderabbitai bot Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Mismatch between test description and expected error code.
You describe expecting "too_many_requests", but the assertion checks for "arguments_associated_resources_not_found". This is contradictory.

Proposed fix:

-  expect(lastResult?.errors?.[0]?.extensions?.code).toBe(
-    "arguments_associated_resources_not_found",
-  );
+  expect(lastResult?.errors?.[0]?.extensions?.code).toBe(
+    "too_many_requests",
+  );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test("with 'too_many_requests' extensions code when rate limit is exceeded", async () => {
const fundId = faker.string.uuid();
const adminAuthToken = await getAdminAuthToken();
const results = await Promise.all(
Array.from({ length: 10 }, () =>
mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${adminAuthToken}`,
},
variables: {
input: {
id: fundId,
},
},
}),
),
);
const lastResult = results.at(-1);
expect(lastResult?.errors).toBeDefined();
expect(lastResult?.errors?.[0]?.extensions?.code).toBe(
"arguments_associated_resources_not_found",
);
});
test("with 'too_many_requests' extensions code when rate limit is exceeded", async () => {
const fundId = faker.string.uuid();
const adminAuthToken = await getAdminAuthToken();
const results = await Promise.all(
Array.from({ length: 10 }, () =>
mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${adminAuthToken}`,
},
variables: {
input: {
id: fundId,
},
},
}),
),
);
const lastResult = results.at(-1);
expect(lastResult?.errors).toBeDefined();
expect(lastResult?.errors?.[0]?.extensions?.code).toBe(
"too_many_requests",
);
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a valid request

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@palisadoes Thank you for confirming! Yes, updating the assertion to expect "too_many_requests" will make the test consistent with its description.

(`°ω°)⊃━☆゚.*・。゚

Copy link

Our Pull Request Approval Process

Thanks for contributing!

Testing Your Code

Remember, your PRs won't be reviewed until these criteria are met:

  1. We don't merge PRs with poor code quality.
    1. Follow coding best practices such that CodeRabbit.ai approves your PR.
  2. We don't merge PRs with failed tests.
    1. When tests fail, click on the Details link to learn more.
    2. Write sufficient tests for your changes (CodeCov Patch Test). Your testing level must be better than the target threshold of the repository
    3. Tests may fail if you edit sensitive files. Ask to add the ignore-sensitive-files-pr label if the edits are necessary.
  3. We cannot merge PRs with conflicting files. These must be fixed.

Our policies make our code better.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
test/routes/graphql/Query/fund.test.ts (1)

255-279: ⚠️ Potential issue

Fix mismatch between test description and expected error code.

The test description suggests checking for rate limiting ("when rate limit is exceeded"), but the assertion checks for "arguments_associated_resources_not_found". This is contradictory.

Apply this diff to fix the assertion:

-expect(lastResult?.errors?.[0]?.extensions?.code).toBe(
-  "arguments_associated_resources_not_found",
-);
+expect(lastResult?.errors?.[0]?.extensions?.code).toBe(
+  "too_many_requests",
+);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5d584f7 and cb5ee2b.

📒 Files selected for processing (1)
  • test/routes/graphql/Query/fund.test.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Run tests for talawa api
  • GitHub Check: Analyse Code With CodeQL (typescript)
🔇 Additional comments (8)
test/routes/graphql/Query/fund.test.ts (8)

1-41: LGTM! Well-structured setup with proper imports and helper functions.

The setup section is well-organized with clear type imports and a focused helper function for admin authentication.


43-182: Consider adding more error scenarios.

While the error handling is comprehensive, consider adding these edge cases:

  • Malformed authorization token
  • Expired authorization token
  • Rate limiting scenarios

216-253: Improve expired token generation.

The test generates an expired token correctly using a timestamp-based approach, which is better than using a hard-coded token.


281-358: Consider adding more success scenarios.

While the basic success cases are covered, consider adding tests for:

  • Funds with null/empty optional fields
  • Funds with maximum length names
  • Multiple funds in the same organization

404-542: LGTM! Comprehensive schema validation tests.

The validation tests cover a wide range of scenarios including:

  • Name length constraints
  • Special characters and whitespace handling
  • Unique constraint validation within organizations

544-619: LGTM! Thorough UUID validation tests.

The tests comprehensively cover:

  • UUID format validation
  • Version constraints (v7)
  • Case sensitivity
  • Invalid formats and versions

621-685: LGTM! Well-structured required field validation tests.

The tests thoroughly validate:

  • Required fields presence
  • Field type constraints
  • Field format validation

687-932: LGTM! Well-implemented helper functions with robust error handling.

The helper functions include:

  • Retry mechanism with exponential backoff
  • Proper error handling and logging
  • Cleanup capabilities for test data

Comment on lines +184 to +214
test("with 'unauthenticated' extensions code if authorization token is malformed", async () => {
const fundId = faker.string.uuid();
// Generate a random invalid token instead of using a hard-coded value
const invalidToken = Buffer.from(faker.string.alphanumeric(32)).toString(
"base64",
);

const fundResult = await mercuriusClient.query(Query_fund, {
headers: {
authorization: `bearer ${invalidToken}`,
},
variables: {
input: {
id: fundId,
},
},
});

expect(fundResult.data.fund).toEqual(null);
expect(fundResult.errors).toEqual(
expect.arrayContaining<TalawaGraphQLFormattedError>([
expect.objectContaining<TalawaGraphQLFormattedError>({
extensions: expect.objectContaining<UnauthenticatedExtensions>({
code: "unauthenticated",
}),
message: expect.any(String),
path: ["fund"],
}),
]),
);
});
Copy link

@coderabbitai coderabbitai bot Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Replace hard-coded token with dynamically generated one.

The test uses a hard-coded malformed token which was flagged by security analysis. Consider using a dynamically generated token instead.

The current implementation has been improved to use a randomly generated token, which is good practice.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a random generated invalid token, won't be a problem. However, if you still need changes please recommend some good practices

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yugal07 Your current implementation using faker.string.alphanumeric(32) with base64 encoding is already following good practices! However, if you'd like to make it even more robust, here are some additional suggestions:

// Option 1: Using crypto for stronger randomness
const cryptoToken = crypto.randomBytes(32).toString('base64');

// Option 2: Test suite covering various malformed scenarios
describe('malformed tokens', () => {
  const cases = [
    ['empty token', ''],
    ['very long token', 'a'.repeat(1000)],
    ['non-base64', '!@#$%^&*()'],
    ['invalid JWT format', 'header.invalid'],
    ['random bytes', crypto.randomBytes(32).toString('base64')]
  ];
  
  test.each(cases)('%s', async (_, token) => {
    // Your test here using the token
  });
});

These are optional enhancements - your current implementation is already secure and follows testing best practices.


✏️ Learnings added
Learnt from: yugal07
PR: PalisadoesFoundation/talawa-api#3166
File: test/routes/graphql/Query/fund.test.ts:184-214
Timestamp: 2025-02-11T11:02:01.149Z
Learning: When testing invalid authentication tokens in tests, it's recommended to:
1. Use dynamically generated tokens instead of hard-coded values
2. Consider using crypto-safe random generation
3. Test various malformed scenarios (empty, very long, non-base64, invalid JWT format)
4. Use reasonable token lengths (e.g., 32 characters)

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@yugal07
Copy link
Author

yugal07 commented Feb 11, 2025

@palisadoes I have implemented all of the coderaabbit suggestions and the remaining suggestion is not valid. Please have a look and let me know if there's need for more changes

@palisadoes palisadoes merged commit da81d6e into PalisadoesFoundation:develop-postgres Feb 12, 2025
17 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants