1
0
forked from baron/baron-sso

로컬/CI 테스트 동기화

This commit is contained in:
Lectom C Han
2026-02-24 16:42:55 +09:00
parent 8a074f16e7
commit 7fee9c597a
11 changed files with 277 additions and 92 deletions

View File

@@ -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
View File

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

View File

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

View File

@@ -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)`

View File

@@ -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)"

View File

@@ -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');
});
});

View File

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

View File

@@ -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 = ""

View File

@@ -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);
});
}