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

refactor: enable safe index access by setting noUncheckedIndexedAccess #78

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from

Conversation

andylizf
Copy link
Contributor

@andylizf andylizf commented Mar 7, 2024

Summary by CodeRabbit

  • Refactor
    • Improved session management by centralizing refresh token logic.
    • Enhanced error handling and code readability across various modules and tests.
  • Bug Fixes
    • Adjusted logic in PageHelper to correctly handle empty data scenarios.

@andylizf andylizf added the enhancement New feature or request label Mar 7, 2024
@andylizf andylizf linked an issue Mar 7, 2024 that may be closed by this pull request
@andylizf andylizf requested a review from Nictheboy March 7, 2024 04:56
@andylizf andylizf marked this pull request as ready for review March 7, 2024 04:57
@@ -110,7 +110,7 @@ export class PageHelper {
prevStart: number,
idGetter: (item: TData) => number,
): [TData[], PageRespondDto] {
if (data.length == 0 || pageSize < 0) {
if (data[0] == null || pageSize < 0) {
Copy link
Contributor Author

@andylizf andylizf Mar 8, 2024

Choose a reason for hiding this comment

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

TData maybe T | undefined

@Nictheboy
Copy link
Contributor

@coderabbitai review

Copy link

coderabbitai bot commented Mar 12, 2024

Walkthrough

The recent updates focus on enhancing code readability, maintainability, and error handling across various components of the project. Key improvements include the introduction of new private methods for cleaner logic abstraction, refinements in variable naming and function return types for consistency, and the adoption of optional chaining for more robust error handling. These changes contribute to a more organized and efficient codebase, facilitating easier future development and debugging.

Changes

Files Change Summaries
.../session.service.ts Added getSessionIdByRefreshToken; refactored refreshSession and revokeSession to use it.
.../helper/page.helper.ts Modified PageHelper logic to check if the first data element is null.
.../users.controller.ts Extracted token retrieval logic into getRefreshTokenFromCookieHeader.
test/...e2e-spec.ts (All) Implemented optional chaining, renamed variables, refactored functions, and updated variable access.

🐇✨
In the land of code, where logic threads weave,
A rabbit hopped in, changes to conceive.
With a flick and a hop, cleaner paths were laid,
Through tangled vines of code, a clearer way displayed.
Now, with each line refined, and errors shy,
The rabbit's work, under the moonlit sky,
Leaves a trail of code, so nimble and bright. 🌟
🐇✨

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>.
    • Generate unit-tests 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 tests 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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

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 as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

@andylizf
Copy link
Contributor Author

andylizf commented Mar 13, 2024

@coderabbitai review

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.

Review Status

Actionable comments generated: 7

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 976382c and 483ee14.
Files ignored due to path filters (1)
  • tsconfig.json is excluded by: !**/*.json
Files selected for processing (11)
  • src/auth/session.service.ts (3 hunks)
  • src/common/helper/page.helper.ts (1 hunks)
  • src/users/users.controller.ts (3 hunks)
  • test/answer.e2e-spec.ts (3 hunks)
  • test/comment.e2e-spec.ts (23 hunks)
  • test/groups.e2e-spec.ts (26 hunks)
  • test/question.e2e-spec.ts (4 hunks)
  • test/topic.e2e-spec.ts (2 hunks)
  • test/user.e2e-spec.ts (18 hunks)
  • test/user.follow.e2e-spec.ts (4 hunks)
  • test/user.profile.e2e-spec.ts (2 hunks)
Check Runs (16)
test (12, 8.0.0, 18.x, 8) completed (1)
test (12, 8.0.0, 18.x, 6) completed (1)
test (12, 8.0.0, 16.x, 8) completed (1)
test (12, 8.0.0, 16.x, 6) completed (1)
test (12, 8.12.2, 18.x, 8) completed (1)
test (12, 8.12.2, 18.x, 6) completed (1)
test (12, 8.12.2, 16.x, 8) completed (1)
test (12, 8.12.2, 16.x, 6) completed (1)
test (latest, 8.0.0, 18.x, 8) completed (1)
test (latest, 8.0.0, 18.x, 6) completed (1)
test (latest, 8.0.0, 16.x, 8) completed (1)
test (latest, 8.0.0, 16.x, 6) completed (1)
test (latest, 8.12.2, 18.x, 8) completed (1)
test (latest, 8.12.2, 18.x, 6) completed (1)
test (latest, 8.12.2, 16.x, 8) completed (1)
test (latest, 8.12.2, 16.x, 6) completed (1)
Additional comments: 34
src/common/helper/page.helper.ts (1)
  • 113-113: The change from checking the length to checking if the first element is null enhances safety by explicitly handling potentially null or undefined values. This is a good practice. Consider adding a comment explaining why this check is necessary for future maintainability.
src/auth/session.service.ts (3)
  • 77-90: The addition of getSessionIdByRefreshToken improves code readability and reusability by encapsulating the logic for extracting the session ID from a refresh token. Ensure that the authService.verify method called within handles potential errors robustly.
  • 101-101: Refactoring refreshSession to use getSessionIdByRefreshToken simplifies its logic and improves maintainability without compromising functionality.
  • 167-167: Refactoring revokeSession to use getSessionIdByRefreshToken enhances code consistency and maintainability.
test/user.profile.e2e-spec.ts (2)
  • 36-42: Refactoring the way mocked email service methods are accessed and reset in the beforeEach block improves readability and maintainability. Ensure that optional chaining is used appropriately and doesn't mask potential issues with the mock setup.
  • 60-68: Using optional chaining for accessing MockedEmailService methods in tests makes the assertions cleaner. Ensure that the tests still accurately capture the intended behavior and that optional chaining doesn't lead to false positives.
test/user.follow.e2e-spec.ts (4)
  • 37-37: Using optional chaining (?.) here improves the safety of accessing sendRegisterCode on potentially undefined instances of MockedEmailService. This is a good practice, especially in tests where the setup might not always guarantee the presence of mocked instances.
  • 64-73: The use of optional chaining (?.) in these lines is consistent with the approach to safely access methods on potentially undefined objects. This change enhances the robustness of the test setup phase by preventing possible runtime errors.
  • 91-97: The application of optional chaining (?.) in these assertions is appropriate and aligns with the goal of making the code safer and more resilient to potential null or undefined values. This practice should help maintain the reliability of the tests.
  • 139-141: Adding a check for tempUserIds[0] being null or undefined before proceeding with the test is a crucial safety measure. This prevents the test from failing in an unclear manner if the setup did not populate tempUserIds as expected. Throwing an explicit error provides clearer feedback for debugging.
test/answer.e2e-spec.ts (3)
  • 37-37: The use of optional chaining (?.) when accessing sendRegisterCode on MockedEmailService.mock.instances[0] is a prudent choice. It ensures that the test does not fail due to an undefined object, which aligns with the goal of making the codebase more robust.
  • 63-69: The introduction of a local variable mockedEmailService to hold MockedEmailService.mock.instances[0] and the subsequent use of optional chaining (?.) is a significant improvement. It not only makes the code cleaner but also safer by handling potential undefined instances gracefully.
  • 87-93: Applying optional chaining (?.) in these assertions is consistent with the overall goal of enhancing code safety and reliability. This approach minimizes the risk of encountering runtime errors due to undefined or null values in the test setup.
test/comment.e2e-spec.ts (9)
  • 30-33: Renaming CommentIds to commentIds, TopicIds to topicIds, and TestCommentPrefix to testCommentPrefix improves readability and adheres to the camelCase naming convention typically used in TypeScript for variables. This change aligns with best practices for naming conventions in TypeScript.
  • 56-56: The adjustment in the return value of the createAuxiliaryUser function to return both the user ID and access token as a tuple is a good practice. It makes the function more versatile by providing both pieces of information needed in subsequent tests without requiring additional calls or global variables.
  • 69-75: Refactoring the beforeEach function to reset the mockedEmailService calls and results for each test ensures that the mock state is clean before each test runs. This is crucial for avoiding test interference and ensuring that each test runs in a predictable environment.
  • 202-208: The modification in the createComment function to explicitly check for undefined commentableId and throw an error if it is undefined is a good practice for error handling. This ensures that the function fails fast with a clear error message if required arguments are not provided, which can help in debugging and maintaining the code.
  • 220-226: Appending created comment IDs to the commentIds array for future reference in tests is a practical approach to managing resources created during tests. This allows for easy access to these resources in subsequent tests, such as deleting or querying comments, which can help in cleaning up or further testing.
  • 234-234: The use of testCommentPrefix in the content of a comment being tested ensures that test-generated content is easily identifiable. This can be useful for filtering or cleaning up test data from the application if needed.
  • 297-297: Retrieving a comment by its ID and verifying the response structure and content is a good practice for testing read operations in the API. This test ensures that the comment retrieval functionality works as expected and returns the correct data.
  • 344-344: Testing the functionality to express an attitude towards a comment is important for ensuring that the application's interaction features work as intended. This test verifies that users can successfully agree to a comment and that the system records this action correctly.
  • 483-483: The test for deleting a comment verifies that the delete operation works as expected and that the system correctly responds with a status indicating the comment has been deleted. This is crucial for ensuring that the application can manage content dynamically.
test/question.e2e-spec.ts (3)
  • 42-42: The use of optional chaining here (MockedEmailService.mock.instances[0]?.sendRegisterCode) is a good practice as it enhances the safety of the code by preventing potential runtime errors when mock.instances[0] is undefined. This change aligns with the PR's objective to improve code safety and reliability.
  • 68-74: Initializing the mockedEmailService variable and resetting the mock calls and results at the beginning of each test is a good practice. It ensures that the state is clean for every test, which is crucial for avoiding test interferences and ensuring test reliability.
  • 92-98: The verification of the sendRegisterCode method being called with the expected arguments and the correct number of times demonstrates attention to detail in testing the email verification functionality. This ensures that the email service behaves as expected, which is critical for user registration flows.
test/groups.e2e-spec.ts (8)
  • 34-35: Consider using a more deterministic approach for generating unique test group prefixes to avoid potential collisions in parallel test executions.
  • 38-47: The creation of auxiliary users involves direct interaction with the email service mock. Ensure that this approach is consistent with the overall testing strategy and consider abstracting it for reuse.
  • 97-100: The assertions on the mock email service's behavior are good for ensuring the expected interactions. However, consider also verifying the content of the email sent to ensure it meets the application's requirements.

Also applies to: 103-103

  • 126-132: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [129-147]

Creating groups in tests without cleaning them up can lead to state pollution between tests. Ensure that there is a teardown step that cleans up created groups to maintain test isolation.

  • 355-361: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [358-382]

The tests for fetching a group's details are well-structured. However, consider adding assertions for additional fields in the groupDto to ensure all expected properties are correctly returned and populated.

  • 418-421: The explicit check for groupId == null is good for error handling. Consider also logging an error message or throwing a more descriptive error to aid in debugging when this condition is met.
  • 544-549: The test for permission control is crucial. Ensure that the application has implemented and enforced permission checks correctly, and consider adding more tests to cover various permission scenarios.
  • 656-662: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [659-678]

The pagination logic in the get group members tests is well-tested. Consider adding tests for edge cases, such as when the page_start is beyond the last member, to ensure the pagination logic handles these scenarios gracefully.

Also applies to: 697-715

test/user.e2e-spec.ts (1)
  • 114-120: Using optional chaining (?.) when accessing MockedEmailService.mock.instances[0]?.sendRegisterCode and similar patterns throughout the tests is a good practice to avoid potential runtime errors when the instance might be undefined. This change aligns with the PR's objective to enhance safety and reliability through stricter type checks.

This approach correctly handles potential null values, improving the robustness of the test suite.

Also applies to: 156-162, 214-220, 283-289, 323-329, 361-367, 399-405, 442-448, 467-471, 522-526, 553-559, 580-584, 615-621, 640-644, 789-795, 853-859, 892-899

Comment on lines +583 to +586
async function unfollow(questionId: number | undefined) {
if (questionId == undefined) {
throw new Error('questionId is undefined');
}
Copy link

Choose a reason for hiding this comment

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

The addition of a guard clause to throw an error if questionId is undefined is a proactive approach to error handling. This change ensures that the function fails fast if called with incorrect arguments, preventing potential runtime errors further down the execution path. However, consider adding a more descriptive error message to aid in debugging.

- throw new Error('questionId is undefined');
+ throw new Error('unfollow function was called with an undefined questionId. Ensure questionId is provided.');

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.

Suggested change
async function unfollow(questionId: number | undefined) {
if (questionId == undefined) {
throw new Error('questionId is undefined');
}
async function unfollow(questionId: number | undefined) {
if (questionId == undefined) {
throw new Error('unfollow function was called with an undefined questionId. Ensure questionId is provided.');
}

Comment on lines +73 to +79
const mockedEmailService = MockedEmailService.mock.instances[0]!;
(mockedEmailService.sendRegisterCode as jest.Mock).mock.calls.length = 0;
(mockedEmailService.sendRegisterCode as jest.Mock).mock.results.length = 0;
(mockedEmailService.sendPasswordResetEmail as jest.Mock).mock.calls.length =
0;
(
MockedEmailService.mock.instances[0].sendRegisterCode as jest.Mock
).mock.calls.length = 0;
(
MockedEmailService.mock.instances[0].sendRegisterCode as jest.Mock
).mock.results.length = 0;
(
MockedEmailService.mock.instances[0].sendPasswordResetEmail as jest.Mock
).mock.calls.length = 0;
(
MockedEmailService.mock.instances[0].sendPasswordResetEmail as jest.Mock
mockedEmailService.sendPasswordResetEmail as jest.Mock
Copy link

Choose a reason for hiding this comment

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

Resetting mock calls and results manually in beforeEach can be error-prone. Consider using Jest's built-in reset functions like jest.clearAllMocks() for clarity and reliability.

-    (mockedEmailService.sendRegisterCode as jest.Mock).mock.calls.length = 0;
-    (mockedEmailService.sendRegisterCode as jest.Mock).mock.results.length = 0;
-    (mockedEmailService.sendPasswordResetEmail as jest.Mock).mock.calls.length = 0;
-    (mockedEmailService.sendPasswordResetEmail as jest.Mock).mock.results.length = 0;
+    jest.clearAllMocks();

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.

Suggested change
const mockedEmailService = MockedEmailService.mock.instances[0]!;
(mockedEmailService.sendRegisterCode as jest.Mock).mock.calls.length = 0;
(mockedEmailService.sendRegisterCode as jest.Mock).mock.results.length = 0;
(mockedEmailService.sendPasswordResetEmail as jest.Mock).mock.calls.length =
0;
(
MockedEmailService.mock.instances[0].sendRegisterCode as jest.Mock
).mock.calls.length = 0;
(
MockedEmailService.mock.instances[0].sendRegisterCode as jest.Mock
).mock.results.length = 0;
(
MockedEmailService.mock.instances[0].sendPasswordResetEmail as jest.Mock
).mock.calls.length = 0;
(
MockedEmailService.mock.instances[0].sendPasswordResetEmail as jest.Mock
mockedEmailService.sendPasswordResetEmail as jest.Mock
const mockedEmailService = MockedEmailService.mock.instances[0]!;
jest.clearAllMocks();

Comment on lines +479 to +484
const TestGroupId = groupIds[0];
const respond = await request(app.getHttpServer())
.put(`/groups/${TestGroupId}`)
.set('Authorization', `Bearer ${TestToken}`)
.send({
name: TestGroupPrefix + '关注幻城谢谢喵',
name: testGroupPrefix + '关注幻城谢谢喵',
Copy link

Choose a reason for hiding this comment

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

When updating a group, ensure that the test verifies not just the success response but also that the group's details have been updated as expected by fetching the group again and checking the updated fields.

Comment on lines +529 to +534
const TestGroupId = groupIds[0];
const respond = await request(app.getHttpServer())
.put(`/groups/${TestGroupId}`)
.set('Authorization', `Bearer ${TestToken}`)
.send({
name: TestGroupPrefix + 'ICS膜膜膜',
name: testGroupPrefix + 'ICS膜膜膜',
Copy link

Choose a reason for hiding this comment

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

The test for the GroupNameAlreadyUsedError is important for ensuring unique group names. Consider adding a setup step that explicitly creates a group with the conflicting name to make the test more robust and self-contained.

@@ -740,7 +740,7 @@
expect(respond.body.code).toBe(404);
});
it('should return empty list when page_size is not positive', async () => {
const TestGroupId = GroupIds[1];
const TestGroupId = groupIds[1];
Copy link

Choose a reason for hiding this comment

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

Testing for invalid page_size input is good for ensuring robustness. Consider also testing other invalid inputs and edge cases to ensure the API handles them gracefully.

Comment on lines +37 to +43
const mockedEmailService = MockedEmailService.mock.instances[0]!;
(mockedEmailService.sendRegisterCode as jest.Mock).mock.calls.length = 0;
(mockedEmailService.sendRegisterCode as jest.Mock).mock.results.length = 0;
(mockedEmailService.sendPasswordResetEmail as jest.Mock).mock.calls.length =
0;
(
MockedEmailService.mock.instances[0].sendRegisterCode as jest.Mock
).mock.calls.length = 0;
(
MockedEmailService.mock.instances[0].sendRegisterCode as jest.Mock
).mock.results.length = 0;
(
MockedEmailService.mock.instances[0].sendPasswordResetEmail as jest.Mock
).mock.calls.length = 0;
(
MockedEmailService.mock.instances[0].sendPasswordResetEmail as jest.Mock
mockedEmailService.sendPasswordResetEmail as jest.Mock
Copy link

Choose a reason for hiding this comment

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

Refactoring the mock setup for EmailService improves readability and maintainability by resetting mock calls and results directly. However, directly manipulating mock.calls.length and mock.results.length is unconventional and might lead to unexpected behavior or maintenance issues in the future.

Consider using jest.clearAllMocks() or jest.resetAllMocks() at the beginning of the beforeEach block to achieve the same effect in a more conventional and reliable manner.

Comment on lines +442 to +448
expect(respond.header['set-cookie']?.[0]).toMatch(
/^REFRESH_TOKEN=.+; Path=\/users\/auth; Expires=.+; HttpOnly; SameSite=Strict$/,
);
TestRefreshToken = respond.header['set-cookie'][0]
.split(';')[0]
.split('=')[1];
const TestRefreshToken2 = respond?.header?.['set-cookie']?.[0]
?.split(';')?.[0]
?.split('=')?.[1];
expect(TestRefreshToken2).toBeDefined();
Copy link

Choose a reason for hiding this comment

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

The handling of set-cookie headers to extract the REFRESH_TOKEN value demonstrates a detailed understanding of HTTP headers and cookie manipulation. However, this pattern is repeated multiple times throughout the file, which could be refactored into a helper function to improve code reusability and maintainability.

Consider creating a utility function, such as extractRefreshTokenFromHeader, to encapsulate the logic for extracting the REFRESH_TOKEN value from the set-cookie header. This function can then be reused across all test cases that require this operation.

Also applies to: 467-471, 522-526, 553-559, 580-584, 615-621, 640-644

@andylizf andylizf self-assigned this Mar 15, 2024
@HuanCheng65 HuanCheng65 force-pushed the dev branch 3 times, most recently from d08274a to d840758 Compare April 1, 2024 03:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add noUncheckedIndexedAccess to tsconfig for Stricter Index Access
2 participants