Skip to content

test: add k6 load/stress/spike/soak scripts for AI problem generation… - #48

Merged
idktomorrow merged 1 commit into
developfrom
feature/problem-refactor
Jul 29, 2026
Merged

idktomorrow merged 1 commit into
developfrom
feature/problem-refactor

Conversation

@Junkov0

@Junkov0 Junkov0 commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

목적

  • AI 문제 생성 API(POST /api/spaces/{spaceId}/problems/ai)는 동기 LLM 호출 구조(캐시/큐 없음)라
  • room-submit 대비 훨씬 낮은 VU에서도 병목이 발생할 수 있어, 배포 서버(AWS ALB) 기준 baseline 성능/한계점을 확인하고자 함.
  • LLM 실호출 비용이 발생하는 API 특성상 VU는 room-submit보다 대폭 낮게 설계함.

테스트 구성 (k6/problem-ai)

시나리오 목적 VU / 시간 임계값
load-test 평시 목표 부하 baseline 3 VU, 4분 p95<15s, error<5%
stress-test 한계점(Break Point) 탐색 2→12 VU 계단식, 6.5분 (검증 executor pool max=10 포함) error<30%
spike-test 순간 폭증/복구력 1→10→1 VU 급변, 1.7분 error<30%
soak-test 장시간 리소스 누수 확인 2 VU, 10분 지속 p95<15s, error<

결과 요약

  • load: 79 req, p95 27.7s / avg 5.3s, 에러율 ~5.1% (4/78) → 평시 3 VU 수준에서도 p95가 15s 기준을 이미 초과
  • stress: 164 req, p95 42.6s / avg 12.4s, 에러율 ~27% (44/163) → VU10(검증 스레드풀 max) 부근부터 응답 급격히 무너짐
  • spike: 31 req, p95 60s(타임아웃 상한 도달), 에러율 ~9.7% (3/31) → 폭증 후 VU1 복귀 구간에서 정상 201 복구는 확인
  • soak: 119 req, p95 38.8s / avg 6.4s, 에러율 ~3.4% (4/118) → 10분 지속에도 에러율 증가 추세

체크리스트

  • 테스트 코드 작성 완료
  • 리뷰어 지정 완료

참고 사항

관련 이슈

Summary by CodeRabbit

  • 새 기능

    • AI 문제 자동 생성 API에 대한 부하, 스트레스, 스파이크 및 장시간 안정성 테스트를 추가했습니다.
    • 테스트 실행 시 인증과 CSRF 보호를 자동으로 처리합니다.
    • 응답 성공률, 오류율, 처리 시간 및 동시 사용자 지표를 확인할 수 있습니다.
  • 테스트 결과

    • 각 부하 테스트의 실행 결과와 주요 성능 지표를 JSON 형식으로 제공합니다.
    • AI 문제 생성 요청의 성공 및 실패 현황을 확인할 수 있습니다.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Problem-AI API 부하 검증을 위한 공통 k6 설정과 인증·페이로드 헬퍼를 추가하고, 부하·소크·스파이크·스트레스 시나리오 및 각 실행 결과 JSON을 구성합니다.

Changes

Problem-AI 성능 테스트

Layer / File(s) Summary
공통 인증 및 페이로드 구성
k6/problem-ai/config.js
서버·계정·공간·카테고리 상수와 로그인, CSRF 쿠키 조회, AI 문제 생성 페이로드 함수를 추가합니다.
부하 테스트 실행 및 결과
k6/problem-ai/load-test.js, k6/problem-ai/load-result.json
최대 3 VU 부하 단계에서 AI 문제 생성 요청을 수행하고, 상태·오류율·지연시간과 실행 결과를 기록합니다.
소크 테스트 실행 및 결과
k6/problem-ai/soak-test.js, k6/problem-ai/soak-result.json
2 VU로 10분간 요청을 반복하며 오류율과 생성 지연시간을 측정하고 결과를 저장합니다.
스파이크·스트레스 테스트 실행 및 결과
k6/problem-ai/spike-test.js, k6/problem-ai/spike-result.json, k6/problem-ai/stress-test.js, k6/problem-ai/stress-result.json
VU 급증 및 단계적 증가 조건에서 인증된 AI 문제 생성 요청을 실행하고 체크·메트릭 결과를 저장합니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

공통 테스트 실행 흐름

sequenceDiagram
  participant k6Setup
  participant AuthEndpoint
  participant ProblemAiApi
  participant Metrics
  k6Setup->>AuthEndpoint: 로그인 및 accessToken 획득
  k6Setup->>AuthEndpoint: XSRF-TOKEN 쿠키 조회
  k6Setup-->>Metrics: authToken, csrfToken 전달
  Metrics->>ProblemAiApi: 인증·CSRF 헤더와 페이로드로 POST
  ProblemAiApi-->>Metrics: 201 응답 및 처리시간 기록
Loading

Possibly related PRs

Suggested reviewers: jaejo, idktomorrow

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed k6로 AI 문제 생성 API의 load/stress/spike/soak 테스트 스크립트를 추가한 변경을 정확히 요약합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/problem-refactor

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
k6/problem-ai/spike-test.js (1)

21-31: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

[중요] 복구 구간의 성공률을 별도 지표로 측정하세요.

현재 error_rate와 체크는 스파이크 전·중·후 요청을 모두 합산합니다. 따라서 결과 JSON의 전체 성공률만으로는 마지막 30초에 실제로 복구되었는지 검증할 수 없습니다. 복구 구간 전용 Rate/Trend와 임계값을 추가해 배포 판단 근거를 분리하세요.

Also applies to: 60-65

🤖 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 `@k6/problem-ai/spike-test.js` around lines 21 - 31, Add a recovery-only Rate
or Trend metric alongside error_rate and record only requests from the final
30-second recovery stage, using the existing request/check flow rather than
aggregating all stages. Add a dedicated threshold for this metric in
options.thresholds so deployment validation separately enforces recovery
success, while preserving the existing overall error_rate threshold.
k6/problem-ai/load-test.js (1)

18-30: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

AI 문제 생성 지연시간 지표에 SLO 임계값을 함께 적용하세요.

thresholdshttp_req_duration는 k6의 표준 HTTP 요청 지표라 로그인(setup())까지 포함할 수 있으며, 현재 ai_generation_latency에 기준 값은 있지만 임계값이 없어 결과 판정에서 누락됩니다. ai_generation_latency: ['p(95)<15000']를 추가하고, 필요하면 기존 기준 유지 여부를 판단하세요.

  • k6/problem-ai/load-test.js#L18-L30: http_req_duration 추가 기준만 유지되도록 설정하거나 ai_generation_latency 기준으로 명확히 갱신하세요.
  • k6/problem-ai/soak-test.js#L19-L31: http_req_duration 추가 기준만 유지되도록 설정하거나 ai_generation_latency 기준으로 명확히 갱신하세요.
🤖 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 `@k6/problem-ai/load-test.js` around lines 18 - 30, The thresholds in
k6/problem-ai/load-test.js lines 18-30 omit the custom ai_generation_latency
SLO; add an ai_generation_latency p95 threshold of 15000 ms and retain or
replace http_req_duration consistently with the intended metric. Apply the same
threshold update in k6/problem-ai/soak-test.js lines 19-31, using the existing
aiLatency Trend metric.
🤖 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 `@k6/problem-ai/config.js`:
- Around line 17-23: Remove the hardcoded credentials from ADMIN_USER and source
both its email/password and BASE_URL exclusively from the corresponding __ENV
variables, without fallback defaults. Rotate or revoke the exposed password
separately, and preserve the existing exported configuration symbols for
consumers.
- Around line 50-57: Update setup() authentication failure handling around
isLoginSuccess and the CSRF acquisition flow: do not return the mock JWT or null
when initial authentication or CSRF retrieval fails; instead fail the load test
immediately using the existing setup error mechanism. Ensure requests proceed
only when both valid authentication and CSRF credentials are available.

In `@k6/problem-ai/load-result.json`:
- Around line 163-164: Remove the exposed setup_data.authToken and
setup_data.csrfToken fields from k6/problem-ai/load-result.json lines 163-164,
k6/problem-ai/soak-result.json lines 163-164, k6/problem-ai/spike-result.json
lines 129-130, and k6/problem-ai/stress-result.json lines 167-168; also revoke
the exposed tokens and associated sessions.

---

Nitpick comments:
In `@k6/problem-ai/load-test.js`:
- Around line 18-30: The thresholds in k6/problem-ai/load-test.js lines 18-30
omit the custom ai_generation_latency SLO; add an ai_generation_latency p95
threshold of 15000 ms and retain or replace http_req_duration consistently with
the intended metric. Apply the same threshold update in
k6/problem-ai/soak-test.js lines 19-31, using the existing aiLatency Trend
metric.

In `@k6/problem-ai/spike-test.js`:
- Around line 21-31: Add a recovery-only Rate or Trend metric alongside
error_rate and record only requests from the final 30-second recovery stage,
using the existing request/check flow rather than aggregating all stages. Add a
dedicated threshold for this metric in options.thresholds so deployment
validation separately enforces recovery success, while preserving the existing
overall error_rate threshold.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4c337003-6743-4b34-bd94-af44715f8911

📥 Commits

Reviewing files that changed from the base of the PR and between 1f198ae and 221db4c.

📒 Files selected for processing (9)
  • k6/problem-ai/config.js
  • k6/problem-ai/load-result.json
  • k6/problem-ai/load-test.js
  • k6/problem-ai/soak-result.json
  • k6/problem-ai/soak-test.js
  • k6/problem-ai/spike-result.json
  • k6/problem-ai/spike-test.js
  • k6/problem-ai/stress-result.json
  • k6/problem-ai/stress-test.js

Comment thread k6/problem-ai/config.js
Comment on lines +17 to +23
export const BASE_URL = 'http://momogo-alb-1906718718.ap-northeast-2.elb.amazonaws.com';

// 2. [테스트 계정] ADMIN 권한 + 공간(Space) 소유 계정
export const ADMIN_USER = {
email: 'jun@test.com',
password: '7964a23!', // TODO: 실제 비밀번호로 교체해서 로컬에서만 사용 (커밋 금지)
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files matching config.js / problem-ai =="
git ls-files | rg '(^|/)config\.js$|problem-ai|^k6/' | sed -n '1,120p'

echo
echo "== k6/problem-ai/config.js =="
if [ -f k6/problem-ai/config.js ]; then
  nl -ba k6/problem-ai/config.js | sed -n '1,120p'
else
  echo "missing"
fi

echo
echo "== search for BASE_URL, ADMIN_USER, Problem-AI, bearer, Authorization headers =="
rg -n "BASE_URL|ADMIN_USER|Authorization|Bearer|PROBLEM_AI|momogo-alb|jun@test\.com|7964a23!" . -S --glob '!node_modules' --glob '!dist' --glob '!build' | sed -n '1,200p'

Repository: MoMoGo-QuizPlatform/MoMoGo

Length of output: 742


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== git diff status/stat =="
git diff --stat || true

echo
echo "== focused file inspection with ast/rg if available =="
if command -v ast-grep >/dev/null 2>&1; then
  ast-grep outline k6/problem-ai/config.js --view expanded || true
fi

echo
echo "== imports/usages from config and k6 =="
for f in $(git ls-files 'k6/problem-ai/*.js' 'k6/problem-ai/**/*.js' | sed -n '1,100p'); do
  echo "--- $f"
  nl -ba "$f" | sed -n '1,160p' | rg -n "BASE_URL|ADMIN_USER|problem-ai/config|loadCookie|Bearer|Authorization|http request|check\\(|open|post\\(|get\\(|setHeaders|__ENV" -C 2 || true
done

Repository: MoMoGo-QuizPlatform/MoMoGo

Length of output: 1057


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
for p in [Path("k6/problem-ai/config.js"), Path("k6/problem-ai/load-test.js"), Path("k6/problem-ai/soak-test.js"), Path("k6/problem-ai/spike-test.js"), Path("k6/problem-ai/stress-test.js")]:
    if not p.exists():
        continue
    lines = p.read_text().splitlines()
    print(f"--- {p} ({len(lines)} lines)")
    for i, line in enumerate(lines, 1):
        if any(k in line for k in ("BASE_URL", "ADMIN_USER", "obtainAuthToken", "obtainCsrfToken", "Authorization", "Bearer", "Authorization:", "k6/http", "check(","login", "csrf")):
            start=max(1,i-3); end=min(len(lines),i+8)
            for j in range(start,end+1):
                print(f"{j:4}: {lines[j-1]}")
            print()
PY

echo "== search for hardcoded credential patterns =="
rg -n "7964a23!|jun@test\.com|Authorization:\s*['\"']Bearer|Bearer|Authorization: Bearer|Bearer|PROBLEM_AI|BASE_URL|momogo-alb" . -S --glob '!node_modules' --glob '!dist' --glob '!build' || true

Repository: MoMoGo-QuizPlatform/MoMoGo

Length of output: 34324


기본값 없는 비밀값 구성만 요청합니다.

k6/problem-ai/config.js의 테스트 계정 비밀번호가 저장소에 직접 들어갔습니다. 커밋이 완료된 상태이므로 이 암호는 폐기/교체해 주세요. 동시에 BASE_URLADMIN_USER도 기본값 없이 __ENV.*에서 주입하도록 분리하는 것이 좋습니다.

🤖 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 `@k6/problem-ai/config.js` around lines 17 - 23, Remove the hardcoded
credentials from ADMIN_USER and source both its email/password and BASE_URL
exclusively from the corresponding __ENV variables, without fallback defaults.
Rotate or revoke the exposed password separately, and preserve the existing
exported configuration symbols for consumers.

Comment thread k6/problem-ai/config.js
Comment on lines +50 to +57
if (!isLoginSuccess) {
console.error(`[Setup 에러] 로그인 실패 (Status: ${loginRes.status}, Body: ${loginRes.body}).`);
console.error(`=> DB에 ${ADMIN_USER.email} 계정이 존재하는지, ADMIN 권한인지 확인해 주세요!`);
return 'mock-jwt-access-token-sample';
}

const token = loginRes.json('accessToken') || loginRes.headers['Authorization'] || 'mock-jwt-access-token-sample';
const cleanToken = token.replace('Bearer ', '');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files:"
git ls-files | rg '(^|/)k6/problem-ai/config\.js$|k6/problem-ai|config\.js' || true

echo
echo "Outline:"
ast-grep outline k6/problem-ai/config.js || true

echo
nl -ba k6/problem-ai/config.js | sed -n '1,130p'

Repository: MoMoGo-QuizPlatform/MoMoGo

Length of output: 957


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "config.js relevant sections:"
awk 'NR>=1 && NR<=110 {printf "%6d: %s\n", NR, $0}' k6/problem-ai/config.js

echo
echo "load-test.js relevant sections:"
awk 'NR>=1 && NR<=220 {printf "%6d: %s\n", NR, $0}' k6/problem-ai/load-test.js

echo
echo "Search for obtainAuthToken/obtainCsrfToken usages:"
rg -n "obtainAuthToken|obtainCsrfToken|csrf|accessToken|mock-jwt|CSRF" k6/problem-ai -S

Repository: MoMoGo-QuizPlatform/MoMoGo

Length of output: 11386


🌐 Web query:

k6 setup exception non-zero exit if exception thrown

💡 Result:

In k6, the behavior of an exception depends on where it occurs and how you handle it. By default, uncaught exceptions during the test execution phase (in the default function) abort the current iteration but do not necessarily terminate the entire test run with a non-zero exit code [1][2]. To ensure your test process exits with a non-zero code when an error occurs, you can use the following approaches: 1. Use exec.test.abort: If you want to stop the entire test execution immediately and signal a failure, use the k6/execution module [3]. import exec from 'k6/execution'; // When you encounter an error exec.test.abort('Aborting test due to critical error'); This will cause k6 to stop and exit with a non-zero status [4]. 2. Use Soft Assertions: If you are using the k6/testing library, soft assertions can mark a test as failed while allowing it to continue execution. If any soft assertions fail, k6 will exit with code 110 upon completion [5]. import { expect } from 'https://jslib.k6.io/k6-testing/0.6.1/index.js'; export default function { expect.soft(response.status).toBe(200); } 3. Exceptions in the Init Context: If an exception is thrown in the init context (outside the default function), k6 will immediately abort the entire test execution with a non-zero exit code [3]. 4. Understanding Exit Codes: k6 uses specific exit codes for different scenarios [4]. A ScriptException typically corresponds to code 107 [4]. If you are looking to distinguish between different failure types, note that these codes are internal to k6 and serve as the standard mechanism for reporting how the process finished [4]. Summary: - fail only aborts the current iteration [2]. - exec.test.abort terminates the entire test run [3]. - Soft assertions (from k6/testing) will cause an exit code of 110 [5]. - Uncaught exceptions generally do not guarantee a non-zero exit code for the entire test process, so explicit abortion is recommended for reliable CI/CD integration [6][7].

Citations:


[중요] 초기 인증 또는 CSRF 획득 실패 시 부하 테스트를 중단하세요.

setup()에서 로그인 실패면 mock-jwt-access-token-sample을, CSRF 쿠키가 없으면 null을 zurück주고 요청이 계속됩니다. main 요청에서는 401/403이 집계되어 성능 오류로 표시되고, LLM 지연 시간까지 부하 결과에 섞여 결과가 무효화될 수 있습니다. 인증 기준이 충족되지 않으면 실행을 실패 처리하세요.

🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 51-51: Avoid logging sensitive data
Context: console.error(=> DB에 ${ADMIN_USER.email} 계정이 존재하는지, ADMIN 권한인지 확인해 주세요!)
Note: [CWE-532] Insertion of Sensitive Information into Log File.

(log-sensitive-data)

🤖 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 `@k6/problem-ai/config.js` around lines 50 - 57, Update setup() authentication
failure handling around isLoginSuccess and the CSRF acquisition flow: do not
return the mock JWT or null when initial authentication or CSRF retrieval fails;
instead fail the load test immediately using the existing setup error mechanism.
Ensure requests proceed only when both valid authentication and CSRF credentials
are available.

Comment on lines +163 to +164
"authToken": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJqdW5AdGVzdC5jb20iLCJyb2xlcyI6WyJST0xFX0FETUlOIl0sIm5hbWUiOiJqdW4iLCJ1c2VyRW1haWwiOiJqdW5AdGVzdC5jb20iLCJ0eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzg1MjIxMTczLCJ1c2VySWQiOiJjYmE4YTFkZi1hZTYzLTRlYmMtYWI5Ni1kNjc1ZGQxMjljNjMiLCJpYXQiOjE3ODUyMTkzNzMsImp0aSI6IjIzODMwZDg0LWFkZmQtNDg2Ni1iNmZmLTIyNGM1OTYzOTU0MSJ9.AI070Smmkh9iEhirwSCgCyHs7vY6I6i6FKezWwE5RA8",
"csrfToken": "bb86b3a1-9911-4131-99f3-64c75d44f20b"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift

[차단] 실행 결과에 포함된 인증 정보를 즉시 제거하고 폐기하세요.

커밋된 결과 파일에 JWT와 CSRF 토큰이 그대로 포함되어 있습니다. 결과 저장 전 setup_data를 제거하고, 이미 노출된 토큰과 연계 세션은 폐기하세요.

  • k6/problem-ai/load-result.json#L163-L164: setup_data.authTokensetup_data.csrfToken을 제거하세요.
  • k6/problem-ai/soak-result.json#L163-L164: setup_data.authTokensetup_data.csrfToken을 제거하세요.
  • k6/problem-ai/spike-result.json#L129-L130: setup_data.authTokensetup_data.csrfToken을 제거하세요.
  • k6/problem-ai/stress-result.json#L167-L168: setup_data.authTokensetup_data.csrfToken을 제거하세요.
🧰 Tools
🪛 Betterleaks (1.7.0)

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

(jwt)

📍 Affects 4 files
  • k6/problem-ai/load-result.json#L163-L164 (this comment)
  • k6/problem-ai/soak-result.json#L163-L164
  • k6/problem-ai/spike-result.json#L129-L130
  • k6/problem-ai/stress-result.json#L167-L168
🤖 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 `@k6/problem-ai/load-result.json` around lines 163 - 164, Remove the exposed
setup_data.authToken and setup_data.csrfToken fields from
k6/problem-ai/load-result.json lines 163-164, k6/problem-ai/soak-result.json
lines 163-164, k6/problem-ai/spike-result.json lines 129-130, and
k6/problem-ai/stress-result.json lines 167-168; also revoke the exposed tokens
and associated sessions.

Source: Linters/SAST tools

@jaejo jaejo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

GOOD JOB

@idktomorrow idktomorrow left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

테스트 코드 작성하시느라 고생 많으셨습니다 ! ❤️

@idktomorrow
idktomorrow merged commit 2e37b58 into develop Jul 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants