-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: qr code show not found exception handling (#107)
* test: add firebase and getUser unit test * fix: qr code show not found exception handling
- Loading branch information
1 parent
c2cc5d6
commit 43ff8c7
Showing
3 changed files
with
259 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import type { CollectionReference, DocumentReference } from 'firebase-admin/firestore'; | ||
import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { | ||
adminDb, | ||
checkRemoveParticipantPermission, | ||
getGroupsData, | ||
getSessionData | ||
} from '../src/lib/server/firebase'; | ||
|
||
// Mock Firestore | ||
vi.mock('firebase-admin/firestore', () => ({ | ||
getFirestore: vi.fn(() => ({ | ||
collection: vi.fn(), | ||
doc: vi.fn() | ||
})), | ||
Timestamp: { | ||
now: vi.fn(() => ({ toMillis: () => 1234567890000 })) | ||
} | ||
})); | ||
|
||
describe('Firebase Server Functions', () => { | ||
beforeEach(() => { | ||
vi.clearAllMocks(); | ||
}); | ||
|
||
describe('getGroupsData', () => { | ||
it('應該正確返回群組資料', async () => { | ||
const mockGroups = { | ||
empty: false, | ||
docs: [ | ||
{ | ||
data: () => ({ | ||
number: 1, | ||
participants: ['user1', 'user2'], | ||
concept: null | ||
}) | ||
}, | ||
{ | ||
data: () => ({ | ||
number: 2, | ||
participants: ['user3', 'user4'], | ||
concept: null | ||
}) | ||
} | ||
] | ||
}; | ||
|
||
const groupsRef = { | ||
get: vi.fn().mockResolvedValue(mockGroups) | ||
} as unknown as CollectionReference; | ||
|
||
const result = await getGroupsData(groupsRef); | ||
expect(result).toHaveLength(2); | ||
expect(result[0].number).toBe(1); | ||
expect(result[1].participants).toContain('user3'); | ||
}); | ||
|
||
it('當沒有找到群組時應該拋出404錯誤', async () => { | ||
const groupsRef = { | ||
get: vi.fn().mockResolvedValue({ empty: true }) | ||
} as unknown as CollectionReference; | ||
|
||
await expect(getGroupsData(groupsRef)).rejects.toThrow('Groups not found'); | ||
}); | ||
}); | ||
|
||
describe('getSessionData', () => { | ||
it('應該正確返回會話資料', async () => { | ||
const mockSession = { | ||
exists: true, | ||
data: () => ({ | ||
title: 'Test Session', | ||
host: 'user1', | ||
status: 'preparing' | ||
}) | ||
}; | ||
|
||
const sessionRef = { | ||
get: vi.fn().mockResolvedValue(mockSession) | ||
} as unknown as DocumentReference; | ||
|
||
const result = await getSessionData(sessionRef); | ||
expect(result.title).toBe('Test Session'); | ||
expect(result.host).toBe('user1'); | ||
}); | ||
|
||
it('當沒有找到會話時應該拋出404錯誤', async () => { | ||
const sessionRef = { | ||
get: vi.fn().mockResolvedValue({ exists: false }) | ||
} as unknown as DocumentReference; | ||
|
||
await expect(getSessionData(sessionRef)).rejects.toThrow('Session not found'); | ||
}); | ||
}); | ||
|
||
describe('checkRemoveParticipantPermission', () => { | ||
it('當使用者是主持人時應該返回true', async () => { | ||
const mockSession = { | ||
exists: true, | ||
data: () => ({ | ||
host: 'host123', | ||
status: 'preparing' | ||
}) | ||
}; | ||
|
||
vi.spyOn(adminDb, 'collection').mockReturnValue({ | ||
doc: vi.fn().mockReturnValue({ | ||
get: vi.fn().mockResolvedValue(mockSession) | ||
}) | ||
} as unknown as CollectionReference); | ||
|
||
const result = await checkRemoveParticipantPermission( | ||
'session123', | ||
'host123', | ||
'participant123' | ||
); | ||
expect(result).toBe(true); | ||
}); | ||
|
||
it('當使用者是被移除的參與者時應該返回true', async () => { | ||
const mockSession = { | ||
exists: true, | ||
data: () => ({ | ||
host: 'host123', | ||
status: 'preparing' | ||
}) | ||
}; | ||
|
||
vi.spyOn(adminDb, 'collection').mockReturnValue({ | ||
doc: vi.fn().mockReturnValue({ | ||
get: vi.fn().mockResolvedValue(mockSession) | ||
}) | ||
} as unknown as CollectionReference); | ||
|
||
const result = await checkRemoveParticipantPermission( | ||
'session123', | ||
'participant123', | ||
'participant123' | ||
); | ||
expect(result).toBe(true); | ||
}); | ||
|
||
it('當使用者既不是主持人也不是被移除的參與者時應該返回false', async () => { | ||
const mockSession = { | ||
exists: true, | ||
data: () => ({ | ||
host: 'host123', | ||
status: 'preparing' | ||
}) | ||
}; | ||
|
||
vi.spyOn(adminDb, 'collection').mockReturnValue({ | ||
doc: vi.fn().mockReturnValue({ | ||
get: vi.fn().mockResolvedValue(mockSession) | ||
}) | ||
} as unknown as CollectionReference); | ||
|
||
const result = await checkRemoveParticipantPermission( | ||
'session123', | ||
'otherUser123', | ||
'participant123' | ||
); | ||
expect(result).toBe(false); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import type { DocumentReference, DocumentSnapshot } from 'firebase/firestore'; | ||
import { doc, getDoc } from 'firebase/firestore'; | ||
import type { Mock } from 'vitest'; | ||
import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { getUser } from '../src/lib/utils/getUser'; | ||
|
||
// Mock Firebase | ||
vi.mock('firebase/firestore', () => ({ | ||
doc: vi.fn(), | ||
getDoc: vi.fn() | ||
})); | ||
|
||
vi.mock('$lib/firebase', () => ({ | ||
db: {} | ||
})); | ||
|
||
describe('getUser', () => { | ||
beforeEach(() => { | ||
vi.clearAllMocks(); | ||
}); | ||
|
||
it('應該正確返回使用者資料', async () => { | ||
const mockUserData = { | ||
uid: 'test122', | ||
displayName: 'Test User', | ||
title: 'Developer', | ||
bio: 'Hello World' | ||
}; | ||
|
||
(doc as unknown as Mock).mockReturnValue({} as DocumentReference); | ||
(getDoc as unknown as Mock).mockResolvedValue({ | ||
exists: () => true, | ||
data: () => mockUserData | ||
} as unknown as DocumentSnapshot); | ||
|
||
const user = await getUser('test123'); | ||
expect(user).toEqual(mockUserData); | ||
}); | ||
|
||
it('當使用者不存在時應該返回預設資料', async () => { | ||
const uid = 'nonexistent123'; | ||
(doc as unknown as Mock).mockReturnValue({} as DocumentReference); | ||
(getDoc as unknown as Mock).mockResolvedValue({ | ||
exists: () => false, | ||
data: () => null | ||
} as unknown as DocumentSnapshot); | ||
|
||
const user = await getUser(uid); | ||
expect(user).toEqual({ | ||
uid, | ||
displayName: uid, | ||
title: null, | ||
bio: null | ||
}); | ||
}); | ||
|
||
it('應該快取使用者資料', async () => { | ||
const mockUserData = { | ||
uid: 'test122', | ||
displayName: 'Test User', | ||
title: 'Developer', | ||
bio: 'Hello World' | ||
}; | ||
|
||
(doc as unknown as Mock).mockReturnValue({} as DocumentReference); | ||
(getDoc as unknown as Mock).mockResolvedValue({ | ||
exists: () => true, | ||
data: () => mockUserData | ||
} as unknown as DocumentSnapshot); | ||
|
||
// 第一次呼叫 | ||
await getUser('test122'); | ||
// 第二次呼叫 | ||
await getUser('test122'); | ||
|
||
// 應該只呼叫一次 getDoc | ||
expect(getDoc).toHaveBeenCalledTimes(1); | ||
}); | ||
}); |