Files
fanuzu/app/test/auth_error_message_test.dart
T

161 lines
6.9 KiB
Dart

import 'package:fancake/features/auth/auth.dart';
import 'package:fancake/features/auth/login_screen.dart' show authNotice;
import 'package:fancake/features/auth/social_providers.dart';
import 'package:firebase_auth/firebase_auth.dart' as fb;
import 'package:flutter_test/flutter_test.dart';
/// 인증 오류 문구가 **경로별로** 맞는 사실을 말하는지 고정한다.
///
/// `account-exists-with-different-credential` 은 로그인 경로와 계정 연결 경로에서
/// 서로 다른 사실을 뜻한다(별첨 B v1.4 「복수 소셜 Provider 연결 허용」).
/// 한 벌로 합쳐 두면 로그인에 실패한 사용자가 자기가 하지도 않은 계정 연결을
/// 설명받고 무엇을 해야 하는지 알 수 없으므로, 회귀 가드를 둔다.
void main() {
group('account-exists-with-different-credential — 경로별 문구', () {
test('로그인 경로는 「다른 수단으로 로그인한 뒤 연결」을 안내한다', () {
final message = authMessage(
_exception('account-exists-with-different-credential',
email: 'fan@gmail.com'),
flow: AuthFlow.signIn,
);
// 오류 응답의 이메일 힌트를 그대로 넣어야 어느 계정인지 알 수 있다.
// 이메일에 조사를 바로 붙이지 않는다 — 도메인 끝 글자에 따라 은/는 이 갈린다.
expect(message, startsWith('fan@gmail.com 계정은'));
expect(message, contains('처음 가입에 쓰신 수단으로 로그인'));
expect(message, contains('SNS 연동'));
// 로그인 경로에서는 「연결된 소셜 계정」이 사실이 아니다.
expect(message, isNot(contains('이미 연결된 소셜 계정')));
});
test('계정 연결 경로는 「다른 FANUZU 계정에 붙어 있다」로 남는다', () {
final message = authMessage(
_exception('account-exists-with-different-credential',
email: 'fan@gmail.com'),
flow: AuthFlow.link,
);
expect(message, '다른 FANUZU 계정에 이미 연결된 소셜 계정입니다.');
});
test('두 경로의 문구가 서로 다르다', () {
final error = _exception('account-exists-with-different-credential',
email: 'fan@gmail.com');
expect(
authMessage(error, flow: AuthFlow.signIn),
isNot(authMessage(error, flow: AuthFlow.link)),
);
});
test('이메일 힌트가 없으면 이메일 없이 같은 안내를 한다', () {
// Apple 은 최초 인가 때만 이메일을 준다 — 두 번째부터 힌트가 없다.
final message = authMessage(
_exception('account-exists-with-different-credential'),
flow: AuthFlow.signIn,
);
expect(message, startsWith('이 계정은'));
expect(message, contains('처음 가입에 쓰신 수단으로 로그인'));
expect(message, isNot(contains('null')));
});
test('에러 카탈로그 ID 는 경로와 무관하게 E-AUTH-08 이다', () {
// 문구만 갈랐다. 카탈로그를 손대면 Admin·QA 의 코드 대조가 어긋난다.
expect(
authCatalogCode(
_exception('account-exists-with-different-credential'),
flow: AuthFlow.signIn,
),
'E-AUTH-08',
);
});
});
group('경로와 무관한 코드는 한 문구를 유지한다', () {
test('credential-already-in-use 는 양쪽이 같다', () {
final error = _exception('credential-already-in-use');
expect(
authMessage(error, flow: AuthFlow.signIn),
'다른 FANUZU 계정에 이미 연결된 소셜 계정입니다.',
);
expect(
authMessage(error, flow: AuthFlow.link),
authMessage(error, flow: AuthFlow.signIn),
);
});
test('계정 존재 여부를 갈라 주지 않는다 — 열거 방지', () {
// 「등록되지 않은 이메일」과 「비밀번호 오류」가 같은 문구로 나가야 한다.
for (final code in ['user-not-found', 'wrong-password']) {
expect(
authMessage(_exception(code), flow: AuthFlow.email),
'이메일 또는 비밀번호를 확인해 주세요.',
reason: code,
);
}
});
});
group('소셜 웹 로그인 실패 · 중단 — 이메일 문구를 쓰지 않는다(10/8 실기기 LINE · R01)', () {
const webCodes = [
'web-context-already-presented',
'web-network-request-failed',
'web-internal-error',
'internal-error',
'invalid-credential',
];
test('소셜 로그인 · 연결의 알 수 없는 실패는 E-AUTH-03 · 소셜 문구', () {
for (final code in webCodes) {
for (final flow in [AuthFlow.signIn, AuthFlow.link]) {
expect(authCatalogCode(_exception(code), flow: flow), 'E-AUTH-03', reason: '$code $flow');
expect(
authMessage(_exception(code), flow: flow),
isNot('이메일 또는 비밀번호를 확인해 주세요.'),
reason: '$code $flow',
);
}
}
expect(
authMessage(_exception('web-context-already-presented'), flow: AuthFlow.signIn),
'로그인이 완료되지 않았습니다. 다시 시도해 주세요.',
);
expect(
authMessage(_exception('web-network-request-failed'), flow: AuthFlow.signIn),
'네트워크 연결을 확인한 뒤 다시 시도해 주세요.',
);
expect(authMessage(_exception('internal-error'), flow: AuthFlow.signIn), '소셜 로그인에 실패했습니다.');
});
test('이메일 폼만 E-AUTH-01 · 「이메일 또는 비밀번호」', () {
expect(authCatalogCode(_exception('internal-error'), flow: AuthFlow.email), 'E-AUTH-01');
expect(authMessage(_exception('internal-error'), flow: AuthFlow.email), '이메일 또는 비밀번호를 확인해 주세요.');
});
test('취소는 성공이 아니고 다시 시도하라고 안내한다', () {
expect(authNotice(const AuthCancelled()), '로그인이 완료되지 않았습니다. 다시 시도해 주세요.');
});
});
// 10/4 실기기 2-3 — Android Apple 로그인 다이얼로그에 「발주사 키」 · 설정 항목이 떴다.
test('설정 전 제공자 안내는 내부 설정 항목을 내보내지 않는다', () {
final notice = authNotice(
const AuthNotConfigured(AuthProviderKind.apple, ['Apple Android 웹 인증 설정']),
)!;
expect(notice, contains('Apple'));
expect(notice, isNot(contains('웹 인증 설정')));
expect(notice, isNot(contains('발주사')));
});
}
/// `FirebaseAuthException` 의 생성자가 `@protected` 라 밖에서 직접 부르면
/// 분석 경고가 난다. 하위 클래스에서 부르는 것은 허용되므로 테스트용으로 뚫는다.
final class _FakeAuthException extends fb.FirebaseAuthException {
_FakeAuthException({required super.code, super.email});
}
fb.FirebaseAuthException _exception(String code, {String? email}) =>
_FakeAuthException(code: code, email: email);