Kakao·LINE 이메일 선택 수집 — 동의한 이메일만 저장 · 이메일 없이 제공자 식별값으로 가입·로그인
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { getAuth } from 'firebase-admin/auth';
|
||||
import { getFirestore } from 'firebase-admin/firestore';
|
||||
|
||||
import { bootAdmin, shutdownAdmin } from './support.js';
|
||||
import { fnAuthOnCreate } from '../src/account/account.js';
|
||||
import { fnAuthKakao, kakaoUidOf } from '../src/account/kakao.js';
|
||||
|
||||
/** 이메일 선택 수집 — 동의한 이메일만 남기고, 이메일이 없어도 가입 · 로그인된다. */
|
||||
|
||||
const U = 'test-email-user';
|
||||
const KAKAO_ID = '555000111';
|
||||
const db = () => getFirestore();
|
||||
|
||||
type KakaoReq = Parameters<typeof fnAuthKakao.run>[0];
|
||||
const onCreate = (uid: string, extra: Record<string, unknown> = {}) =>
|
||||
(fnAuthOnCreate as unknown as { run: (u: unknown) => Promise<void> })
|
||||
.run({ uid, email: undefined, displayName: undefined, photoURL: undefined, providerData: [], ...extra });
|
||||
|
||||
function stubKakao(email?: string) {
|
||||
vi.stubGlobal('fetch', vi.fn(async (url: string) => {
|
||||
if (url.endsWith('/v1/user/access_token_info')) return Response.json({ id: Number(KAKAO_ID), app_id: 123 });
|
||||
return Response.json({
|
||||
id: Number(KAKAO_ID),
|
||||
kakao_account: { profile: { nickname: '카카오' }, ...(email ? { email, is_email_valid: true, is_email_verified: true } : {}) },
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
async function wipe() {
|
||||
await getAuth().deleteUsers([U, kakaoUidOf(KAKAO_ID)]);
|
||||
for (const uid of [U, kakaoUidOf(KAKAO_ID)]) await db().recursiveDelete(db().doc(`users/${uid}`));
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
if (!process.env.FIREBASE_AUTH_EMULATOR_HOST) throw new Error('FIREBASE_AUTH_EMULATOR_HOST 가 없습니다.');
|
||||
process.env.KAKAO_APP_ID = '123';
|
||||
bootAdmin();
|
||||
});
|
||||
afterAll(async () => { await wipe(); await shutdownAdmin(); });
|
||||
beforeEach(wipe);
|
||||
afterEach(() => { vi.unstubAllGlobals(); });
|
||||
|
||||
describe('계정 생성 트리거', () => {
|
||||
it('제공자 정보의 이메일을 쓰고 · 이미 쓴 값은 덮지 않는다', async () => {
|
||||
await db().doc(`users/${U}`).set({ displayName: '먼저씀' });
|
||||
await onCreate(U, { providerData: [{ providerId: 'oidc.line', email: 'line@fan.io' }] });
|
||||
const user = (await db().doc(`users/${U}`).get()).data()!;
|
||||
expect(user.displayName).toBe('먼저씀');
|
||||
expect(user.email).toBe('line@fan.io');
|
||||
expect(user.status).toBe('active');
|
||||
expect((await db().doc(`users/${U}/wallet/main`).get()).get('activityBalance')).toBe(0);
|
||||
});
|
||||
|
||||
it('이메일이 없으면 null 로 만든다', async () => {
|
||||
await onCreate(U, { providerData: [{ providerId: 'oidc.line' }] });
|
||||
expect((await db().doc(`users/${U}`).get()).get('email')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('카카오 이메일', () => {
|
||||
it('동의한 이메일만 사용자 문서에 남기고 · 없어도 로그인된다', async () => {
|
||||
stubKakao();
|
||||
const first = await fnAuthKakao.run({ data: { accessToken: 'good' } } as KakaoReq) as { token: string };
|
||||
expect(first.token).toBeTruthy();
|
||||
expect((await db().doc(`users/${kakaoUidOf(KAKAO_ID)}`).get()).get('email')).toBeUndefined();
|
||||
stubKakao('fan@kakao.com');
|
||||
await fnAuthKakao.run({ data: { accessToken: 'good' } } as KakaoReq);
|
||||
expect((await db().doc(`users/${kakaoUidOf(KAKAO_ID)}`).get()).get('email')).toBe('fan@kakao.com');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user