퀘스트 일시 정지 · 이어하기 · 진행 상태 서버 저장 · 목록 보상받기

This commit is contained in:
Sn-Kinos
2026-10-06 03:20:46 +09:00
parent ecf5998725
commit abe5f1ae50
28 changed files with 1023 additions and 118 deletions
+41 -1
View File
@@ -6,7 +6,7 @@ import { bootAdmin, shutdownAdmin, wipeUser } from './support.js';
import { grantPop } from '../src/ledger/ledger.js';
import { verifiedQuestCountOf } from '../src/ledger/fpi.js';
import { fnAdminCompletionVoid } from '../src/admin/admin.js';
import { fnAdReward, fnQuestAttend, fnQuestVerifySteps } from '../src/quest/quest.js';
import { fnAdReward, fnQuestAttend, fnQuestProgress, fnQuestVerifySteps } from '../src/quest/quest.js';
import { fnQuestVerifyMusicShake, fnQuestVerifyPlace, placeProofStorage } from '../src/quest/verify.js';
import { fnCampaignFund } from '../src/fandom/fandom.js';
@@ -715,3 +715,43 @@ describe('POP → 행성 반영 (별첨 1 ② 완료 시나리오 1)', () => {
// 여기서 남긴 문서를 그대로 읽는다. 그래서 이 테스트는 상태를 지우지 않는다.
});
});
describe('진행 상태 — 일시 정지 · 이어하기 · 진행완료 (fn-quest-progress · 원장 C-83)', () => {
const ref = () => db().doc(`users/${UID}/questProgress/${STEPS_QUEST}`);
it('진행중 → 진행완료를 남기고, 일일 한도 · 완료 횟수는 건드리지 않는다', async () => {
await call(fnQuestProgress, { questId: STEPS_QUEST, state: 'inProgress', total: 1000 });
expect((await ref().get()).get('session.state')).toBe('inProgress');
await call(fnQuestProgress, {
questId: STEPS_QUEST, state: 'paused', progress: 3, total: 60, positionMs: 3000, hits: [0, 0.1, -1],
});
await call(fnQuestProgress, { questId: STEPS_QUEST, state: 'completed', progress: 1200, total: 1000 });
const snap = await ref().get();
// 세션은 통째로 바뀐다 — 일시 정지의 판정 기록이 남지 않는다.
expect(snap.get('session')).toMatchObject({ state: 'completed', progress: 1200, total: 1000 });
expect(snap.get('session.hits')).toBeUndefined();
expect(snap.get('count')).toBeUndefined();
expect(snap.get('dayKey')).toBeUndefined();
});
it('보상을 받으면 세션이 지워지고, 중단(none)도 세션만 지운다', async () => {
await call(fnQuestProgress, { questId: STEPS_QUEST, state: 'completed', progress: 1200, total: 1000 });
await call(fnQuestVerifySteps, { questId: STEPS_QUEST, steps: 1200 });
expect((await ref().get()).get('session')).toBeUndefined();
expect((await ref().get()).get('count')).toBe(1);
await call(fnQuestProgress, { questId: STEPS_QUEST, state: 'inProgress' });
await call(fnQuestProgress, { questId: STEPS_QUEST, state: 'none' });
expect((await ref().get()).get('session')).toBeUndefined();
expect((await ref().get()).get('count')).toBe(1);
});
it('진행 단계가 없는 유형 · 없는 퀘스트 · 이상한 값은 거부한다(Q-B-12 A)', async () => {
expect(await codeOf(() => call(fnQuestProgress, { questId: ATTEND_QUEST, state: 'inProgress' }))).toBe('E-QST-01');
expect(await codeOf(() => call(fnQuestProgress, { questId: 'no-such-quest', state: 'paused' }))).toBe('E-QST-01');
expect(await codeOf(() => call(fnQuestProgress, { questId: STEPS_QUEST, state: 'rewarded' }))).toBe('E-QST-02');
expect(await codeOf(() => call(fnQuestProgress, { questId: STEPS_QUEST, state: 'paused', progress: -1 }))).toBe('E-QST-02');
expect(await codeOf(() => call(fnQuestProgress, { questId: STEPS_QUEST, state: 'paused', hits: [2] }))).toBe('E-QST-02');
expect((await db().doc(`users/${UID}/questProgress/${ATTEND_QUEST}`).get()).exists).toBe(false);
});
});