Zap·후원 FPI 귀속 · 이클립스 방어 Shake 전용 · 가입 팬덤만 방어
This commit is contained in:
@@ -146,12 +146,16 @@ it('캠페인 후원은 기여 POP 만 쓰고 targetPop 초과를 막으며 캠
|
||||
expect(wallet.activityBalance).toBe(50);
|
||||
expect((await db.doc(`fandoms/${b}`).get()).get('growthPop')).toBe(20);
|
||||
expect((await db.doc(`fandoms/${a}`).get()).get('growthPop')).toBeUndefined();
|
||||
// FPI 도 캠페인 팬덤 b 로 — 성장과 갈라지지 않는다(10/3 #10).
|
||||
expect((await db.collection(`fandoms/${b}/fpiEvents`).get()).size).toBe(1);
|
||||
expect((await db.collection(`fandoms/${a}/fpiEvents`).get()).size).toBe(0);
|
||||
await db.recursiveDelete(db.collection(`fandoms/${b}/fpiEvents`));
|
||||
const donations = await db.collection(`campaigns/${cid}/donations`).get();
|
||||
expect(donations.docs.map((d) => d.get('donorUid'))).toEqual([uid]);
|
||||
await db.recursiveDelete(db.doc(`campaigns/${cid}`));
|
||||
});
|
||||
|
||||
it('fn-pop-spend 는 Zap 만 받고 횟수 · 귀속 팬덤을 서버가 정하며 EXP 를 주지 않는다(리뷰 B1 · H1 · M1)', async () => {
|
||||
it('fn-pop-spend 는 Zap 만 받고 횟수를 서버가 세며 Zap 한 가입 행성에 귀속하고 EXP 를 주지 않는다(리뷰 B1 · H1 · M1)', async () => {
|
||||
const db = getFirestore();
|
||||
const { fnPopSpend } = await import('../src/ledger/pop.js');
|
||||
const run = (data: Record<string, unknown>) => fnPopSpend.run(
|
||||
@@ -164,14 +168,27 @@ it('fn-pop-spend 는 Zap 만 받고 횟수 · 귀속 팬덤을 서버가 정하
|
||||
await expect(run({ popType: 'activity', amount: 5, reasonCode: 'ZAP_CONTRIBUTION', idempotencyKey: 'zap:odd' }))
|
||||
.rejects.toMatchObject({ details: { code: 'E-POP-02' } });
|
||||
|
||||
// 클라이언트가 count 1e9 · 남의 팬덤 b 를 보내도 서버는 30 ÷ 10 = 3 회 · 대표 팬덤 a 로 적는다.
|
||||
// 가입하지 않은 행성 b 에는 Zap 할 수 없다 — 남의 행성 성장을 올리지 못한다(리뷰 M1).
|
||||
await expect(run({ popType: 'activity', amount: 30, fandomId: b,
|
||||
reasonCode: 'ZAP_CONTRIBUTION', idempotencyKey: 'zap:b0' }))
|
||||
.rejects.toMatchObject({ details: { code: 'E-PERM-01' } });
|
||||
|
||||
// b 에 가입한 뒤에는 **Zap 한 행성 b** 에 성장 · FPI · 기여가 귀속된다(상세정의 5장 · 10/3 #10).
|
||||
// 대표 팬덤은 a 그대로다. 클라이언트 count 1e9 는 무시하고 30 ÷ 10 = 3 회로 적는다.
|
||||
await db.doc(`users/${uid}`).update({ fandomIds: [a, b] });
|
||||
await run({ popType: 'activity', amount: 30, count: 1e9, fandomId: b,
|
||||
reasonCode: 'ZAP_CONTRIBUTION', idempotencyKey: 'zap:b1' });
|
||||
const events = await db.collection(`fandoms/${a}/fpiEvents`).get();
|
||||
const events = await db.collection(`fandoms/${b}/fpiEvents`).get();
|
||||
expect(events.docs.map((e) => [e.get('axis'), e.get('units')])).toEqual([['contribution', 3]]);
|
||||
expect((await db.collection(`fandoms/${b}/fpiEvents`).get()).size).toBe(0);
|
||||
expect((await db.doc(`fandoms/${a}`).get()).get('growthPop')).toBe(30);
|
||||
expect((await db.doc(`fandoms/${b}`).get()).get('growthPop')).toBeUndefined();
|
||||
expect((await db.collection(`fandoms/${a}/fpiEvents`).get()).size).toBe(0);
|
||||
expect((await db.doc(`fandoms/${b}`).get()).get('growthPop')).toBe(30);
|
||||
expect((await db.doc(`fandoms/${a}`).get()).get('growthPop')).toBeUndefined();
|
||||
expect((await db.doc(`fandoms/${b}/members/${uid}`).get()).get('contributedPop')).toBe(30);
|
||||
|
||||
// 대상이 없는 구 클라이언트는 대표 팬덤 a 로 간다.
|
||||
await run({ popType: 'activity', amount: 10, reasonCode: 'ZAP_CONTRIBUTION', idempotencyKey: 'zap:a1' });
|
||||
expect((await db.doc(`fandoms/${a}`).get()).get('growthPop')).toBe(10);
|
||||
expect((await db.doc(`users/${uid}`).get()).get('userExp')).toBeUndefined();
|
||||
await db.recursiveDelete(db.collection(`fandoms/${a}/fpiEvents`));
|
||||
await db.recursiveDelete(db.collection(`fandoms/${b}/fpiEvents`));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user