Conversation
개요AWS S3 SDK 의존성을 추가하고 Cloudflare R2 기반 이미지 업로드 기능을 구현합니다. 설정 속성, S3 클라이언트 빈, 스토리지 서비스 계약/구현, REST API 엔드포인트, 인증 보안, 테스트를 포함합니다. 변경사항R2 이미지 업로드 기능
이미지 업로드 시퀀스sequenceDiagram
participant Client
participant StorageController
participant R2StorageService
participant S3Client
participant R2Storage["Cloudflare R2"]
Client->>StorageController: POST /storage/images (multipart)
StorageController->>R2StorageService: uploadImage(file, "images")
R2StorageService->>R2StorageService: validateImage (크기, MIME 타입)
R2StorageService->>R2StorageService: buildObjectKey (UUID + 확장자)
R2StorageService->>S3Client: putObject (PutObjectRequest)
S3Client->>R2Storage: PUT object
R2Storage-->>S3Client: success
R2StorageService->>R2StorageService: 공개 URL 조립 (publicUrl + key)
R2StorageService-->>StorageController: imageUrl String
StorageController-->>Client: ImageUploadResponse (200 OK)
예상 코드 리뷰 난이도🎯 3 (보통) | ⏱️ ~35분 관련 PR
시 🐰
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/main/java/com/valanse/valanse/common/config/R2Properties.java`:
- Around line 6-15: R2Properties lacks validation so null/empty values cause
ambiguous exceptions when R2Config.s3Client calls URI.create and
AwsBasicCredentials.create; add validation to enforce required fields by
annotating the class with `@Validated` (or switching to constructor binding with
`@ConstructorBinding`) and mark each required String field (accountId, endpoint,
bucket, accessKey, secretKey, publicUrl if required) with `@NotBlank/`@NotNull (or
use `@NotEmpty`) and provide clear validation messages so Spring fails fast with a
readable error on startup; ensure R2Config.s3Client continues to reference
properties.getEndpoint(), getAccessKey(), getSecretKey() but rely on validated
properties to avoid NPE/IllegalArgumentException.
In
`@src/main/java/com/valanse/valanse/service/StorageService/R2StorageService.java`:
- Around line 57-68: validateImage currently only checks file size and MIME type
(ALLOWED_IMAGE_TYPES, MAX_IMAGE_SIZE) which can be spoofed; update
R2StorageService.validateImage to also verify the binary is a real image by
attempting to decode it (e.g., ImageIO.read(file.getInputStream()) and throwing
ApiException if it returns null) and, because ImageIO doesn't support all
formats (e.g., webp), add a fallback magic-byte check for JPEG/PNG/GIF/WEBP by
peeking the first bytes of file.getInputStream() (ensure the stream is
resettable or use a buffered copy) and throw ApiException on mismatch; wrap
IOExceptions into ApiException and close streams properly.
In
`@src/test/java/com/valanse/valanse/service/StorageService/R2StorageServiceTest.java`:
- Around line 50-60: Update the tests for storageService.uploadImage to assert
detailed ApiException properties and S3 interactions: in the non-image test
assert the thrown ApiException's ErrorCode and message, and verify the mocked
s3Client.putObject (or equivalent) was never invoked; add a test that stubs
s3Client.putObject to throw S3Exception and assert the service maps it to
ErrorCode.BAD_GATEWAY; add a test that stubs MultipartFile.getBytes() to throw
IOException and assert the service maps it to ErrorCode.BAD_REQUEST (use
ApiException exception = assertThrows(...) and
assertThat(exception.getErrorCode()).isEqualTo(...), and use verify(s3Client,
never()).putObject(...) to confirm no S3 call on validation failure).
- Around line 34-48: Update the R2StorageServiceTest to strengthen S3 call
verification and add boundary tests: when asserting the putObject call
(s3Client.putObject) capture the PutObjectRequest and RequestBody (or use
argument matchers) and verify the request's bucket name, key/path contains the
"images" prefix, and the PutObjectRequest's metadata/content-type equals the
file's MIME type; also assert the RequestBody/byte content length equals the
uploaded byte length. Add tests calling storageService.uploadImage for oversized
input (e.g., 11MB) and empty/null payloads asserting ApiException is thrown (use
assertThrows(ApiException.class, ...)) and ensure the existing positive test
still verifies URL format and that putObject was invoked with the correct
captured values.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7c0e5a76-c18c-4605-8b13-ebc2917d9bc9
📒 Files selected for processing (10)
build.gradlesrc/main/java/com/valanse/valanse/common/config/R2Config.javasrc/main/java/com/valanse/valanse/common/config/R2Properties.javasrc/main/java/com/valanse/valanse/common/config/SecurityConfig.javasrc/main/java/com/valanse/valanse/controller/StorageController.javasrc/main/java/com/valanse/valanse/dto/Storage/ImageUploadResponse.javasrc/main/java/com/valanse/valanse/service/StorageService/R2StorageService.javasrc/main/java/com/valanse/valanse/service/StorageService/StorageService.javasrc/test/java/com/valanse/valanse/service/StorageService/R2StorageServiceTest.javasrc/test/resources/application-test.yml
| @Component | ||
| @ConfigurationProperties(prefix = "cloudflare.r2") | ||
| public class R2Properties { | ||
|
|
||
| private String accountId; | ||
| private String endpoint; | ||
| private String bucket; | ||
| private String accessKey; | ||
| private String secretKey; | ||
| private String publicUrl; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# R2Properties의 필수값 검증 어노테이션 적용 여부 확인
fd -i R2Properties.java src | xargs -I{} sh -c '
echo "== {} ==";
rg -n "`@ConfigurationProperties`|`@Validated`|`@NotBlank`|private String (accountId|endpoint|bucket|accessKey|secretKey|publicUrl)" "{}"
'Repository: ValanSee/ValanSe_Server
Length of output: 386
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) R2Properties.java 전체 확인
echo "---- R2Properties.java (head/tail with line numbers) ----"
FILE="src/main/java/com/valanse/valanse/common/config/R2Properties.java"
if [ -f "$FILE" ]; then
nl -ba "$FILE" | sed -n '1,220p'
else
echo "Missing $FILE"
fi
# 2) 이 프로퍼티(클래스)를 참조하는 곳 확인
echo
echo "---- Usages of R2Properties ----"
rg -n "R2Properties" src || true
# 3) 공용 URL/endpoint/accountId 등을 실제로 쓰는 곳에서 URI.create(등) 여부 확인
echo
echo "---- URI.create usages around R2/endpoint/publicUrl ----"
rg -n "URI\.create|publicUrl|endpoint|accountId|secretKey|accessKey" src/main/java/com/valanse/valanse || trueRepository: ValanSee/ValanSe_Server
Length of output: 186
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/main/java/com/valanse/valanse/common/config/R2Properties.java"
echo "---- R2Properties.java (line-numbered) ----"
if [ -f "$FILE" ]; then
cat -n "$FILE" | sed -n '1,220p'
else
echo "Missing $FILE"
fi
echo
echo "---- Usages of R2Properties ----"
rg -n "R2Properties" src || true
echo
echo "---- URI.create usages and related fields ----"
rg -n "URI\.create" src || true
rg -n "publicUrl|endpoint|accountId|accessKey|secretKey|bucket" src/main/java/com/valanse/valanse || trueRepository: ValanSee/ValanSe_Server
Length of output: 6433
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# show relevant sections
echo "---- R2Config.java (key parts) ----"
FILE="src/main/java/com/valanse/valanse/common/config/R2Config.java"
if [ -f "$FILE" ]; then
sed -n '1,120p' "$FILE" | nl -ba
else
echo "Missing $FILE"
fi
echo
echo "---- R2StorageService.java (key parts) ----"
FILE="src/main/java/com/valanse/valanse/service/StorageService/R2StorageService.java"
if [ -f "$FILE" ]; then
sed -n '1,120p' "$FILE" | nl -ba
else
echo "Missing $FILE"
fiRepository: ValanSee/ValanSe_Server
Length of output: 164
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "---- R2Config.java (selected lines) ----"
FILE="src/main/java/com/valanse/valanse/common/config/R2Config.java"
if [ -f "$FILE" ]; then
cat -n "$FILE" | sed -n '1,140p'
else
echo "Missing $FILE"
fi
echo
echo "---- R2StorageService.java (selected lines) ----"
FILE="src/main/java/com/valanse/valanse/service/StorageService/R2StorageService.java"
if [ -f "$FILE" ]; then
cat -n "$FILE" | sed -n '1,140p'
else
echo "Missing $FILE"
fiRepository: ValanSee/ValanSe_Server
Length of output: 4918
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "import (jakarta\.validation|javax\.validation)\.constraints\.NotBlank|`@NotBlank`|import org\.springframework\.validation\.annotation\.Validated|`@Validated`" src || trueRepository: ValanSee/ValanSe_Server
Length of output: 49
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "---- root build files ----"
ls -la
# pom.xml / build.gradle 계열 확인
rg -n "spring-boot-starter-validation|validation-api|jakarta\.validation|javax\.validation" -S . || trueRepository: ValanSee/ValanSe_Server
Length of output: 1136
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "---- build.gradle (validation-related section) ----"
sed -n '1,120p' build.gradle | cat -n
echo
echo "---- jakarta/javax validation imports in src ----"
rg -n "jakarta\.validation|javax\.validation" src || true
echo
echo "---- Spring `@Validated` usage in src ----"
rg -n "org\.springframework\.validation\.annotation\.Validated|`@Validated`" src || trueRepository: ValanSee/ValanSe_Server
Length of output: 3036
R2 필수 설정값을 부팅 시점에 검증해 주세요.
- R2Properties는
@ConfigurationProperties만있고@Validated/@notblank 제약이 없어 null/빈 문자열이 그대로 바인딩됩니다. - 실제로 R2Config.s3Client에서 URI.create(properties.getEndpoint())와 AwsBasicCredentials.create(properties.getAccessKey(), properties.getSecretKey())를 즉시 호출하므로 null/형식 불일치 시 부팅 중 NPE/IllegalArgumentException 같은 모호한 예외로 실패할 수 있습니다.
제안 diff
+import jakarta.validation.constraints.NotBlank;
import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
import org.springframework.stereotype.Component;
`@Component`
`@ConfigurationProperties`(prefix = "cloudflare.r2")
+@Validated
public class R2Properties {
+ `@NotBlank`
private String accountId;
+ `@NotBlank`
private String endpoint;
+ `@NotBlank`
private String bucket;
+ `@NotBlank`
private String accessKey;
+ `@NotBlank`
private String secretKey;
+ `@NotBlank`
private String publicUrl;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/valanse/valanse/common/config/R2Properties.java` around
lines 6 - 15, R2Properties lacks validation so null/empty values cause ambiguous
exceptions when R2Config.s3Client calls URI.create and
AwsBasicCredentials.create; add validation to enforce required fields by
annotating the class with `@Validated` (or switching to constructor binding with
`@ConstructorBinding`) and mark each required String field (accountId, endpoint,
bucket, accessKey, secretKey, publicUrl if required) with `@NotBlank/`@NotNull (or
use `@NotEmpty`) and provide clear validation messages so Spring fails fast with a
readable error on startup; ensure R2Config.s3Client continues to reference
properties.getEndpoint(), getAccessKey(), getSecretKey() but rely on validated
properties to avoid NPE/IllegalArgumentException.
| private void validateImage(MultipartFile file) { | ||
| if (file == null || file.isEmpty()) { | ||
| throw new ApiException("업로드할 이미지 파일을 선택해주세요.", HttpStatus.BAD_REQUEST); | ||
| } | ||
|
|
||
| if (file.getSize() > MAX_IMAGE_SIZE) { | ||
| throw new ApiException("이미지는 5MB 이하만 업로드할 수 있습니다.", HttpStatus.BAD_REQUEST); | ||
| } | ||
|
|
||
| if (!ALLOWED_IMAGE_TYPES.contains(file.getContentType())) { | ||
| throw new ApiException("jpg, png, webp, gif 이미지 파일만 업로드할 수 있습니다.", HttpStatus.BAD_REQUEST); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# MIME-only 검증 여부 확인 (바이너리 시그니처 검증 코드 유무 탐색)
fd -i R2StorageService.java src | xargs -I{} sh -c '
echo "== {} ==";
rg -n "ALLOWED_IMAGE_TYPES|getContentType|ImageIO|Tika|magic" "{}"
'Repository: ValanSee/ValanSe_Server
Length of output: 348
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/main/java/com/valanse/valanse/service/StorageService/R2StorageService.java"
echo "===== validateImage() snippet ====="
nl -ba "$FILE" | sed -n '40,110p'
echo "===== allowed types set ====="
nl -ba "$FILE" | sed -n '15,55p'
echo "===== repo-wide image signature validation hints ====="
rg -n "ImageIO|Tika|magic|contentType\\(file\\.getContentType\\)|getInputStream\\(" src/main/java || true
echo "===== validateImage 호출/흐름 ====="
rg -n "validateImage\\(" -S src/main/java || trueRepository: ValanSee/ValanSe_Server
Length of output: 164
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/main/java/com/valanse/valanse/service/StorageService/R2StorageService.java"
echo "===== validateImage() snippet (line 40-110) ====="
awk 'NR>=40 && NR<=110 {printf "%5d:%s\n", NR, $0}' "$FILE" || true
echo "===== allowed types set (line 15-55) ====="
awk 'NR>=15 && NR<=55 {printf "%5d:%s\n", NR, $0}' "$FILE" || true
echo "===== repo-wide image signature validation hints ====="
rg -n "ImageIO|Tika|magic|getInputStream\\(" src/main/java || true
rg -n "contentType\\(file\\.getContentType\\)|getContentType\\(\\)" src/main/java || true
echo "===== validateImage 호출/흐름 ====="
rg -n "validateImage\\(" src/main/java || trueRepository: ValanSee/ValanSe_Server
Length of output: 4568
MIME 타입만으로는 이미지 검증이 부족합니다.
src/main/java/com/valanse/valanse/service/StorageService/R2StorageService.java의 validateImage()는 file.getContentType()와 크기만 검사하고(라인 66-68), 매직 바이트/디코딩 가능 여부 같은 바이너리 시그니처 검증이 없습니다. 따라서 Content-Type을 위조한 비이미지 업로드가 통과할 수 있으니, 최소한 ImageIO.read(...) 또는 매직 바이트 기반 검사로 유효한 이미지인지 확인을 추가해 주세요.
제안 diff
+import javax.imageio.ImageIO;
+import java.io.InputStream;
@@
private void validateImage(MultipartFile file) {
@@
if (!ALLOWED_IMAGE_TYPES.contains(file.getContentType())) {
throw new ApiException("jpg, png, webp, gif 이미지 파일만 업로드할 수 있습니다.", HttpStatus.BAD_REQUEST);
}
+
+ try (InputStream in = file.getInputStream()) {
+ if (ImageIO.read(in) == null) {
+ throw new ApiException("유효한 이미지 파일이 아닙니다.", HttpStatus.BAD_REQUEST);
+ }
+ } catch (IOException e) {
+ throw new ApiException("이미지 파일을 읽을 수 없습니다.", HttpStatus.BAD_REQUEST);
+ }
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/main/java/com/valanse/valanse/service/StorageService/R2StorageService.java`
around lines 57 - 68, validateImage currently only checks file size and MIME
type (ALLOWED_IMAGE_TYPES, MAX_IMAGE_SIZE) which can be spoofed; update
R2StorageService.validateImage to also verify the binary is a real image by
attempting to decode it (e.g., ImageIO.read(file.getInputStream()) and throwing
ApiException if it returns null) and, because ImageIO doesn't support all
formats (e.g., webp), add a fallback magic-byte check for JPEG/PNG/GIF/WEBP by
peeking the first bytes of file.getInputStream() (ensure the stream is
resettable or use a buffered copy) and throw ApiException on mismatch; wrap
IOExceptions into ApiException and close streams properly.
| @Test | ||
| void 이미지를_R2에_업로드하고_공개_URL을_반환한다() { | ||
| MockMultipartFile file = new MockMultipartFile( | ||
| "file", | ||
| "sample.png", | ||
| "image/png", | ||
| "image".getBytes() | ||
| ); | ||
|
|
||
| String imageUrl = storageService.uploadImage(file, "images"); | ||
|
|
||
| assertThat(imageUrl).startsWith("https://cdn.example.com/images/"); | ||
| assertThat(imageUrl).endsWith(".png"); | ||
| verify(s3Client).putObject(any(PutObjectRequest.class), any(RequestBody.class)); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift
S3 호출 검증이 불충분하고 파일 크기 검증 테스트가 누락되었습니다.
현재 테스트는 any() 매처만 사용하여 putObject 호출 여부만 확인하고 있습니다. 컨텍스트에 따르면 서비스는 파일 크기 및 MIME 타입을 검증해야 하지만, 이에 대한 테스트가 없습니다.
다음 개선사항을 권장합니다:
putObject호출 시 올바른 버킷명, 키(경로), Content-Type이 전달되는지 검증- 파일 크기 제한 초과 시 예외 발생 테스트 추가
- 빈 파일, null 파일 등 경계 케이스 테스트 추가
🧪 검증 강화 예시
`@Test`
void 이미지를_R2에_업로드하고_공개_URL을_반환한다() {
MockMultipartFile file = new MockMultipartFile(
"file",
"sample.png",
"image/png",
"image".getBytes()
);
String imageUrl = storageService.uploadImage(file, "images");
assertThat(imageUrl).startsWith("https://cdn.example.com/images/");
assertThat(imageUrl).endsWith(".png");
- verify(s3Client).putObject(any(PutObjectRequest.class), any(RequestBody.class));
+
+ ArgumentCaptor<PutObjectRequest> requestCaptor = ArgumentCaptor.forClass(PutObjectRequest.class);
+ verify(s3Client).putObject(requestCaptor.capture(), any(RequestBody.class));
+
+ PutObjectRequest request = requestCaptor.getValue();
+ assertThat(request.bucket()).isEqualTo("test-bucket");
+ assertThat(request.key()).startsWith("images/");
+ assertThat(request.contentType()).isEqualTo("image/png");
}추가 테스트 케이스:
`@Test`
void 파일_크기가_제한을_초과하면_예외가_발생한다() {
byte[] largeContent = new byte[11 * 1024 * 1024]; // 11MB
MockMultipartFile file = new MockMultipartFile(
"file",
"large.png",
"image/png",
largeContent
);
assertThrows(ApiException.class, () -> storageService.uploadImage(file, "images"));
}
`@Test`
void 빈_파일은_업로드하지_않는다() {
MockMultipartFile file = new MockMultipartFile(
"file",
"empty.png",
"image/png",
new byte[0]
);
assertThrows(ApiException.class, () -> storageService.uploadImage(file, "images"));
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/test/java/com/valanse/valanse/service/StorageService/R2StorageServiceTest.java`
around lines 34 - 48, Update the R2StorageServiceTest to strengthen S3 call
verification and add boundary tests: when asserting the putObject call
(s3Client.putObject) capture the PutObjectRequest and RequestBody (or use
argument matchers) and verify the request's bucket name, key/path contains the
"images" prefix, and the PutObjectRequest's metadata/content-type equals the
file's MIME type; also assert the RequestBody/byte content length equals the
uploaded byte length. Add tests calling storageService.uploadImage for oversized
input (e.g., 11MB) and empty/null payloads asserting ApiException is thrown (use
assertThrows(ApiException.class, ...)) and ensure the existing positive test
still verifies URL format and that putObject was invoked with the correct
captured values.
| @Test | ||
| void 이미지가_아닌_파일은_업로드하지_않는다() { | ||
| MockMultipartFile file = new MockMultipartFile( | ||
| "file", | ||
| "sample.txt", | ||
| "text/plain", | ||
| "text".getBytes() | ||
| ); | ||
|
|
||
| assertThrows(ApiException.class, () -> storageService.uploadImage(file, "images")); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift
예외 검증이 불충분하고 예외 매핑 테스트가 누락되었습니다.
현재 테스트는 ApiException이 발생하는지만 확인하고, 예외의 상세 정보(에러 코드, 메시지)나 S3 클라이언트가 호출되지 않았는지 검증하지 않습니다. 컨텍스트에 따르면 서비스는 IOException → BAD_REQUEST, S3Exception → BAD_GATEWAY로 매핑해야 하지만 이에 대한 테스트가 없습니다.
다음 개선사항을 권장합니다:
ApiException의 에러 코드 및 메시지 검증- 검증 실패 시 S3 호출이 발생하지 않았는지 확인
S3Exception발생 시BAD_GATEWAY매핑 테스트 추가IOException발생 시BAD_REQUEST매핑 테스트 추가
🧪 예외 검증 강화 및 추가 테스트 예시
`@Test`
void 이미지가_아닌_파일은_업로드하지_않는다() {
MockMultipartFile file = new MockMultipartFile(
"file",
"sample.txt",
"text/plain",
"text".getBytes()
);
- assertThrows(ApiException.class, () -> storageService.uploadImage(file, "images"));
+ ApiException exception = assertThrows(ApiException.class,
+ () -> storageService.uploadImage(file, "images"));
+
+ assertThat(exception.getErrorCode()).isEqualTo(ErrorCode.BAD_REQUEST);
+ verify(s3Client, never()).putObject(any(PutObjectRequest.class), any(RequestBody.class));
}추가 테스트 케이스:
`@Test`
void S3_업로드_실패_시_BAD_GATEWAY_예외가_발생한다() {
MockMultipartFile file = new MockMultipartFile(
"file",
"sample.png",
"image/png",
"image".getBytes()
);
when(s3Client.putObject(any(PutObjectRequest.class), any(RequestBody.class)))
.thenThrow(S3Exception.builder().message("S3 error").build());
ApiException exception = assertThrows(ApiException.class,
() -> storageService.uploadImage(file, "images"));
assertThat(exception.getErrorCode()).isEqualTo(ErrorCode.BAD_GATEWAY);
}
`@Test`
void IOException_발생_시_BAD_REQUEST_예외가_발생한다() throws Exception {
MultipartFile file = mock(MultipartFile.class);
when(file.getContentType()).thenReturn("image/png");
when(file.getOriginalFilename()).thenReturn("sample.png");
when(file.getSize()).thenReturn(100L);
when(file.getBytes()).thenThrow(new IOException("Read error"));
ApiException exception = assertThrows(ApiException.class,
() -> storageService.uploadImage(file, "images"));
assertThat(exception.getErrorCode()).isEqualTo(ErrorCode.BAD_REQUEST);
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/test/java/com/valanse/valanse/service/StorageService/R2StorageServiceTest.java`
around lines 50 - 60, Update the tests for storageService.uploadImage to assert
detailed ApiException properties and S3 interactions: in the non-image test
assert the thrown ApiException's ErrorCode and message, and verify the mocked
s3Client.putObject (or equivalent) was never invoked; add a test that stubs
s3Client.putObject to throw S3Exception and assert the service maps it to
ErrorCode.BAD_GATEWAY; add a test that stubs MultipartFile.getBytes() to throw
IOException and assert the service maps it to ErrorCode.BAD_REQUEST (use
ApiException exception = assertThrows(...) and
assertThat(exception.getErrorCode()).isEqualTo(...), and use verify(s3Client,
never()).putObject(...) to confirm no S3 call on validation failure).
Summary by CodeRabbit
릴리스 노트
New Features
Tests