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

Hubobe 51 be 포토 포인트 #23

Merged
merged 78 commits into from
Oct 4, 2024
Merged

Conversation

haroya01
Copy link
Member

@haroya01 haroya01 commented Sep 29, 2024

PR 이슈번호

추후 작성

추가 되거나 변경된 내용


논의 해 볼 내용


래퍼런스

Summary by CodeRabbit

  • New Features

    • Introduced a new test environment setup with MySQL, Redis, Elasticsearch, and MongoDB services.
    • Added a comprehensive suite of unit tests for the UserPointSearchResolver class, covering various scenarios for retrieving user points.
    • Implemented a FixtureBase class to facilitate test data setup and authentication token management.
    • Expanded UserPointFixture class with additional methods for creating and managing user points and related entities.
    • Added a UserPointService class for managing user points, including saving and deleting functionalities.
    • Introduced a UserPointControllerTest class to validate endpoints related to user points.
  • Bug Fixes

    • Streamlined response handling for toggling and deleting bookmarks.
    • Updated image URL validation tests to focus solely on validation without deletion.
  • Chores

    • Updated subproject commit identifier to reflect the latest modifications.
    • Shifted to a new method for test environment configuration, removing previous SSH actions.
    • Updated project dependencies to enhance testing, security, and data handling capabilities.

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

🧹 Outside diff range and nitpick comments (2)
src/test/java/com/hubo/gillajabi/HuboApplicationTests.java (2)

3-20: Remove unused imports to improve code clarity.

Several imported classes are not used in the current implementation. Consider removing the following unused imports:

- import co.elastic.clients.elasticsearch._types.NodeStatistics;
- import co.elastic.clients.elasticsearch.nodes.Stats;
- import org.elasticsearch.client.Request;
- import org.elasticsearch.client.Response;
- import org.elasticsearch.client.RestClient;
- import static org.junit.jupiter.api.Assertions.*;

Also, note that org.junit.jupiter.api.Test is imported twice (lines 8 and 18). Remove the duplicate import.


27-46: Consider expanding test coverage for Elasticsearch operations.

While the current test checks the Elasticsearch connection, it would be beneficial to add more test methods to cover various Elasticsearch operations relevant to your application. For example:

  1. Test indexing a document
  2. Test searching for documents
  3. Test updating a document
  4. Test deleting a document

These additional tests would provide more comprehensive coverage of your Elasticsearch integration.

Would you like assistance in generating these additional test methods?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between e72c901 and fa417b2.

📒 Files selected for processing (2)
  • BE-hubo-gillajabi-resources (1 hunks)
  • src/test/java/com/hubo/gillajabi/HuboApplicationTests.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • BE-hubo-gillajabi-resources

Comment on lines +36 to 45
public void testElasticsearchConnection() {
logger.info("Starting Elasticsearch connection test");
try {
var response = elasticsearchClient.info();
logger.info("Elasticsearch info: {}", response);
} catch (Exception e) {
logger.error("Error connecting to Elasticsearch", e);
throw new RuntimeException("Failed to connect to Elasticsearch", e);
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance the Elasticsearch connection test method.

While the test method is well-structured, consider the following improvements:

  1. Add assertions to validate the Elasticsearch connection:
assertNotNull(response);
assertNotNull(response.version());
assertEquals("You know, for Search", response.tagline());
  1. Use more specific exception handling:
} catch (IOException e) {
    logger.error("Error connecting to Elasticsearch", e);
    fail("Failed to connect to Elasticsearch: " + e.getMessage());
}
  1. Consider renaming the method to be more descriptive, e.g., testElasticsearchConnectionAndInfo().

  2. Add a comment explaining the purpose of this test in the context of your application.

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

🧹 Outside diff range and nitpick comments (1)
src/main/java/com/hubo/gillajabi/search/infrastructure/config/ElasticsearchConfig.java (1)

30-30: Approve logging additions with a security suggestion.

The addition of logging statements improves the visibility of the configuration process, which is beneficial for debugging and monitoring. However, there's a potential security concern:

  1. Logging the full 'uris' value (line 30) might expose sensitive information in the logs.

Consider masking part of the URI to avoid exposing full connection details in the logs.

Here's a suggested modification for line 30:

- log.info("uris: " + uris);
+ log.info("Configuring Elasticsearch with URI: {}", maskUri(uris));

And add this helper method to the class:

private String maskUri(String uri) {
    // This is a simple masking. Adjust as needed for your specific URI format.
    return uri.replaceAll("(http://|https://)([^:]+)(:\\d+)?", "$1****$3");
}

This change will log the URI while masking the host part, improving security.

Also applies to: 36-37

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between fa417b2 and cf41088.

📒 Files selected for processing (3)
  • BE-hubo-gillajabi-resources (1 hunks)
  • build.gradle (2 hunks)
  • src/main/java/com/hubo/gillajabi/search/infrastructure/config/ElasticsearchConfig.java (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • BE-hubo-gillajabi-resources
  • build.gradle
🔇 Additional comments (1)
src/main/java/com/hubo/gillajabi/search/infrastructure/config/ElasticsearchConfig.java (1)

3-4: Approve logging framework change and query profile annotation removal.

The switch from Log4j to Slf4j is a good improvement. Slf4j provides a more flexible logging facade that allows for easier switching between different logging implementations.

However, I noticed that the @Profile("!test") annotation has been removed. Can you confirm if this was intentional? This change might affect how this configuration is used in different environments, particularly in test scenarios.

To verify the impact of removing the @Profile annotation, let's check for its usage in test files:

Also applies to: 16-16

✅ Verification successful

To ensure the removal of the @Profile("!test") annotation doesn't affect other parts of the codebase, please run the following script to search for its usage across all Java files:


Verified removal of @Profile("!test") annotation.

The search across all Java files did not reveal any usage of @Profile("!test"). Therefore, removing this annotation from ElasticsearchConfig.java should not have unintended side effects on other parts of the application.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for usage of @Profile("!test") in test files

# Test: Search for @Profile("!test") in test files
rg --type java -g '*Test.java' '@Profile\("!test"\)'

Length of output: 54


Script:

#!/bin/bash
# Description: Search for usage of @Profile("!test") in all Java files

rg --type java '@Profile\("!test"\)'

Length of output: 38

Comment on lines +31 to 34
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo(uris.replace("http://", ""))
.withBasicAuth(username, password)
.build();
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Suggest improvement for handling both HTTP and HTTPS URIs.

The current implementation removes the "http://" prefix from the URI. However, it doesn't account for potential "https://" URIs. To make the configuration more robust and secure, consider handling both HTTP and HTTPS protocols.

Here's a suggested modification:

- .connectedTo(uris.replace("http://", ""))
+ .connectedTo(uris.replaceFirst("^https?://", ""))

This change will remove both "http://" and "https://" prefixes, making the configuration work correctly for both secure and non-secure connections.

📝 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
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo(uris.replace("http://", ""))
.withBasicAuth(username, password)
.build();
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo(uris.replaceFirst("^https?://", ""))
.withBasicAuth(username, password)
.build();

Copy link

codecov bot commented Oct 1, 2024

Codecov Report

Attention: Patch coverage is 68.53583% with 101 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...ructure/exception/CourseBookMarkExceptionCode.java 0.00% 11 Missing ⚠️
...ajabi/place/application/dto/response/PointDto.java 0.00% 11 Missing ⚠️
...i/course/domain/service/CourseBookMarkService.java 0.00% 10 Missing ⚠️
...rastructure/exception/CourseBookMarkException.java 0.00% 8 Missing ⚠️
...ajabi/global/exception/GlobalExceptionHandler.java 20.00% 8 Missing ⚠️
...bi/image/infrastructure/aop/ImageUploadAspect.java 46.66% 4 Missing and 4 partials ⚠️
...ajabi/review/domain/entity/PostSearchDocument.java 0.00% 8 Missing ⚠️
...o/gillajabi/place/domain/service/PlaceService.java 0.00% 6 Missing ⚠️
...lication/presenation/CourseBookMarkController.java 0.00% 5 Missing ⚠️
...llajabi/point/domain/entity/UserPointDocument.java 85.71% 5 Missing ⚠️
... and 12 more

Impacted file tree graph

@@              Coverage Diff              @@
##             develop      #23      +/-   ##
=============================================
+ Coverage      34.87%   35.51%   +0.63%     
- Complexity       493      751     +258     
=============================================
  Files            220      327     +107     
  Lines           2962     4370    +1408     
  Branches         132      168      +36     
=============================================
+ Hits            1033     1552     +519     
- Misses          1903     2772     +869     
- Partials          26       46      +20     
Files with missing lines Coverage Δ Complexity Δ
...va/com/hubo/gillajabi/city/domain/entity/City.java 58.33% <100.00%> (+3.78%) 7.00 <1.00> (+1.00)
...course/application/dto/response/CoursePreview.java 100.00% <100.00%> (ø) 4.00 <1.00> (?)
...gillajabi/course/domain/entity/CourseBookMark.java 30.00% <100.00%> (ø) 3.00 <1.00> (?)
...com/hubo/gillajabi/crawl/domain/entity/Course.java 24.44% <ø> (-3.56%) 10.00 <0.00> (ø)
...bi/global/common/config/MongoMigrationManager.java 94.59% <100.00%> (ø) 9.00 <4.00> (?)
.../com/hubo/gillajabi/global/dto/CursorPageInfo.java 100.00% <100.00%> (ø) 3.00 <3.00> (?)
...ajabi/image/domain/service/ImageUploadService.java 17.54% <ø> (-10.05%) 7.00 <0.00> (+1.00) ⬇️
...i/image/domain/service/ImageValidationService.java 100.00% <100.00%> (ø) 8.00 <4.00> (+3.00)
...abi/image/infrastructure/util/ImageUrlBuilder.java 50.00% <100.00%> (-16.67%) 1.00 <1.00> (-1.00)
...jabi/login/domain/entity/MemberAuthentication.java 84.61% <100.00%> (+42.94%) 10.00 <1.00> (+5.00)
... and 35 more

... and 20 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b4bc11e...0e68491. Read the comment docs.

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: 8

🧹 Outside diff range and nitpick comments (1)
.github/workflows/opened-pr-test.yml (1)

207-214: Improved test logging and output capture

The addition of detailed logging and output capture for Gradle tests is a good improvement for debugging and troubleshooting.

This change will make it easier to diagnose issues when tests fail. However, be aware that it might slightly increase the workflow execution time due to the additional I/O operations.

Consider adding a condition to only enable detailed logging when needed:

- name: Test with Gradle and generate detailed logs
  run: |
    mkdir -p logs
    if [ "${{ github.event_name }}" == "pull_request" ]; then
      ./gradlew test -Dspring.profiles.active=test --info --stacktrace 2>&1 | tee logs/test_output.log
    else
      ./gradlew test -Dspring.profiles.active=test
    fi
  env:
    LOGGING_LEVEL_ROOT: ${{ github.event_name == 'pull_request' && 'DEBUG' || 'INFO' }}
    LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: ${{ github.event_name == 'pull_request' && 'DEBUG' || 'INFO' }}

This way, detailed logging is only enabled for pull requests, potentially saving time on other workflow runs.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between cf41088 and d2cfa70.

📒 Files selected for processing (6)
  • .github/workflows/opened-pr-test.yml (2 hunks)
  • BE-hubo-gillajabi-resources (1 hunks)
  • src/main/java/com/hubo/gillajabi/review/domain/entity/PostSearchDocument.java (5 hunks)
  • src/test/java/com/hubo/gillajabi/image/domain/service/ImageValidationServiceTest.java (5 hunks)
  • src/test/java/com/hubo/gillajabi/point/application/resolver/UserPointSearchResolverTest.java (1 hunks)
  • src/test/java/com/hubo/gillajabi/point/fixture/UserPointFixture.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • BE-hubo-gillajabi-resources
  • src/main/java/com/hubo/gillajabi/review/domain/entity/PostSearchDocument.java
  • src/test/java/com/hubo/gillajabi/image/domain/service/ImageValidationServiceTest.java
  • src/test/java/com/hubo/gillajabi/point/application/resolver/UserPointSearchResolverTest.java
  • src/test/java/com/hubo/gillajabi/point/fixture/UserPointFixture.java
🧰 Additional context used
🪛 actionlint
.github/workflows/opened-pr-test.yml

78-78: shellcheck reported issue in this script: SC2046:warning:1:13: Quote this to prevent word splitting

(shellcheck)


83-83: shellcheck reported issue in this script: SC2046:warning:1:16: Quote this to prevent word splitting

(shellcheck)


100-100: shellcheck reported issue in this script: SC2086:info:8:6: Double quote to prevent globbing and word splitting

(shellcheck)

🪛 yamllint
.github/workflows/opened-pr-test.yml

[error] 61-61: trailing spaces

(trailing-spaces)


[error] 117-117: trailing spaces

(trailing-spaces)


[error] 120-120: trailing spaces

(trailing-spaces)


[error] 123-123: trailing spaces

(trailing-spaces)


[error] 126-126: trailing spaces

(trailing-spaces)


[error] 129-129: trailing spaces

(trailing-spaces)

🔇 Additional comments (2)
.github/workflows/opened-pr-test.yml (2)

215-228: Excellent addition of log artifact uploads

The new steps to upload test and Gradle logs as artifacts are a valuable addition to the workflow.

These changes will greatly improve the ability to debug failed tests by providing easy access to detailed logs. The use of if: always() ensures that logs are uploaded even if the tests fail, which is crucial for troubleshooting.

Good job on enhancing the observability of your CI process!


Line range hint 1-245: Overall improvements to the CI workflow with some areas for refinement

The changes to this workflow file significantly enhance the test environment setup and improve observability. Key improvements include:

  1. Addition of services (MySQL, Redis, Elasticsearch, MongoDB) for a more comprehensive test environment.
  2. Detailed setup and verification steps for each service.
  3. Enhanced logging and artifact uploads for better debugging capabilities.

While these changes are generally positive, there are some areas for further improvement:

  1. Security: Use separate credentials for different services and consider using non-root users where possible.
  2. Efficiency: Some setup steps could be optimized or combined.
  3. Code cleanliness: Remove commented-out code related to the old remote testing setup.
  4. Internationalization: Use English for all log messages to ensure clarity for international teams.

Addressing these points will further enhance the robustness and maintainability of your CI process.

🧰 Tools
🪛 actionlint

78-78: shellcheck reported issue in this script: SC2046:warning:1:13: Quote this to prevent word splitting

(shellcheck)


83-83: shellcheck reported issue in this script: SC2046:warning:1:16: Quote this to prevent word splitting

(shellcheck)


100-100: shellcheck reported issue in this script: SC2086:info:8:6: Double quote to prevent globbing and word splitting

(shellcheck)

🪛 yamllint

[error] 61-61: trailing spaces

(trailing-spaces)


[error] 117-117: trailing spaces

(trailing-spaces)


[error] 120-120: trailing spaces

(trailing-spaces)


[error] 123-123: trailing spaces

(trailing-spaces)


[error] 126-126: trailing spaces

(trailing-spaces)


[error] 129-129: trailing spaces

(trailing-spaces)

.github/workflows/opened-pr-test.yml Show resolved Hide resolved
Comment on lines +56 to +60
options: >-
--health-cmd "mongosh --eval 'db.runCommand({ping:1})'"
--health-interval 10s
--health-timeout 5s
--health-retries 5
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider a more portable health check for MongoDB

The current health check uses mongosh, which might not be available in all environments. A more portable solution would be to use the mongo command-line tool, which is more commonly available.

Update the MongoDB health check to use mongo:

options: >-
  --health-cmd "mongo --eval 'db.runCommand({ping:1})'"
  --health-interval 10s
  --health-timeout 5s
  --health-retries 5

This change ensures better compatibility across different environments.

Comment on lines +77 to +80
- name: Install Elasticsearch plugin
run: |
docker exec $(docker ps -q --filter "name=elasticsearch") \
/usr/share/elasticsearch/bin/elasticsearch-plugin install --batch analysis-nori
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix potential issue in Elasticsearch plugin installation

The current plugin installation step uses command substitution without quotes, which could lead to issues if the container name contains spaces or special characters.

Update the plugin installation command to use proper quoting:

- name: Install Elasticsearch plugin
  run: |
    docker exec "$(docker ps -q --filter "name=elasticsearch")" \
    /usr/share/elasticsearch/bin/elasticsearch-plugin install --batch analysis-nori

This change ensures that the command works correctly regardless of the container name format.

🧰 Tools
🪛 actionlint

78-78: shellcheck reported issue in this script: SC2046:warning:1:13: Quote this to prevent word splitting

(shellcheck)

Comment on lines 189 to 198
# - name: Start test environment on remote server
# uses: appleboy/ssh-action@master
# with:
# host: ${{ secrets.BE_HOST }}
# username: ${{ secrets.BE_USER }}
# port: ${{ secrets.BE_PORT }}
# password: ${{ secrets.BE_PW }}
# script: |
# cd ~/hubo/test
# ./start-test-docker-compose.sh
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove commented-out remote server actions

The presence of commented-out code for remote server actions indicates a shift in testing strategy from remote to local testing. Keeping unused code, even as comments, can lead to confusion and make the workflow file harder to maintain.

Consider removing these commented-out sections entirely to improve the clarity and maintainability of the workflow file. If there's a possibility that you might need these steps in the future, consider moving them to a separate document or creating a git branch with these changes for future reference.

Also applies to: lines 229-239

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: 8

🧹 Outside diff range and nitpick comments (4)
src/test/java/com/hubo/gillajabi/image/domain/service/ImageValidationServiceTest.java (4)

31-39: Approve changes with a minor suggestion

The test method has been correctly updated to focus on validation without deletion, aligning with the new validateImageUrl method. The use of assertDoesNotThrow is appropriate for this scenario.

Consider updating the test method name to match the new functionality:

- void 단일_이미지_url_검증_및_삭제_성공() {
+ void 단일_이미지_url_검증_성공() {

58-69: Approve changes with suggestions for consistency

The test has been updated to reflect the new deleteImageUrls functionality. The verification steps now correctly use deleteById, which aligns with the changes in the repository.

For better consistency, consider updating the test method name and display name to remove references to validation:

- @DisplayName("여러 이미지 URL 검증 및 삭제 - 성공")
+ @DisplayName("여러 이미지 URL 삭제 - 성공")
- void 여러_이미지_URL_검증_및_삭제_성공() {
+ void 여러_이미지_URL_삭제_성공() {

73-74: Approve new test with suggestions for consistency

The addition of a separate test for single image URL deletion improves test coverage. However, there's an inconsistency between the display name and the method name.

Please update the method name to match the display name for consistency:

@DisplayName("단일 이미지 URL 삭제")
- void 여러_이미지_URL_삭제_실패(){
+ void 단일_이미지_URL_삭제_성공(){

76-83: Approve implementation with suggestion for additional testing

The implementation correctly tests the single image URL deletion functionality. The use of deleteById in the verification step is consistent with other tests.

Consider adding a test case for the failure scenario, as the current test only covers the success case. This could involve mocking a scenario where the deletion fails and verifying the appropriate exception is thrown.

Example:

@Test
@DisplayName("단일 이미지 URL 삭제 - 실패")
void 단일_이미지_URL_삭제_실패(){
    // given
    String imageUrl = "http://example.com/non-existent-image.jpg";
    doThrow(new RuntimeException("Deletion failed")).when(imageUploadUrlRepository).deleteById(imageUrl);

    // when & then
    assertThrows(RuntimeException.class, () -> imageValidationService.deleteImageUrl(imageUrl));
    verify(imageUploadUrlRepository, times(1)).deleteById(imageUrl);
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between d2cfa70 and 7431801.

📒 Files selected for processing (5)
  • .github/workflows/opened-pr-test.yml (2 hunks)
  • BE-hubo-gillajabi-resources (1 hunks)
  • src/test/java/com/hubo/gillajabi/config/FixtureBase.java (1 hunks)
  • src/test/java/com/hubo/gillajabi/image/domain/service/ImageValidationServiceTest.java (2 hunks)
  • src/test/java/com/hubo/gillajabi/point/application/resolver/UserPointSearchResolverTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • BE-hubo-gillajabi-resources
  • src/test/java/com/hubo/gillajabi/point/application/resolver/UserPointSearchResolverTest.java
🧰 Additional context used
🪛 actionlint
.github/workflows/opened-pr-test.yml

78-78: shellcheck reported issue in this script: SC2046:warning:1:13: Quote this to prevent word splitting

(shellcheck)


83-83: shellcheck reported issue in this script: SC2046:warning:1:16: Quote this to prevent word splitting

(shellcheck)


100-100: shellcheck reported issue in this script: SC2086:info:8:6: Double quote to prevent globbing and word splitting

(shellcheck)

🪛 yamllint
.github/workflows/opened-pr-test.yml

[error] 61-61: trailing spaces

(trailing-spaces)


[error] 117-117: trailing spaces

(trailing-spaces)


[error] 120-120: trailing spaces

(trailing-spaces)


[error] 123-123: trailing spaces

(trailing-spaces)


[error] 126-126: trailing spaces

(trailing-spaces)


[error] 129-129: trailing spaces

(trailing-spaces)

🔇 Additional comments (4)
src/test/java/com/hubo/gillajabi/config/FixtureBase.java (1)

38-44: LGTM: Repository retrieval methods are well-implemented.

The getRepository and getCrudRepository methods are well-designed:

  1. They provide flexible access to different types of repositories.
  2. The use of generics ensures type safety.
  3. The protected access modifier allows proper use by subclasses.

These methods will be very useful for setting up test fixtures.

src/test/java/com/hubo/gillajabi/image/domain/service/ImageValidationServiceTest.java (1)

51-51: Approve method name update

The method call has been correctly updated to validateImageUrl, which aligns with the changes in the ImageValidationService class. This change appropriately focuses the test on validation without deletion.

.github/workflows/opened-pr-test.yml (2)

33-60: LGTM! Redis and MongoDB setups are well-configured

The Redis and MongoDB service configurations are properly set up. The MongoDB health check using mongosh is a good practice for ensuring the service is ready before running tests.


195-207: LGTM! Artifact upload steps are well-configured

The artifact upload steps for test logs and Gradle logs are properly set up. Using the if: always() condition ensures that these logs are uploaded even if the test step fails, which is crucial for debugging. Good job on following best practices for CI/CD workflows.

Comment on lines +1 to +30
package com.hubo.gillajabi.config;

import com.hubo.gillajabi.login.application.dto.response.TokenResponse;
import com.hubo.gillajabi.login.domain.constant.RoleStatus;
import com.hubo.gillajabi.login.domain.entity.MemberAuthentication;
import com.hubo.gillajabi.login.infrastructure.security.TokenProvider;
import com.hubo.gillajabi.member.domain.entity.Member;
import com.hubo.gillajabi.member.infrastructure.persistence.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Profile;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.CrudRepository;

import java.time.LocalDateTime;


/*
repository 의존성을 넣는 abstract class 입니다
ApplicationContext, TokenProvider를 함께 받습니다.
*/
@Profile("test")
@Configuration
@ComponentScan(basePackages = "com.hubo.gillajabi",
useDefaultFilters = false,
includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = FixtureBase.class))
public abstract class FixtureBase {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider broadening the @ComponentScan configuration.

The current @ComponentScan configuration is very specific, only including the FixtureBase class itself. This might limit the usefulness of the configuration. Consider if you need to include other classes or use a broader scanning strategy to make this configuration more flexible and reusable.

You might want to adjust the @ComponentScan annotation to include other relevant classes or packages. For example:

@ComponentScan(basePackages = "com.hubo.gillajabi.config",
        useDefaultFilters = false,
        includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, 
                         classes = {FixtureBase.class, OtherRelevantClass.class}))

This would allow you to include other classes that might be relevant for your test fixtures.

Comment on lines +46 to +62
public MemberAuthentication createAndLoadMember(String username, RoleStatus roleStatus) {
MemberRepository memberRepository = getRepository(MemberRepository.class);

Member member = Member.builder()
.nickName(username)
.profileImageUrl("profile.com")
.lastLoginAt(LocalDateTime.now())
.build();
MemberAuthentication memberAuthentication = MemberAuthentication.builder()
.member(member)
.roleStatus(roleStatus)
.build();

memberRepository.save(member);

return memberAuthentication;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider making lastLoginAt configurable in createAndLoadMember.

The createAndLoadMember method is well-implemented, but setting lastLoginAt to the current time might cause issues in some tests where you need to control this value.

Consider modifying the method signature to accept a LocalDateTime parameter for lastLoginAt:

public MemberAuthentication createAndLoadMember(String username, RoleStatus roleStatus, LocalDateTime lastLoginAt) {
    // ...
    Member member = Member.builder()
            .nickName(username)
            .profileImageUrl("profile.com")
            .lastLoginAt(lastLoginAt)
            .build();
    // ...
}

This change would allow more flexibility in testing scenarios where the last login time is relevant.

Comment on lines +64 to +67
public TokenResponse createAndLoadTokenResponse(MemberAuthentication memberAuthentication) {
TokenProvider tokenProvider = context.getBean(TokenProvider.class);
return tokenProvider.createToken(memberAuthentication);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use the injected TokenProvider instead of retrieving it from the context.

The createAndLoadTokenResponse method is retrieving the TokenProvider from the context, but there's already an injected TokenProvider field in the class.

Modify the method to use the injected TokenProvider:

public TokenResponse createAndLoadTokenResponse(MemberAuthentication memberAuthentication) {
    return tokenProvider.createToken(memberAuthentication);
}

This change will make the code more consistent and slightly more efficient by avoiding the context lookup.

Comment on lines +16 to +31
mysql:
image: mysql:8.0.33
env:
MYSQL_DATABASE: ${{ secrets.MYSQL_DATABASE }}
MYSQL_ROOT_HOST: '%'
MYSQL_ROOT_PASSWORD: ${{ secrets.MYSQL_ROOT_PASSWORD }}
TZ: Asia/Seoul
MYSQL_CHARACTER_SET_SERVER: utf8mb4
MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using a non-root user for MySQL in the test environment

While using the root user is convenient for testing, it's a good practice to use a dedicated user with limited privileges, even in test environments. This helps to catch potential permission issues early and aligns better with security best practices.

Consider creating a dedicated test user for MySQL:

env:
  MYSQL_DATABASE: ${{ secrets.MYSQL_DATABASE }}
  MYSQL_USER: ${{ secrets.MYSQL_TEST_USER }}
  MYSQL_PASSWORD: ${{ secrets.MYSQL_TEST_PASSWORD }}
  MYSQL_ROOT_PASSWORD: ${{ secrets.MYSQL_ROOT_PASSWORD }}

Then, update your application configuration to use these non-root credentials.

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 UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 7431801 and d63159d.

📒 Files selected for processing (1)
  • .github/workflows/opened-pr-test.yml (2 hunks)
🧰 Additional context used
🪛 actionlint
.github/workflows/opened-pr-test.yml

78-78: shellcheck reported issue in this script: SC2046:warning:1:13: Quote this to prevent word splitting

(shellcheck)


83-83: shellcheck reported issue in this script: SC2046:warning:1:16: Quote this to prevent word splitting

(shellcheck)


100-100: shellcheck reported issue in this script: SC2086:info:8:6: Double quote to prevent globbing and word splitting

(shellcheck)

🪛 yamllint
.github/workflows/opened-pr-test.yml

[error] 61-61: trailing spaces

(trailing-spaces)


[error] 117-117: trailing spaces

(trailing-spaces)


[error] 120-120: trailing spaces

(trailing-spaces)


[error] 123-123: trailing spaces

(trailing-spaces)


[error] 126-126: trailing spaces

(trailing-spaces)


[error] 129-129: trailing spaces

(trailing-spaces)

Comment on lines +15 to +31
services:
mysql:
image: mysql:8.0.33
env:
MYSQL_DATABASE: ${{ secrets.MYSQL_DATABASE }}
MYSQL_ROOT_HOST: '%'
MYSQL_ROOT_PASSWORD: ${{ secrets.MYSQL_ROOT_PASSWORD }}
TZ: Asia/Seoul
MYSQL_CHARACTER_SET_SERVER: utf8mb4
MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using a non-root user for MySQL in the test environment

While using the root user is convenient for testing, it's a good practice to use a dedicated user with limited privileges, even in test environments. This helps to catch potential permission issues early and aligns better with security best practices.

Consider creating a dedicated test user for MySQL:

env:
  MYSQL_DATABASE: ${{ secrets.MYSQL_DATABASE }}
  MYSQL_USER: ${{ secrets.MYSQL_TEST_USER }}
  MYSQL_PASSWORD: ${{ secrets.MYSQL_TEST_PASSWORD }}
  MYSQL_ROOT_PASSWORD: ${{ secrets.MYSQL_ROOT_PASSWORD }}

Then, update your application configuration to use these non-root credentials.

Comment on lines +38 to +43
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.5.3
env:
discovery.type: single-node
xpack.security.enabled: true
ELASTIC_PASSWORD: ${{ secrets.MYSQL_ROOT_PASSWORD }}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use a separate secret for Elasticsearch password

Currently, the Elasticsearch password is set to the MySQL root password. This practice reduces security by reusing credentials across different services.

Create a separate secret for the Elasticsearch password and update the configuration:

env:
  discovery.type: single-node
  xpack.security.enabled: true
  ELASTIC_PASSWORD: ${{ secrets.ELASTICSEARCH_PASSWORD }}

Don't forget to add the new ELASTICSEARCH_PASSWORD secret to your GitHub repository secrets.

Comment on lines +64 to +98
- name: Wait for Elasticsearch
run: |
timeout 600 bash -c '
while ! curl -s -u elastic:${{ secrets.MYSQL_ROOT_PASSWORD }} -o /dev/null -w "%{http_code}" http://127.0.0.1:9200 | grep -q "200"; do
echo "Waiting for Elasticsearch..."
sleep 5
done
'

- name: Verify Elasticsearch Health
run: |
curl -u elastic:${{ secrets.MYSQL_ROOT_PASSWORD }} http://127.0.0.1:9200/_cluster/health

- name: Install Elasticsearch plugin
run: |
docker exec $(docker ps -q --filter "name=elasticsearch") \
/usr/share/elasticsearch/bin/elasticsearch-plugin install --batch analysis-nori

- name: Restart Elasticsearch
run: |
docker restart $(docker ps -q --filter "name=elasticsearch")

- name: Wait for Elasticsearch after restart
run: |
timeout 600 bash -c '
until curl -s -u elastic:${{ secrets.MYSQL_ROOT_PASSWORD }} http://127.0.0.1:9200/_cluster/health | grep -q "status\":\"[green|yellow]"; do
echo "Waiting for Elasticsearch to be operational..."
sleep 15
done
'

- name: Verify Elasticsearch Health
run: |
curl -u elastic:${{ secrets.MYSQL_ROOT_PASSWORD }} http://127.0.0.1:9200/_cluster/health

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize Elasticsearch setup and verification process

The current Elasticsearch setup and verification process is thorough but can be streamlined for efficiency. Consider combining the wait, plugin installation, restart, and health check steps into a single step:

- name: Setup and verify Elasticsearch
  run: |
    # Wait for Elasticsearch to be ready
    timeout 600 bash -c '
    until curl -s -u elastic:${{ secrets.ELASTICSEARCH_PASSWORD }} -o /dev/null -w "%{http_code}" http://localhost:9200 | grep -q "200"; do
      echo "Waiting for Elasticsearch..."
      sleep 5
    done
    '
    
    # Install plugin
    docker exec "$(docker ps -q --filter "name=elasticsearch")" \
    /usr/share/elasticsearch/bin/elasticsearch-plugin install --batch analysis-nori
    
    # Restart Elasticsearch
    docker restart "$(docker ps -q --filter "name=elasticsearch")"
    
    # Wait for Elasticsearch to be operational after restart
    timeout 600 bash -c '
    until curl -s -u elastic:${{ secrets.ELASTICSEARCH_PASSWORD }} http://localhost:9200/_cluster/health | grep -q "status\":\"[green|yellow]"; do
      echo "Waiting for Elasticsearch to be operational..."
      sleep 15
    done
    '
    
    # Verify final health
    curl -s -u elastic:${{ secrets.ELASTICSEARCH_PASSWORD }} http://localhost:9200/_cluster/health | jq .

This combined step reduces the number of separate actions while maintaining the necessary checks and setup procedures.

🧰 Tools
🪛 actionlint

78-78: shellcheck reported issue in this script: SC2046:warning:1:13: Quote this to prevent word splitting

(shellcheck)


83-83: shellcheck reported issue in this script: SC2046:warning:1:16: Quote this to prevent word splitting

(shellcheck)

Comment on lines +99 to +131
- name: MySQL connection health check
run: |
for i in {30..0}; do
if mysqladmin ping -h 127.0.0.1 --silent; then
break
fi
echo 'MySQL을 기다리는 중...'
sleep 1
done
if [ $i -eq 0 ]; then
echo 'MySQL이 30초 내에 시작되지 않았습니다.'
exit 1
fi

- name: Verify MySQL connection and configure access
run: |
echo "Showing initial databases:"
mysql --host 127.0.0.1 --port 3306 -uroot -p${{ secrets.MYSQL_ROOT_PASSWORD }} -e "SHOW DATABASES;" || echo "Failed to show initial databases"

echo "Creating hubo database:"
mysql --host 127.0.0.1 --port 3306 -uroot -p${{ secrets.MYSQL_ROOT_PASSWORD }} -e "CREATE DATABASE IF NOT EXISTS hubo;" || echo "Failed to create hubo database"

echo "Creating root user:"
mysql --host 127.0.0.1 --port 3306 -uroot -p${{ secrets.MYSQL_ROOT_PASSWORD }} -e "CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY '${{ secrets.MYSQL_ROOT_PASSWORD }}';" || echo "Failed to create root user"

echo "Granting privileges:"
mysql --host 127.0.0.1 --port 3306 -uroot -p${{ secrets.MYSQL_ROOT_PASSWORD }} -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;" || echo "Failed to grant privileges"

echo "Flushing privileges:"
mysql --host 127.0.0.1 --port 3306 -uroot -p${{ secrets.MYSQL_ROOT_PASSWORD }} -e "FLUSH PRIVILEGES;" || echo "Failed to flush privileges"

echo "Showing final databases:"
mysql --host 127.0.0.1 --port 3306 -uroot -p${{ secrets.MYSQL_ROOT_PASSWORD }} -e "SHOW DATABASES;" || echo "Failed to show final databases"
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize MySQL setup and use English for messages

The current MySQL setup process is thorough but can be optimized. Additionally, using English for messages ensures better understanding across international teams.

Consider the following improvements:

  1. Use English for messages:
- echo 'MySQL을 기다리는 중...'
+ echo 'Waiting for MySQL...'
- echo 'MySQL이 30초 내에 시작되지 않았습니다.'
+ echo 'MySQL did not start within 30 seconds.'
  1. Combine MySQL operations into a single connection for efficiency:
- name: Setup and verify MySQL
  run: |
    # Wait for MySQL
    timeout 30 bash -c 'until mysqladmin ping -h"127.0.0.1" -P3306 -uroot -p"${{ secrets.MYSQL_ROOT_PASSWORD }}" --silent; do echo "Waiting for MySQL..."; sleep 1; done'
    
    # Configure MySQL
    mysql -h127.0.0.1 -P3306 -uroot -p"${{ secrets.MYSQL_ROOT_PASSWORD }}" << EOF
    SHOW DATABASES;
    CREATE DATABASE IF NOT EXISTS hubo;
    CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY '${{ secrets.MYSQL_ROOT_PASSWORD }}';
    GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
    FLUSH PRIVILEGES;
    SHOW DATABASES;
EOF

These changes improve efficiency, readability, and internationalization of the MySQL setup process.

🧰 Tools
🪛 actionlint

100-100: shellcheck reported issue in this script: SC2086:info:8:6: Double quote to prevent globbing and word splitting

(shellcheck)

🪛 yamllint

[error] 117-117: trailing spaces

(trailing-spaces)


[error] 120-120: trailing spaces

(trailing-spaces)


[error] 123-123: trailing spaces

(trailing-spaces)


[error] 126-126: trailing spaces

(trailing-spaces)


[error] 129-129: trailing spaces

(trailing-spaces)

Comment on lines +134 to +144
- name: Debug Elasticsearch before tests
run: |
echo "Checking Elasticsearch status before running tests..."
curl -s -u elastic:${{ secrets.MYSQL_ROOT_PASSWORD }} http://127.0.0.1:9200/_cluster/health | jq .
echo "Listing Elasticsearch indices..."
curl -s -u elastic:${{ secrets.MYSQL_ROOT_PASSWORD }} http://127.0.0.1:9200/_cat/indices?v
echo "Checking Elasticsearch plugins..."
curl -s -u elastic:${{ secrets.MYSQL_ROOT_PASSWORD }} http://127.0.0.1:9200/_cat/plugins?v
echo "Checking Elasticsearch settings..."
curl -s -u elastic:${{ secrets.MYSQL_ROOT_PASSWORD }} http://127.0.0.1:9200/_cluster/settings?pretty

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance security in Elasticsearch debug output

While the debug information is valuable for troubleshooting, it might expose sensitive data in CI logs.

Consider the following improvements:

  1. Mask sensitive information in the output:
- name: Debug Elasticsearch before tests
  run: |
    echo "Checking Elasticsearch status before running tests..."
    curl -s -u elastic:${{ secrets.ELASTICSEARCH_PASSWORD }} http://localhost:9200/_cluster/health | jq 'del(.cluster_name, .cluster_uuid)' | sed 's/\("password":\s*\)"[^"]*"/\1"[REDACTED]"/'
    echo "Listing Elasticsearch indices..."
    curl -s -u elastic:${{ secrets.ELASTICSEARCH_PASSWORD }} http://localhost:9200/_cat/indices?v | sed 's/\(index\s*\)[^ ]*/\1[REDACTED]/'
    echo "Checking Elasticsearch plugins..."
    curl -s -u elastic:${{ secrets.ELASTICSEARCH_PASSWORD }} http://localhost:9200/_cat/plugins?v
    echo "Checking Elasticsearch settings..."
    curl -s -u elastic:${{ secrets.ELASTICSEARCH_PASSWORD }} http://localhost:9200/_cluster/settings?pretty | sed 's/\("password":\s*\)"[^"]*"/\1"[REDACTED]"/'
  1. Consider moving detailed debug information to a separate step that only runs on failure or when explicitly requested.

These changes help protect sensitive information while still providing valuable debug output when needed.

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: 7

🧹 Outside diff range and nitpick comments (3)
src/test/java/com/hubo/gillajabi/point/application/presenation/UserPointControllerTest.java (2)

82-115: Consider extracting the request JSON to a constant or helper method

The test method is well-structured and comprehensive. However, the request JSON is defined inline, which can make the test less readable, especially for larger JSON payloads.

Consider extracting the request JSON to a constant or a helper method. This would improve readability and make it easier to reuse the same JSON structure in other tests if needed. Here's an example of how you could refactor this:

 public class UserPointControllerTest {
+    private static final String VALID_USER_POINT_JSON = """
+            {
+              "content": "여기는 좋은 장소입니다.",
+              "latitude": 37.5665,
+              "longitude": 126.9780,
+              "imageUrls": [
+                "https://example.com/image1.jpg"
+              ]
+            }
+            """.replace("\n", "").replace("\r", "");

     @Test
     void 사용자_포인트_저장_성공() throws Exception {
         // given
-        String requestJson = """
-                {
-                  "content": "여기는 좋은 장소입니다.",
-                  "latitude": 37.5665,
-                  "longitude": 126.9780,
-                  "imageUrls": [
-                    "https://example.com/image1.jpg"
-                  ]
-                }
-                """.replace("\n", "").replace("\r", "");
         ImageUploadUrl imageUploadUrl = userPointFixture.createImageUploadUrl("https://example.com/image1.jpg");

         // ... rest of the test method ...

         mockMvc.perform(
                 post("/api/user-point")
                         .header("Authorization", "Bearer " + tokenResponse.accessToken())
                         .contentType(MediaType.APPLICATION_JSON)
-                        .content(requestJson))
+                        .content(VALID_USER_POINT_JSON))
                 .andDo(print())
                 .andExpect(status().isCreated());

         // ... rest of the test method ...
     }
 }

This change improves the readability of the test method and makes it easier to maintain the JSON structure across multiple tests if needed.


149-180: Remove unused variable

The test method is well-structured and clear. However, there's an unused variable imageUploadUrl that can be removed to improve code cleanliness.

Consider removing the unused imageUploadUrl variable:

     @Test
     void 사용자가_트래킹_중이_아닌_경우() throws Exception {
         // given
         String requestJson = """
                 {
                   "content": "여기는 좋은 장소입니다.",
                   "latitude": 37.5665,
                   "longitude": 126.9780,
                   "imageUrls": [
                     "https://example.com/image1.jpg"
                   ]
                 }
                 """.replace("\n", "").replace("\r", "");
         MemberAuthentication memberAuthentication = userPointFixture.createAndLoadMember("testUser", RoleStatus.USER);
         TokenResponse tokenResponse = userPointFixture.createAndLoadTokenResponse(memberAuthentication);
-        ImageUploadUrl imageUploadUrl = userPointFixture.createImageUploadUrl("https://example.com/image1.jpg");

         // ... rest of the test method ...
     }

This change removes the unused variable, making the test more concise and easier to maintain.

src/test/java/com/hubo/gillajabi/point/fixture/UserPointFixture.java (1)

213-214: Consider removing commented-out code or explain its necessity.

The commented-out code might not be needed anymore. Removing it can improve code readability. If it's still required for future reference or debugging, consider adding a comment explaining why it's kept.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between d63159d and cc77cd5.

📒 Files selected for processing (7)
  • src/main/java/com/hubo/gillajabi/point/domain/entity/UserPoint.java (1 hunks)
  • src/main/java/com/hubo/gillajabi/point/domain/service/UserPointService.java (1 hunks)
  • src/main/java/com/hubo/gillajabi/track/application/dto/request/TrackStartRequest.java (1 hunks)
  • src/main/java/com/hubo/gillajabi/track/domain/entity/TrackRecord.java (4 hunks)
  • src/main/java/com/hubo/gillajabi/track/domain/entity/TrackStatus.java (1 hunks)
  • src/test/java/com/hubo/gillajabi/point/application/presenation/UserPointControllerTest.java (1 hunks)
  • src/test/java/com/hubo/gillajabi/point/fixture/UserPointFixture.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/hubo/gillajabi/point/domain/entity/UserPoint.java
  • src/main/java/com/hubo/gillajabi/point/domain/service/UserPointService.java
🔇 Additional comments (7)
src/main/java/com/hubo/gillajabi/track/domain/entity/TrackStatus.java (1)

39-41: Approved: Improved parameter naming in createByMemberAndTrackId method

The change from id to trackId in both the method signature and its usage within the method body enhances code clarity and maintainability. This modification better reflects the purpose of the parameter and improves overall readability.

To ensure this change doesn't introduce any issues, please verify that all calls to this method have been updated accordingly. Run the following script to check for any remaining usages of the old method signature:

If the script returns any results, those locations may need to be updated to use the new trackId parameter name.

src/main/java/com/hubo/gillajabi/track/domain/entity/TrackRecord.java (4)

7-7: LGTM: Import statements updated appropriately.

The changes to the import statements are appropriate:

  1. The addition of java.util.ArrayList is necessary for the new implementation of the photoPoints field.
  2. The more specific Lombok imports improve code clarity and reduce potential conflicts.

Also applies to: 11-11


19-19: LGTM: Builder pattern added to TrackRecord class.

The addition of the @Builder annotation is appropriate and consistent with the changes made to the photoPoints field and the createByMember method. This will provide a more flexible way to create TrackRecord instances.


37-38: LGTM: Improved initialization of photoPoints field.

The changes to the photoPoints field are well-implemented:

  1. The @Builder.Default annotation ensures that the default value is used when the builder pattern is employed.
  2. Initializing with an empty ArrayList prevents potential null pointer exceptions and is a good practice.
  3. This change is consistent with the addition of the @Builder annotation to the class.

Line range hint 1-86: Overall assessment: Good improvements with minor suggestions.

The changes to the TrackRecord class are generally positive:

  1. Improved import statements for better clarity.
  2. Added @Builder annotation for more flexible instance creation.
  3. Better initialization of the photoPoints field to prevent null pointer exceptions.
  4. New addPhotoPoint method for adding multiple photo points.

The suggested improvements to the addPhotoPoint method will further enhance the robustness and consistency of the class. Once these are addressed, the changes will significantly improve the overall quality of the TrackRecord class.

src/test/java/com/hubo/gillajabi/point/application/presenation/UserPointControllerTest.java (2)

182-201: Well-structured test for user point deletion

This test method is well-implemented and covers the successful scenario for user point deletion. It properly sets up the test data, performs the deletion operation, and verifies the result.

The test method is comprehensive and follows good testing practices. It creates the necessary test data, performs the deletion operation, and then verifies that the point has been removed from the database. Keep up the good work!


224-247: Well-implemented test for unauthorized point deletion

This test method effectively covers the scenario where a user attempts to delete a point that doesn't belong to them. It properly sets up two different users, creates a point for one user, and then attempts to delete it with the other user's credentials.

The test is well-structured and comprehensive. It correctly:

  1. Sets up two different users
  2. Creates a point for one user
  3. Attempts to delete the point using the other user's credentials
  4. Verifies that the operation fails with a 403 Forbidden status
  5. Checks the error message

This approach ensures that the system properly enforces access control for point deletion. Good job on covering this important security aspect!

@@ -13,7 +13,7 @@

@Getter
@Setter
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

⚠️ Potential issue

Inconsistent Constructor Access Levels for DTOs

The @AllArgsConstructor annotation in TrackStartRequest.java has been changed to use the default public access level. However, the shell script analysis reveals that other DTOs within the codebase have varying access levels specified for @AllArgsConstructor. This inconsistency can lead to unintended instantiation from outside the package and may violate encapsulation principles.

  • Recommendation:
    • Standardize the access levels for all @AllArgsConstructor annotations across DTOs. If public access is intended, explicitly specify @AllArgsConstructor(access = AccessLevel.PUBLIC) to enhance code readability and maintain consistency.
    • Review other DTO classes to ensure that their constructor access levels align with the project's design principles and encapsulation requirements.
🔗 Analysis chain

Consider the implications of changing the @AllArgsConstructor access level.

The @AllArgsConstructor annotation has been modified to remove the AccessLevel.PROTECTED specification. This change makes the all-args constructor public by default, which could have several implications:

  1. It increases the visibility of the constructor, potentially allowing unintended instantiation from outside the package.
  2. It may conflict with the principle of encapsulation, as DTOs are often designed to be immutable or have controlled construction.
  3. It could lead to inconsistencies with other similar DTOs in the codebase if they follow a different pattern.

Consider whether this change is intentional and aligns with your project's design principles. If the intention is to allow public access, it might be clearer to explicitly specify @AllArgsConstructor(access = AccessLevel.PUBLIC) for better code readability and to show that this is a deliberate choice.

To assess the impact and consistency of this change, let's verify the usage of @AllArgsConstructor across the codebase:

This will help us understand if this change is consistent with other DTOs and how it might affect the usage of TrackStartRequest.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the usage of @AllArgsConstructor in other DTOs

# Search for @AllArgsConstructor usage in Java files
echo "Usage of @AllArgsConstructor in the codebase:"
rg --type java "@AllArgsConstructor" -C 2

# Check for other classes in the same package
echo "\nOther classes in the same package:"
fd -e java . "src/main/java/com/hubo/gillajabi/track/application/dto/request"

# Check for usage of TrackStartRequest
echo "\nUsage of TrackStartRequest:"
rg "TrackStartRequest" -t java -C 2

Length of output: 40018

@@ -52,7 +52,7 @@ public class TrackRecord extends BaseEntity {
private Long calorie;

public static TrackRecord createByMember(final Member member, final Course course) {
return new TrackRecord(null, null, null, null, null, member, course, 0L,BigDecimal.ZERO,0L);
return new TrackRecord(null, null, null, null, new ArrayList<>(), member, course, 0L,BigDecimal.ZERO,0L);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improvements needed for consistency and robustness.

The changes to createByMember and the addition of addPhotoPoint are good steps, but there are some areas for improvement:

  1. In createByMember, the initialization of photoPoints with new ArrayList<>() is consistent with the field changes. This is good.

  2. For the new addPhotoPoint method:
    a. The parameter name photoPoint is singular while it's a List. Consider renaming to photoPoints for clarity.
    b. Add a null check on the input list to prevent potential NullPointerExceptions.
    c. For consistency with other methods like addPhotoPoints, consider using the PhotoPoint.addTrackRecord utility method.

Consider refactoring the addPhotoPoint method as follows:

public void addPhotoPoint(List<PhotoPoint> photoPoints) {
    if (photoPoints != null) {
        this.photoPoints.addAll(photoPoints);
        PhotoPoint.addTrackRecord(new HashSet<>(photoPoints), this);
    }
}

This change improves consistency with other methods, adds null safety, and maintains the bidirectional relationship between TrackRecord and PhotoPoint.

Also applies to: 84-86

Comment on lines +1 to +248
@DisplayName("트래킹 중이 아닌경우 실패")
@Test
void 사용자가_트래킹_중이_아닌_경우() throws Exception {
// given
String requestJson = """
{
"content": "여기는 좋은 장소입니다.",
"latitude": 37.5665,
"longitude": 126.9780,
"imageUrls": [
"https://example.com/image1.jpg"
]
}
""".replace("\n", "").replace("\r", "");
MemberAuthentication memberAuthentication = userPointFixture.createAndLoadMember("testUser", RoleStatus.USER);
TokenResponse tokenResponse = userPointFixture.createAndLoadTokenResponse(memberAuthentication);
ImageUploadUrl imageUploadUrl = userPointFixture.createImageUploadUrl("https://example.com/image1.jpg");

// when
String result = mockMvc.perform(
post("/api/user-point")
.header("Authorization", "Bearer " + tokenResponse.accessToken())
.contentType(MediaType.APPLICATION_JSON)
.content(requestJson))
.andDo(print())
.andExpect(status().isBadRequest())
.andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8);

// then
JsonNode rootNode = objectMapper.readTree(result);
assertThat(rootNode.get("message").asText()).isEqualTo("현재 트래킹중이 아닙니다.");
}

@DisplayName("사용자 포인트 삭제가 성공한다")
@Test
void 사용자_포인트_삭제_성공() throws Exception {
// given
MemberAuthentication memberAuthentication = userPointFixture.createAndLoadMember("testUser", RoleStatus.USER);
TokenResponse tokenResponse = userPointFixture.createAndLoadTokenResponse(memberAuthentication);
UserPointDocument userPointDocument = userPointFixture.createAndLoadUserPointDocument(memberAuthentication.getMember());

// when
mockMvc.perform(
delete("/api/user-point/{id}", userPointDocument.getUserPointId())
.header("Authorization", "Bearer " + tokenResponse.accessToken())
.contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isNoContent());

// then
List<UserPointDocument> userPointDocuments = userPointDocumentRepository.findAll();
assertThat(userPointDocuments).isEmpty();
}

@DisplayName("사용자 포인트 삭제 시 해당 사용자의 포인트가 없을 경우 실패한다")
@Test
void 사용자_포인트_삭제시_해당_사용자의_포인트가_없을_경우_실패한다() throws Exception {
// given
MemberAuthentication memberAuthentication = userPointFixture.createAndLoadMember("testUser", RoleStatus.USER);
TokenResponse tokenResponse = userPointFixture.createAndLoadTokenResponse(memberAuthentication);

// when
String result = mockMvc.perform(
delete("/api/user-point/{id}", 1L)
.header("Authorization", "Bearer " + tokenResponse.accessToken())
.contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isNotFound())
.andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8);

// then
JsonNode rootNode = objectMapper.readTree(result);
assertThat(rootNode.get("message").asText()).isEqualTo("포인트를 찾을 수 없습니다.");
}

@DisplayName("사용자 포인트 삭제시 해당 사용자의 포인트가 아닐 경우 실패한다")
@Test
void 사용자_포인트_삭제시_해당_사용자의_포인트가_아닐_경우_실패() throws Exception {
// given
MemberAuthentication memberAuthentication = userPointFixture.createAndLoadMember("testUser", RoleStatus.USER);
TokenResponse tokenResponse = userPointFixture.createAndLoadTokenResponse(memberAuthentication);
UserPointDocument userPointDocument = userPointFixture.createAndLoadUserPointDocument(memberAuthentication.getMember());
MemberAuthentication otherMemberAuthentication = userPointFixture.createAndLoadMember("otherUser", RoleStatus.USER);
TokenResponse otherTokenResponse = userPointFixture.createAndLoadTokenResponse(otherMemberAuthentication);

// when
String result = mockMvc.perform(
delete("/api/user-point/{id}", userPointDocument.getUserPointId())
.header("Authorization", "Bearer " + otherTokenResponse.accessToken())
.contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isForbidden())
.andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8);

// then
JsonNode rootNode = objectMapper.readTree(result);
assertThat(rootNode.get("message").asText()).isEqualTo("포인트 소유자가 아닙니다.");
}
}

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using @nested classes for better test organization

The test class is comprehensive and covers various scenarios well. To further improve the organization and readability of the tests, consider using JUnit 5's @Nested annotation to group related tests together.

Restructure the test class using @Nested inner classes to group related tests. This can make the test suite more organized and easier to understand. Here's an example of how you could refactor:

@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@Transactional
@ActiveProfiles("test")
class UserPointControllerTest {
    // ... existing setup code ...

    @Nested
    @DisplayName("User Point Creation Tests")
    class UserPointCreationTests {
        @Test
        void 사용자_포인트_저장_성공() throws Exception {
            // ... existing test code ...
        }

        @Test
        void 사용자의_포인트_저장_요청에서_이미지가_유효하지_않을_경우() throws Exception {
            // ... existing test code ...
        }

        @Test
        void 사용자가_트래킹_중이_아닌_경우() throws Exception {
            // ... existing test code ...
        }
    }

    @Nested
    @DisplayName("User Point Deletion Tests")
    class UserPointDeletionTests {
        @Test
        void 사용자_포인트_삭제_성공() throws Exception {
            // ... existing test code ...
        }

        @Test
        void 사용자_포인트_삭제시_해당_사용자의_포인트가_없을_경우_실패한다() throws Exception {
            // ... existing test code ...
        }

        @Test
        void 사용자_포인트_삭제시_해당_사용자의_포인트가_아닐_경우_실패() throws Exception {
            // ... existing test code ...
        }
    }
}

This structure groups related tests together, making the test suite more organized and easier to navigate. It also allows for shared setup methods within each nested class if needed.

Comment on lines +70 to +80
@AfterEach
void cleanupAfterTest() {
mongoTemplate.getCollectionNames().forEach(collectionName -> {
mongoTemplate.dropCollection(collectionName);
});

RedisServerCommands serverCommands = Objects.requireNonNull(redisTemplate.getConnectionFactory())
.getConnection()
.serverCommands();
serverCommands.flushAll();
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using @DirtiesContext for test isolation

The current cleanup method manually drops MongoDB collections and flushes Redis. While this approach works, it might be more efficient and cleaner to use Spring's @DirtiesContext annotation. This annotation ensures a clean application context for each test method, which is particularly useful when dealing with embedded databases or in-memory data stores.

Consider replacing the cleanupAfterTest method with the @DirtiesContext annotation at the class level:

 @AutoConfigureMockMvc
 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
 @Transactional
 @ActiveProfiles("test")
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
 public class UserPointControllerTest {
     // ... existing code ...

-    @AfterEach
-    void cleanupAfterTest() {
-        mongoTemplate.getCollectionNames().forEach(collectionName -> {
-            mongoTemplate.dropCollection(collectionName);
-        });
-
-        RedisServerCommands serverCommands = Objects.requireNonNull(redisTemplate.getConnectionFactory())
-                .getConnection()
-                .serverCommands();
-        serverCommands.flushAll();
-    }

This change will ensure that the application context is reset after each test method, providing a clean state without the need for manual cleanup.

📝 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
@AfterEach
void cleanupAfterTest() {
mongoTemplate.getCollectionNames().forEach(collectionName -> {
mongoTemplate.dropCollection(collectionName);
});
RedisServerCommands serverCommands = Objects.requireNonNull(redisTemplate.getConnectionFactory())
.getConnection()
.serverCommands();
serverCommands.flushAll();
}
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@Transactional
@ActiveProfiles("test")
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class UserPointControllerTest {
// ... existing code ...
}

Comment on lines +203 to +222
@DisplayName("사용자 포인트 삭제 시 해당 사용자의 포인트가 없을 경우 실패한다")
@Test
void 사용자_포인트_삭제시_해당_사용자의_포인트가_없을_경우_실패한다() throws Exception {
// given
MemberAuthentication memberAuthentication = userPointFixture.createAndLoadMember("testUser", RoleStatus.USER);
TokenResponse tokenResponse = userPointFixture.createAndLoadTokenResponse(memberAuthentication);

// when
String result = mockMvc.perform(
delete("/api/user-point/{id}", 1L)
.header("Authorization", "Bearer " + tokenResponse.accessToken())
.contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isNotFound())
.andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8);

// then
JsonNode rootNode = objectMapper.readTree(result);
assertThat(rootNode.get("message").asText()).isEqualTo("포인트를 찾을 수 없습니다.");
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using a non-existent ID consistently

The test method is well-structured and tests the correct scenario. However, the use of 1L as a non-existent ID might lead to false positives if an ID with value 1 happens to exist in the future.

Consider using a method to generate a guaranteed non-existent ID. This will make the test more robust and less likely to produce false positives. Here's a suggested change:

+    private Long getNonExistentPointId() {
+        return userPointDocumentRepository.findAll().stream()
+                .mapToLong(UserPointDocument::getUserPointId)
+                .max()
+                .orElse(0L) + 1;
+    }

     @Test
     void 사용자_포인트_삭제시_해당_사용자의_포인트가_없을_경우_실패한다() throws Exception {
         // given
         MemberAuthentication memberAuthentication = userPointFixture.createAndLoadMember("testUser", RoleStatus.USER);
         TokenResponse tokenResponse = userPointFixture.createAndLoadTokenResponse(memberAuthentication);
+        Long nonExistentId = getNonExistentPointId();

         // when
         String result = mockMvc.perform(
-                        delete("/api/user-point/{id}", 1L)
+                        delete("/api/user-point/{id}", nonExistentId)
                                 .header("Authorization", "Bearer " + tokenResponse.accessToken())
                                 .contentType(MediaType.APPLICATION_JSON))
                 .andDo(print())
                 .andExpect(status().isNotFound())
                 .andReturn().getResponse().getContentAsString(StandardCharsets.UTF_8);

         // ... rest of the test method ...
     }

This change ensures that the test always uses a non-existent ID, making it more reliable and less prone to false positives.

Committable suggestion was skipped due to low confidence.

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: 0

🧹 Outside diff range and nitpick comments (1)
src/test/java/com/hubo/gillajabi/crawl/domain/service/duru/RoadGpxInfoDuruServiceTest.java (1)

22-22: Improved mock API response generation.

The changes to the mockApiResponseService method enhance the test's reliability and predictability:

  1. The method signature now correctly declares throws JsonProcessingException, aligning with the use of XmlMapper.writeValueAsString().
  2. Using fixtureMonkey.giveMeBuilder() instead of fixtureMonkey.giveMeOne() allows for more precise control over the generated test data.
  3. Constraining the size of trkseg and trkpt lists makes the tests more focused and predictable.

These improvements will lead to more reliable and targeted tests. Great job!

Consider extracting the magic numbers (1 and 2) used for list sizes into named constants for better readability and maintainability. For example:

private static final int TRKSEG_SIZE = 1;
private static final int TRKPT_SIZE = 2;

// ...

.size("trk.trkseg", TRKSEG_SIZE)
.size("trk.trkseg[*].trkpt", TRKPT_SIZE)

This makes it easier to understand the intent behind these specific sizes and allows for easy modification if needed in the future.

Also applies to: 54-57

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between cc77cd5 and 0e68491.

📒 Files selected for processing (2)
  • src/test/java/com/hubo/gillajabi/crawl/domain/service/duru/RoadGpxInfoDuruServiceTest.java (2 hunks)
  • src/test/java/com/hubo/gillajabi/point/application/resolver/UserPointSearchResolverTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/java/com/hubo/gillajabi/point/application/resolver/UserPointSearchResolverTest.java

@haroya01 haroya01 merged commit 420bc21 into develop Oct 4, 2024
8 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
develoap 개발 관련 작업
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant