FPI 사용자·국가 랭킹 서버 집계 · YouTube 스밍 · Music Shake 참여

This commit is contained in:
Sn-Kinos
2026-10-05 17:49:47 +09:00
parent 5238fc4ef3
commit 96a95a12d1
31 changed files with 738 additions and 116 deletions
+35 -2
View File
@@ -310,6 +310,22 @@ void main() {
final snapshot = await FirestoreGalaxyRepository(_backend(db)).load();
final rank = {for (final p in snapshot.planets) p.id: p.rank};
expect(rank, {'bts': 1, 'svt': 2, 'neb': 3});
expect(snapshot.planets.firstWhere((p) => p.id == 'neb').rankLabel, '-');
});
// 원장 B-17 — 최근 7일 유효 활동이 없는 정식 행성(서버 rankingEligible: false)도 순위 밖이다.
test('최근 7일 활동이 없는 행성은 순위 없이 바깥 궤도에 서고 Ranking View 중심이 되지 않는다', () async {
final db = await _seed();
await db.doc('fandoms/idle').set({
'name': '휴면', 'stage': 2, 'fpiScore': 999999, 'rankingEligible': false,
});
await db.doc('fandoms/svt').set({
'name': 'SEVENTEEN', 'stage': 1, 'fpiScore': 120000, 'rankingEligible': true,
});
final snapshot = await FirestoreGalaxyRepository(_backend(db)).load();
final rank = {for (final p in snapshot.planets) p.id: (p.rank, p.rankLabel)};
expect(rank, {'bts': (1, '1'), 'svt': (2, '2'), 'idle': (3, '-')});
expect(snapshot.rankingView.planets.firstWhere((p) => p.isCenter).id, 'bts');
});
test('대표 팬덤은 primaryFandomId 가 정본이고 가입 목록은 최신 가입순이다(C-15 · §2.58)', () async {
@@ -466,13 +482,12 @@ void main() {
expect((await repo.load(nebula: false)).totalFpi, 300000);
});
test('내 기여 상위 집계가 없으면 Mock 사유를 화면에 넘긴다 — 대조표 3-5', () async {
test('서버가 내 기여 상위 % 를 적지 않은 축은 null 이다 — 「상위 100%」를 짓지 않는다', () async {
final db = await seedPlanet();
await db.doc('fandoms/bts/members/$_uid')
.set({'nickname': '나'});
final detail = await FirestorePlanetRepository(_backend(db), fandomId: 'bts')
.load(nebula: false);
expect(detail.mockNote, isNotNull);
expect(detail.scores.every((s) => s.myTopPercent == null), isTrue);
});
@@ -551,6 +566,24 @@ void main() {
expect(referral.mine!.rank, 1);
});
test('사용자 순위 · 순위 변동은 서버 5분 집계(rankByAxis · rankPrevByAxis)를 읽는다', () async {
final db = await seedPlanet();
await db.doc('fandoms/bts/members/other').set({
'rankByAxis': {'bpm': 1}, 'rankPrevByAxis': {'bpm': 2},
}, SetOptions(merge: true));
await db.doc('fandoms/bts/members/$_uid').set({
'rankByAxis': {'bpm': 2}, 'rankPrevByAxis': {'bpm': 1},
}, SetOptions(merge: true));
final repo = FirestorePlanetRepository(_backend(db), fandomId: 'bts');
final bpm = await repo.userRanking(metric: FpiMetric.bpm, fandomId: 'bts');
expect(bpm.rows.map((r) => (r.name, r.rank, r.change)).toList(), [
('버니즈', 1, 1),
('나', 2, -1),
]);
expect(bpm.mine!.change, -1);
expect(bpm.mockNote, isNull);
});
test('국가 랭킹은 사전 집계 1건을 읽고, 없으면 Mock 이라고 알린다', () async {
final db = await seedPlanet();
final repo = FirestorePlanetRepository(_backend(db), fandomId: 'bts');