퀘스트 일시 정지 · 이어하기 · 진행 상태 서버 저장 · 목록 보상받기
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import 'package:fake_cloud_firestore/fake_cloud_firestore.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart' show SetOptions;
|
||||
import 'package:cloud_firestore/cloud_firestore.dart' show SetOptions, Timestamp;
|
||||
import 'package:fancake/core/data/backend.dart';
|
||||
import 'package:fancake/features/quest/quest_data.dart';
|
||||
import 'package:fancake/features/quest/quest_evidence.dart';
|
||||
@@ -627,4 +627,123 @@ service cloud.firestore {
|
||||
expect(live.capacity, 5);
|
||||
});
|
||||
});
|
||||
|
||||
group('진행 상태 저장 · 일시 정지 · 최신순 (원장 C-83 · 명세 518:88395)', () {
|
||||
Future<FakeFirebaseFirestore> shakeSeed() async {
|
||||
final db = await _seed();
|
||||
await db.doc('quests/shake').set({
|
||||
'type': 'musicshake', 'title': '응원 흔들기', 'dailyLimit': 1,
|
||||
'totalSegments': 60, 'bpm': 120,
|
||||
'link': {'url': 'https://youtu.be/WMweEpGlu_U'},
|
||||
});
|
||||
return db;
|
||||
}
|
||||
|
||||
test('목록은 createdAt 내림차순으로 서버 정렬해 커서로 이어 읽는다', () async {
|
||||
final db = FakeFirebaseFirestore();
|
||||
await db.doc('users/$_uid').set({'uid': _uid});
|
||||
await db.doc('config/policy').set({'attendPop': 10});
|
||||
for (var i = 0; i < 12; i++) {
|
||||
await db.doc('quests/q$i').set({
|
||||
'type': 'attend', 'title': 'Q$i', 'dailyLimit': 1,
|
||||
'createdAt': Timestamp.fromMillisecondsSinceEpoch(1000000 + i * 1000),
|
||||
});
|
||||
}
|
||||
final repo = FirestoreQuestRepository(_RecordingBackend(db));
|
||||
final first = await repo.load();
|
||||
final next = await repo.more(offset: first.length);
|
||||
expect([...first, ...next].map((c) => c.id).toList(),
|
||||
[for (var i = 11; i >= 0; i--) 'q$i']);
|
||||
});
|
||||
|
||||
test('Music Shake 세션 — 화면 밖 진행중은 일시 정지 · 진행완료는 보상받기 · 진행중 탭이 받는다', () async {
|
||||
final db = await shakeSeed();
|
||||
final ref = db.doc('users/$_uid/questProgress/shake');
|
||||
final repo = FirestoreQuestRepository(_RecordingBackend(db));
|
||||
|
||||
await ref.set({'session': {'state': 'inProgress', 'progress': 43, 'total': 60}});
|
||||
var card = _card(await repo.load(), 'shake');
|
||||
// 앱이 꺼진 진행중도 진행 화면 밖이므로 일시 정지다.
|
||||
expect(card.stage, QuestStage.paused);
|
||||
expect(card.stage.buttonLabel, '이어하기');
|
||||
expect(QuestTab.inProgress.accepts(card), isTrue);
|
||||
|
||||
await ref.set({'session': {
|
||||
'state': 'completed', 'progress': 60, 'total': 60,
|
||||
'judgement': {'perfect': 50, 'good': 5, 'miss': 5, 'maxCombo': 30},
|
||||
}});
|
||||
card = _card(await repo.load(), 'shake');
|
||||
expect(card.stage, QuestStage.completed);
|
||||
expect(QuestTab.inProgress.accepts(card), isTrue);
|
||||
});
|
||||
|
||||
test('목록에서 바로 받는 Music Shake 는 서버 세션의 판정표를 보낸다', () async {
|
||||
final db = await shakeSeed();
|
||||
await db.doc('users/$_uid/questProgress/shake').set({'session': {
|
||||
'state': 'completed', 'progress': 60, 'total': 60,
|
||||
'judgement': {'perfect': 50, 'good': 5, 'miss': 5, 'maxCombo': 30},
|
||||
}});
|
||||
final backend = _RecordingBackend(db);
|
||||
final repo = FirestoreQuestRepository(backend);
|
||||
await repo.verify(_card(await repo.load(), 'shake'));
|
||||
final call = backend.calls.single;
|
||||
expect(call.name, 'fnQuestVerifyMusicShake');
|
||||
expect(call.payload['totalSegments'], 60);
|
||||
expect(call.payload['judgement'], {'perfect': 50, 'good': 5, 'miss': 5, 'maxCombo': 30});
|
||||
});
|
||||
|
||||
test('Walk 는 일시 정지 없이 오늘 걸음으로 진행중 · 진행완료를 낸다(C-62)', () async {
|
||||
final db = await _seed();
|
||||
await db.doc('users/$_uid/questProgress/steps').set({
|
||||
'session': {'state': 'inProgress', 'progress': 0, 'total': 10000},
|
||||
});
|
||||
var steps = 4_000;
|
||||
final repo = FirestoreQuestRepository(
|
||||
_RecordingBackend(db),
|
||||
todaySteps: () async => steps,
|
||||
);
|
||||
var card = _card(await repo.load(), 'steps');
|
||||
expect((card.stage, card.progress), (QuestStage.inProgress, 4_000));
|
||||
steps = 10_200; // 화면 밖에서 걸었다.
|
||||
card = _card(await repo.load(), 'steps');
|
||||
expect((card.stage, card.progress), (QuestStage.completed, 10_200));
|
||||
});
|
||||
|
||||
test('진행 상태는 fnQuestProgress 로만 쓰고 중단은 none 이다', () async {
|
||||
final db = await _seed();
|
||||
final backend = _RecordingBackend(db);
|
||||
final repo = FirestoreQuestRepository(backend);
|
||||
await repo.advance('shake', QuestStage.paused,
|
||||
session: const QuestSession(
|
||||
state: QuestStage.paused, progress: 43, total: 60,
|
||||
position: Duration(milliseconds: 21500), hits: [0.1, -1],
|
||||
));
|
||||
await repo.advance('steps', QuestStage.notStarted);
|
||||
expect(backend.calls.map((c) => c.name), ['fnQuestProgress', 'fnQuestProgress']);
|
||||
expect(backend.calls.first.payload, {
|
||||
'questId': 'shake', 'state': 'paused', 'progress': 43, 'total': 60,
|
||||
'positionMs': 21500, 'hits': [0.1, -1.0],
|
||||
});
|
||||
expect(backend.calls.last.payload, {'questId': 'steps', 'state': 'none'});
|
||||
});
|
||||
|
||||
test('캡슐은 가장 최근에 멈춘 Quest 1건이다(QA 3-34)', () async {
|
||||
final db = await shakeSeed();
|
||||
await db.doc('quests/shake2').set({
|
||||
'type': 'musicshake', 'title': '두 번째', 'totalSegments': 30, 'bpm': 100,
|
||||
});
|
||||
await db.doc('users/$_uid/questProgress/shake').set({'session': {
|
||||
'state': 'paused', 'progress': 43, 'total': 60,
|
||||
'updatedAt': Timestamp.fromMillisecondsSinceEpoch(2000),
|
||||
}});
|
||||
await db.doc('users/$_uid/questProgress/shake2').set({'session': {
|
||||
'state': 'paused', 'progress': 3, 'total': 30,
|
||||
'updatedAt': Timestamp.fromMillisecondsSinceEpoch(1000),
|
||||
}});
|
||||
final repo = FirestoreQuestRepository(_RecordingBackend(db));
|
||||
final card = await repo.pausedQuest();
|
||||
expect(card?.id, 'shake');
|
||||
expect((card?.session?.progress, card?.session?.total), (43, 60));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user