공식 로고 앱 아이콘 적용 · 확정 정책에 맞춰 등급·POP 소멸 필드를 규칙·인덱스에서 제외

This commit is contained in:
Sn-Kinos
2026-08-14 00:46:56 +09:00
parent 832cb88c30
commit 97ea5fad35
41 changed files with 51 additions and 77 deletions
@@ -49,11 +49,9 @@ export function userDocument(uid, overrides = {}) {
uid,
displayName: uid,
fandomId: 'fandom-a',
grade: 'SILVER',
totalContributedPop: 0,
status: 'active',
updatedAt: '2026-08-08T00:00:00Z',
...overrides,
};
}
@@ -43,7 +43,7 @@ afterAll(async () => {
await testEnvironment.cleanup();
});
describe('원칙 1 — POP·FPI·등급·원장은 클라이언트 쓰기 불가', () => {
describe('원칙 1 — POP·FPI·원장은 클라이언트 쓰기 불가', () => {
test('본인 지갑 잔액 직접 증액을 거부한다', async () => {
await seedDocuments(testEnvironment, {
'users/alice/wallet/main': { balance: 100 },
@@ -61,14 +61,14 @@ describe('원칙 1 — POP·FPI·등급·원장은 클라이언트 쓰기 불가
);
});
test('POP Lot 만료일 연장을 거부한다', async () => {
test('POP Lot 잔량 조작을 거부한다', async () => {
await seedDocuments(testEnvironment, {
'users/alice/popLots/lot-1': { remaining: 100, expiresAt: '2026-08-09T00:00:00Z' },
'users/alice/popLots/lot-1': { remaining: 100 },
});
await assertFails(
updateDoc(doc(dbFor('alice'), 'users/alice/popLots/lot-1'), {
expiresAt: '2099-12-31T00:00:00Z',
remaining: 999_999,
}),
);
});
@@ -82,12 +82,14 @@ describe('원칙 1 — POP·FPI·등급·원장은 클라이언트 쓰기 불가
);
});
test('사용자 등급 직접 승격을 거부한다', async () => {
test('누적 기여 통계 직접 조작을 거부한다', async () => {
await seedDocuments(testEnvironment, {
'users/alice': userDocument('alice'),
});
await assertFails(updateDoc(doc(dbFor('alice'), 'users/alice'), { grade: 'DIAMOND' }));
await assertFails(
updateDoc(doc(dbFor('alice'), 'users/alice'), { totalContributedPop: 999_999 }),
);
});
test('다른 사용자의 지갑 쓰기를 거부한다', async () => {
@@ -125,11 +127,11 @@ describe('원칙 2 — 개인 리소스와 쓰기 필드 제한', () => {
await assertFails(updateDoc(doc(dbFor('alice'), 'users/bob'), { displayName: 'Forged' }));
});
test('허용 필드와 등급을 섞은 본인 업데이트를 전부 거부한다', async () => {
test('허용 필드와 서버 통계를 섞은 본인 업데이트를 전부 거부한다', async () => {
await assertFails(
updateDoc(doc(dbFor('alice'), 'users/alice'), {
displayName: 'New Alice',
grade: 'DIAMOND',
totalContributedPop: 999_999,
}),
);
});
@@ -251,15 +253,13 @@ describe('F-02 — 캠페인 집계 필드 위조 회귀', () => {
summary: 'Summary',
body: 'Body',
coverImageUrl: 'https://example.test/cover.png',
campaignType: 'support',
campaignType: 'other',
targetPop: 1000,
endsAt: '2026-09-01T00:00:00Z',
status: 'draft',
raisedPop: 0,
achievedRate: 0,
donorCount: 0,
votedPop: 0,
isAiDraft: false,
refundState: 'none',
createdAt: '2026-08-08T00:00:00Z',
updatedAt: '2026-08-08T00:00:00Z',
@@ -275,14 +275,12 @@ describe('F-02 — 캠페인 집계 필드 위조 회귀', () => {
await assertSucceeds(setDoc(doc(dbFor('alice'), 'campaigns/valid'), validCampaign));
});
test('집계·투표·AI 초안 필드 위조를 거부한다', async () => {
test('집계 필드 위조를 거부한다', async () => {
await assertFails(
setDoc(doc(dbFor('alice'), 'campaigns/forged'), {
...validCampaign,
achievedRate: 1,
donorCount: 99_999,
votedPop: 999_999,
isAiDraft: true,
}),
);
});
@@ -389,7 +387,10 @@ describe('F-14 — posts 허용 필드 회귀', () => {
describe('F-06 — users 클라이언트 생성 경로 회귀', () => {
test('일반 사용자의 users 문서 생성을 거부한다', async () => {
await assertFails(
setDoc(doc(dbFor('alice'), 'users/alice'), userDocument('alice', { grade: 'DIAMOND' })),
setDoc(
doc(dbFor('alice'), 'users/alice'),
userDocument('alice', { totalContributedPop: 999_999 }),
),
);
});