forked from baron/baron-sso
로컬/CI 테스트 동기화
This commit is contained in:
@@ -95,11 +95,12 @@ jobs:
|
||||
npx biome check . --linter-enabled=false --organize-imports-enabled=false
|
||||
|
||||
- name: Lint Go backend
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: v1.59
|
||||
working-directory: backend
|
||||
args: --enable-only=gofmt,gofumpt
|
||||
run: |
|
||||
docker run --rm \
|
||||
-v "${PWD}/backend:/app" \
|
||||
-w /app \
|
||||
golangci/golangci-lint:v2.10.1 \
|
||||
golangci-lint fmt -E gofmt -E gofumpt -d
|
||||
|
||||
- name: Sync userfront locales
|
||||
run: |
|
||||
|
||||
143
Makefile
143
Makefile
@@ -107,11 +107,30 @@ logs-app:
|
||||
docker compose -f $(COMPOSE_APP) logs -f
|
||||
|
||||
# --- 로컬 통합 코드 체크 ---
|
||||
.PHONY: code-check code-check-i18n code-check-go-lint code-check-userfront-lint code-check-front-lint code-check-backend-tests code-check-userfront-tests code-check-adminfront-tests code-check-devfront-tests code-check-userfront-e2e-tests
|
||||
ifeq ($(CI),)
|
||||
PLAYWRIGHT_INSTALL_ALL := npx playwright install
|
||||
PLAYWRIGHT_INSTALL_CHROMIUM := npx playwright install chromium
|
||||
else
|
||||
PLAYWRIGHT_INSTALL_ALL := npx playwright install --with-deps
|
||||
PLAYWRIGHT_INSTALL_CHROMIUM := npx playwright install --with-deps chromium
|
||||
endif
|
||||
|
||||
code-check: code-check-i18n code-check-go-lint code-check-userfront-lint code-check-front-lint code-check-backend-tests code-check-userfront-tests code-check-adminfront-tests code-check-devfront-tests code-check-userfront-e2e-tests
|
||||
.PHONY: code-check code-check-lint code-check-test-jobs code-check-i18n code-check-go-lint code-check-sync-userfront-locales code-check-userfront-install code-check-userfront-lint code-check-front-lint code-check-backend-tests code-check-userfront-tests code-check-adminfront-tests code-check-devfront-tests code-check-userfront-e2e-tests
|
||||
|
||||
code-check: code-check-lint code-check-test-jobs
|
||||
@echo "code-check complete."
|
||||
|
||||
code-check-lint: code-check-i18n code-check-front-lint code-check-go-lint code-check-sync-userfront-locales code-check-userfront-install code-check-userfront-lint
|
||||
|
||||
code-check-test-jobs:
|
||||
@echo "==> run CI-equivalent test jobs (parallel)"
|
||||
@$(MAKE) --no-print-directory -j5 --output-sync=target \
|
||||
code-check-backend-tests \
|
||||
code-check-userfront-tests \
|
||||
code-check-userfront-e2e-tests \
|
||||
code-check-adminfront-tests \
|
||||
code-check-devfront-tests
|
||||
|
||||
code-check-i18n:
|
||||
@echo "==> i18n resource check"
|
||||
@mkdir -p reports
|
||||
@@ -122,60 +141,83 @@ code-check-i18n:
|
||||
code-check-go-lint:
|
||||
@echo "==> go lint/format check"
|
||||
@if command -v golangci-lint >/dev/null 2>&1; then \
|
||||
cd backend && golangci-lint run --enable-only=gofmt,gofumpt; \
|
||||
cd backend && golangci-lint fmt -E gofmt -E gofumpt -d; \
|
||||
elif command -v docker >/dev/null 2>&1; then \
|
||||
docker run --rm \
|
||||
-v "$$(pwd)/backend:/app" \
|
||||
-w /app \
|
||||
golangci/golangci-lint:v2.10.1 \
|
||||
golangci-lint fmt -E gofmt -E gofumpt -d; \
|
||||
else \
|
||||
echo "WARN: golangci-lint not found, fallback to gofmt check only."; \
|
||||
unformatted="$$(cd backend && gofmt -l .)"; \
|
||||
if [ -n "$$unformatted" ]; then \
|
||||
echo "gofmt required:"; \
|
||||
echo "$$unformatted"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
echo "ERROR: golangci-lint not found and docker is unavailable."; \
|
||||
echo "Install golangci-lint v2.10.1 or Docker to match CI lint step."; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
code-check-sync-userfront-locales:
|
||||
@echo "==> sync userfront locales"
|
||||
/bin/sh ./scripts/sync_userfront_locales.sh
|
||||
|
||||
code-check-userfront-install:
|
||||
@echo "==> install userfront dependencies"
|
||||
cd userfront && flutter pub get
|
||||
|
||||
code-check-userfront-lint:
|
||||
@echo "==> userfront format/analyze"
|
||||
cd userfront && flutter pub get
|
||||
cd userfront && dart format --output=show --set-exit-if-changed lib test
|
||||
cd userfront && dart format --output=none --set-exit-if-changed lib test
|
||||
cd userfront && flutter analyze --no-fatal-warnings --no-fatal-infos
|
||||
|
||||
code-check-front-lint:
|
||||
@echo "==> adminfront biome lint/format check"
|
||||
rm -rf adminfront/playwright-report adminfront/test-results
|
||||
cd adminfront && npm ci
|
||||
cd adminfront && npx biome check src tests playwright.config.ts --formatter-enabled=false --organize-imports-enabled=false
|
||||
cd adminfront && npx biome check src tests playwright.config.ts --linter-enabled=false --organize-imports-enabled=false
|
||||
cd adminfront && npx biome check . --formatter-enabled=false --organize-imports-enabled=false
|
||||
cd adminfront && npx biome check . --linter-enabled=false --organize-imports-enabled=false
|
||||
@echo "==> devfront biome lint/format check"
|
||||
rm -rf devfront/playwright-report devfront/test-results
|
||||
cd devfront && npm ci
|
||||
cd devfront && npx biome check src tests playwright.config.ts --formatter-enabled=false --organize-imports-enabled=false
|
||||
cd devfront && npx biome check src tests playwright.config.ts --linter-enabled=false --organize-imports-enabled=false
|
||||
cd devfront && npx biome check . --formatter-enabled=false --organize-imports-enabled=false
|
||||
cd devfront && npx biome check . --linter-enabled=false --organize-imports-enabled=false
|
||||
|
||||
code-check-backend-tests:
|
||||
@echo "==> backend tests"
|
||||
cd backend && go test -v ./...
|
||||
|
||||
code-check-userfront-tests:
|
||||
@echo "==> userfront tests"
|
||||
cd userfront && flutter test
|
||||
@echo "==> userfront tests (isolated workspace)"
|
||||
@tmp_dir="$$(mktemp -d /tmp/baron-sso-userfront-tests.XXXXXX)"; \
|
||||
trap 'rm -rf "$$tmp_dir"' EXIT INT TERM; \
|
||||
mkdir -p "$$tmp_dir/scripts"; \
|
||||
cp scripts/sync_userfront_locales.sh "$$tmp_dir/scripts/"; \
|
||||
cp -R locales "$$tmp_dir/locales"; \
|
||||
if command -v rsync >/dev/null 2>&1; then \
|
||||
rsync -a --delete \
|
||||
--exclude '.dart_tool' \
|
||||
--exclude 'build' \
|
||||
--exclude '.pub-cache' \
|
||||
--exclude '.flutter-plugins' \
|
||||
--exclude '.flutter-plugins-dependencies' \
|
||||
userfront/ "$$tmp_dir/userfront/"; \
|
||||
else \
|
||||
cp -R userfront "$$tmp_dir/userfront"; \
|
||||
rm -rf "$$tmp_dir/userfront/.dart_tool" "$$tmp_dir/userfront/build"; \
|
||||
fi; \
|
||||
cd "$$tmp_dir" && /bin/sh ./scripts/sync_userfront_locales.sh; \
|
||||
cd "$$tmp_dir/userfront" && flutter test
|
||||
|
||||
code-check-adminfront-tests:
|
||||
@echo "==> adminfront tests"
|
||||
@mkdir -p reports/adminfront
|
||||
@rm -rf reports/adminfront/playwright-report reports/adminfront/test-results
|
||||
@status=0; \
|
||||
(cd adminfront && npx playwright install) || status=$$?; \
|
||||
if [ $$status -eq 0 ]; then \
|
||||
(cd adminfront && npm test) || status=$$?; \
|
||||
fi; \
|
||||
[ -d adminfront/playwright-report ] && cp -R adminfront/playwright-report reports/adminfront/ || true; \
|
||||
[ -d adminfront/test-results ] && cp -R adminfront/test-results reports/adminfront/ || true; \
|
||||
exit $$status
|
||||
./scripts/run_adminfront_ci_tests.sh adminfront-tests
|
||||
|
||||
code-check-devfront-tests:
|
||||
@echo "==> devfront tests"
|
||||
@mkdir -p reports/devfront
|
||||
@rm -rf reports/devfront/playwright-report reports/devfront/test-results
|
||||
@status=0; \
|
||||
(cd devfront && npx playwright install) || status=$$?; \
|
||||
(cd devfront && npm ci) || status=$$?; \
|
||||
if [ $$status -eq 0 ]; then \
|
||||
(cd devfront && $(PLAYWRIGHT_INSTALL_ALL)) || status=$$?; \
|
||||
fi; \
|
||||
if [ $$status -eq 0 ]; then \
|
||||
(cd devfront && npm test) || status=$$?; \
|
||||
fi; \
|
||||
@@ -184,20 +226,47 @@ code-check-devfront-tests:
|
||||
exit $$status
|
||||
|
||||
code-check-userfront-e2e-tests:
|
||||
@echo "==> userfront wasm playwright e2e tests"
|
||||
@echo "==> userfront wasm playwright e2e tests (isolated workspace)"
|
||||
@mkdir -p reports/userfront-e2e
|
||||
@rm -rf reports/userfront-e2e/playwright-report reports/userfront-e2e/test-results
|
||||
@status=0; \
|
||||
(cd userfront && flutter build web --wasm --release) || status=$$?; \
|
||||
@tmp_dir="$$(mktemp -d /tmp/baron-sso-userfront-e2e-tests.XXXXXX)"; \
|
||||
trap 'rm -rf "$$tmp_dir"' EXIT INT TERM; \
|
||||
mkdir -p "$$tmp_dir/scripts"; \
|
||||
cp scripts/sync_userfront_locales.sh "$$tmp_dir/scripts/"; \
|
||||
cp -R locales "$$tmp_dir/locales"; \
|
||||
if command -v rsync >/dev/null 2>&1; then \
|
||||
rsync -a --delete \
|
||||
--exclude '.dart_tool' \
|
||||
--exclude 'build' \
|
||||
--exclude '.pub-cache' \
|
||||
--exclude '.flutter-plugins' \
|
||||
--exclude '.flutter-plugins-dependencies' \
|
||||
userfront/ "$$tmp_dir/userfront/"; \
|
||||
rsync -a --delete \
|
||||
--exclude 'node_modules' \
|
||||
--exclude 'playwright-report' \
|
||||
--exclude 'test-results' \
|
||||
userfront-e2e/ "$$tmp_dir/userfront-e2e/"; \
|
||||
else \
|
||||
cp -R userfront "$$tmp_dir/userfront"; \
|
||||
rm -rf "$$tmp_dir/userfront/.dart_tool" "$$tmp_dir/userfront/build"; \
|
||||
cp -R userfront-e2e "$$tmp_dir/userfront-e2e"; \
|
||||
rm -rf "$$tmp_dir/userfront-e2e/node_modules" "$$tmp_dir/userfront-e2e/playwright-report" "$$tmp_dir/userfront-e2e/test-results"; \
|
||||
fi; \
|
||||
status=0; \
|
||||
(cd "$$tmp_dir" && /bin/sh ./scripts/sync_userfront_locales.sh) || status=$$?; \
|
||||
if [ $$status -eq 0 ]; then \
|
||||
(cd userfront-e2e && npm ci) || status=$$?; \
|
||||
(cd "$$tmp_dir/userfront-e2e" && npm ci) || status=$$?; \
|
||||
fi; \
|
||||
if [ $$status -eq 0 ]; then \
|
||||
(cd userfront-e2e && npx playwright install --with-deps chromium) || status=$$?; \
|
||||
(cd "$$tmp_dir/userfront" && flutter build web --wasm --release) || status=$$?; \
|
||||
fi; \
|
||||
if [ $$status -eq 0 ]; then \
|
||||
(cd userfront-e2e && npm test) || status=$$?; \
|
||||
(cd "$$tmp_dir/userfront-e2e" && $(PLAYWRIGHT_INSTALL_CHROMIUM)) || status=$$?; \
|
||||
fi; \
|
||||
[ -d userfront-e2e/playwright-report ] && cp -R userfront-e2e/playwright-report reports/userfront-e2e/ || true; \
|
||||
[ -d userfront-e2e/test-results ] && cp -R userfront-e2e/test-results reports/userfront-e2e/ || true; \
|
||||
if [ $$status -eq 0 ]; then \
|
||||
(cd "$$tmp_dir/userfront-e2e" && npm test) || status=$$?; \
|
||||
fi; \
|
||||
[ -d "$$tmp_dir/userfront-e2e/playwright-report" ] && cp -R "$$tmp_dir/userfront-e2e/playwright-report" reports/userfront-e2e/ || true; \
|
||||
[ -d "$$tmp_dir/userfront-e2e/test-results" ] && cp -R "$$tmp_dir/userfront-e2e/test-results" reports/userfront-e2e/ || true; \
|
||||
exit $$status
|
||||
|
||||
@@ -82,4 +82,4 @@ Error generating stack: `+a.message+`
|
||||
<div id='root'></div>
|
||||
</body>
|
||||
</html>
|
||||
<script id="playwrightReportBase64" type="application/zip">data:application/zip;base64,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</script>
|
||||
<script id="playwrightReportBase64" type="application/zip">data:application/zip;base64,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</script>
|
||||
@@ -66,7 +66,17 @@ cd userfront
|
||||
flutter test test/error_screen_test.dart
|
||||
```
|
||||
|
||||
## 6. 관련 이슈
|
||||
## 6. Backend `code` 주입/매핑 경로
|
||||
- 기본 매핑 함수: `backend/internal/response/error_response.go`
|
||||
- `404 -> not_found`
|
||||
- `429 -> rate_limited`
|
||||
- legacy 응답 보강 미들웨어: `backend/internal/middleware/error_code_enricher.go`
|
||||
- 핸들러가 `{"error": ...}`만 반환해도 status 기반 `code`를 주입
|
||||
- 신규 권장 패턴: `response.Error(...)` 또는 공통 helper(`errorJSON`, `errorJSONCode`)로 핸들러에서 명시 코드 반환
|
||||
|
||||
UserFront는 위 경로로 전달된 `code`를 기준으로 whitelist/ory/unknown 분기를 수행합니다.
|
||||
|
||||
## 7. 관련 이슈
|
||||
- `#164` `[UserFront] 에러 노출 whitelist 정의 및 적용`
|
||||
- `#259` `백엔드 i18n/에러 메시지 fallback 정책 재정리 및 반영 계획 수립`
|
||||
- `#260` `[Backend] 에러 응답 code 통일 구현 계획 (phase rollout)`
|
||||
|
||||
@@ -5,6 +5,14 @@ job_name="${1:-adminfront-tests}"
|
||||
|
||||
mkdir -p reports
|
||||
|
||||
if [ -n "${CI:-}" ]; then
|
||||
playwright_install_cmd=(npx playwright install --with-deps)
|
||||
playwright_install_desc="npx playwright install --with-deps"
|
||||
else
|
||||
playwright_install_cmd=(npx playwright install)
|
||||
playwright_install_desc="npx playwright install"
|
||||
fi
|
||||
|
||||
set +e
|
||||
(
|
||||
cd adminfront
|
||||
@@ -36,7 +44,7 @@ fi
|
||||
set +e
|
||||
(
|
||||
cd adminfront
|
||||
npx playwright install --with-deps
|
||||
"${playwright_install_cmd[@]}"
|
||||
) 2>&1 | tee reports/adminfront-provision.log
|
||||
provision_exit_code=${PIPESTATUS[0]}
|
||||
set -e
|
||||
@@ -51,7 +59,7 @@ if [ "$provision_exit_code" -ne 0 ]; then
|
||||
echo "- Exit Code: \`$provision_exit_code\`"
|
||||
echo
|
||||
echo "## Command"
|
||||
echo "\`cd adminfront && npx playwright install --with-deps\`"
|
||||
echo "\`cd adminfront && ${playwright_install_desc}\`"
|
||||
echo
|
||||
echo "## Provision Log Tail (last 200 lines)"
|
||||
echo '```text'
|
||||
@@ -80,7 +88,7 @@ if [ "$test_exit_code" -ne 0 ]; then
|
||||
echo "## Commands"
|
||||
echo "1. \`cd adminfront\`"
|
||||
echo "2. \`npm ci\`"
|
||||
echo "3. \`npx playwright install --with-deps\`"
|
||||
echo "3. \`${playwright_install_desc}\`"
|
||||
echo "4. \`npm test\`"
|
||||
echo
|
||||
echo "## Log Tail (last 200 lines)"
|
||||
|
||||
@@ -75,8 +75,18 @@ async function mockUserfrontApis(
|
||||
}
|
||||
|
||||
if (path.endsWith('/api/v1/auth/qr/approve')) {
|
||||
const body = route.request().postDataJSON() as { pendingRef?: string };
|
||||
options.captureApprove?.(body.pendingRef ?? null);
|
||||
if (route.request().method() == 'POST') {
|
||||
let pendingRef: string | null = null;
|
||||
try {
|
||||
const body = (route.request().postDataJSON() ?? {}) as {
|
||||
pendingRef?: string;
|
||||
};
|
||||
pendingRef = body.pendingRef ?? null;
|
||||
} catch (_) {
|
||||
pendingRef = null;
|
||||
}
|
||||
options.captureApprove?.(pendingRef);
|
||||
}
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
@@ -137,7 +147,9 @@ test.describe('UserFront WASM auth routing', () => {
|
||||
|
||||
await page.goto('/ko/approve?ref=e2e-approve-ref');
|
||||
|
||||
await expect(page).toHaveURL(/\/ko\/dashboard$/);
|
||||
await expect(page).toHaveURL(/\/ko\/dashboard(?:\?.*)?$/, {
|
||||
timeout: 10_000,
|
||||
});
|
||||
expect(approvedRef).toBe('e2e-approve-ref');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ const SIGNIN_SUBMIT_X = 640;
|
||||
const SIGNIN_SUBMIT_Y = 381;
|
||||
|
||||
const RESET_NEW_PASSWORD_X = 640;
|
||||
const RESET_NEW_PASSWORD_Y = 401;
|
||||
const RESET_NEW_PASSWORD_Y = 382;
|
||||
const RESET_CONFIRM_PASSWORD_X = 640;
|
||||
const RESET_CONFIRM_PASSWORD_Y = 464;
|
||||
const RESET_SUBMIT_X = 640;
|
||||
@@ -236,7 +236,13 @@ test.describe('UserFront WASM password login and reset', () => {
|
||||
const capture: RequestCapture = { clientLogs: [] };
|
||||
await mockAuthApis(page, capture);
|
||||
|
||||
const policyLoaded = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/api/v1/auth/password/policy') &&
|
||||
response.status() === 200,
|
||||
);
|
||||
await page.goto('/ko/reset-password?token=reset-token-e2e');
|
||||
await policyLoaded;
|
||||
await page.waitForTimeout(900);
|
||||
await fillAt(page, RESET_NEW_PASSWORD_X, RESET_NEW_PASSWORD_Y, 'ValidPass1!A');
|
||||
await fillAt(
|
||||
@@ -250,7 +256,10 @@ test.describe('UserFront WASM password login and reset', () => {
|
||||
force: true,
|
||||
});
|
||||
|
||||
await expect(page).toHaveURL(/\/ko\/signin$/);
|
||||
await expect
|
||||
.poll(() => capture.resetBody?.newPassword as string | undefined)
|
||||
.toBe('ValidPass1!A');
|
||||
await expect(page).toHaveURL(/\/ko\/signin(?:\?.*)?$/, { timeout: 10_000 });
|
||||
expect(capture.resetToken).toBe('reset-token-e2e');
|
||||
expect(capture.resetBody?.newPassword).toBe('ValidPass1!A');
|
||||
});
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -80,6 +80,7 @@ title_with_code = ""
|
||||
type = ""
|
||||
|
||||
[msg.userfront.error.whitelist]
|
||||
"$normalizedCode" = ""
|
||||
settings_disabled = ""
|
||||
invalid_session = ""
|
||||
verification_required = ""
|
||||
@@ -91,6 +92,7 @@ bad_request = ""
|
||||
password_or_email_mismatch = ""
|
||||
|
||||
[msg.userfront.error.ory]
|
||||
"$normalizedCode" = ""
|
||||
access_denied = ""
|
||||
consent_required = ""
|
||||
interaction_required = ""
|
||||
@@ -558,3 +560,13 @@ verify = ""
|
||||
|
||||
[ui.userfront.signup.success]
|
||||
action = ""
|
||||
|
||||
|
||||
# Auto-added missing keys
|
||||
|
||||
[ui.common]
|
||||
admin_only = ""
|
||||
assign = ""
|
||||
none = ""
|
||||
select = ""
|
||||
select_placeholder = ""
|
||||
|
||||
@@ -143,4 +143,54 @@ void main() {
|
||||
expect(find.text('원문 메시지'), findsNothing);
|
||||
expect(find.text(type), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('프로덕션은 not_found 코드를 whitelist 메시지로 노출한다 (404 매핑)', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
await _pumpErrorScreen(
|
||||
tester,
|
||||
errorCode: 'not_found',
|
||||
description: '원문 메시지',
|
||||
isProdOverride: true,
|
||||
);
|
||||
|
||||
final detail = tr(
|
||||
'msg.userfront.error.whitelist.not_found',
|
||||
fallback: internalErrorWhitelistMessages['not_found']!,
|
||||
);
|
||||
final type = tr(
|
||||
'msg.userfront.error.type',
|
||||
fallback: '오류 종류: {{type}}',
|
||||
params: {'type': 'not_found'},
|
||||
);
|
||||
|
||||
expect(find.text(detail), findsOneWidget);
|
||||
expect(find.text(type), findsOneWidget);
|
||||
expect(find.text('원문 메시지'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('프로덕션은 rate_limited 코드를 whitelist 메시지로 노출한다 (429 매핑)', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
await _pumpErrorScreen(
|
||||
tester,
|
||||
errorCode: 'rate_limited',
|
||||
description: '원문 메시지',
|
||||
isProdOverride: true,
|
||||
);
|
||||
|
||||
final detail = tr(
|
||||
'msg.userfront.error.whitelist.rate_limited',
|
||||
fallback: internalErrorWhitelistMessages['rate_limited']!,
|
||||
);
|
||||
final type = tr(
|
||||
'msg.userfront.error.type',
|
||||
fallback: '오류 종류: {{type}}',
|
||||
params: {'type': 'rate_limited'},
|
||||
);
|
||||
|
||||
expect(find.text(detail), findsOneWidget);
|
||||
expect(find.text(type), findsOneWidget);
|
||||
expect(find.text('원문 메시지'), findsNothing);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user