cozy-games/packages/leaderboard/test/firebase-adapter.test.js

102 lines
4.2 KiB
JavaScript

// @ts-check
import { describe, it, expect, beforeEach, vi } from 'vitest'
// Stub the firebase SDK at the adapter boundary so no real app/network is touched.
const fb = vi.hoisted(() => ({
initializeApp: vi.fn(() => ({ __app: true })),
getFirestore: vi.fn(() => ({ __internalStore: true })),
connectFirestoreEmulator: vi.fn(),
doc: vi.fn((...args) => ({ __ref: args })),
getDoc: vi.fn(async () => ({ data: () => ({ passingStatus: 'ok' }) })),
getDocs: vi.fn(async () => ({ docs: [] })),
setDoc: vi.fn(async () => {}),
collection: vi.fn((...args) => ({ __col: args })),
query: vi.fn((...args) => ({ __q: args })),
where: vi.fn((...args) => ({ __where: args })),
orderBy: vi.fn((...args) => ({ __order: args })),
limit: vi.fn((...args) => ({ __limit: args }))
}))
vi.mock('firebase/app', () => ({ initializeApp: fb.initializeApp }))
vi.mock('firebase/firestore/lite', () => ({
getFirestore: fb.getFirestore,
connectFirestoreEmulator: fb.connectFirestoreEmulator,
doc: fb.doc,
getDoc: fb.getDoc,
getDocs: fb.getDocs,
setDoc: fb.setDoc,
collection: fb.collection,
query: fb.query,
where: fb.where,
orderBy: fb.orderBy,
limit: fb.limit
}))
import { FirebaseAdapter } from '../adapters/firebase.js'
beforeEach(() => vi.clearAllMocks())
describe('FirebaseAdapter — consumer-supplied (injected) store', () => {
it('uses an injected store as-is and initializes nothing', () => {
// Stand-in for a privileged / server-side Firestore instance.
const store = { __adminStore: true }
const adapter = new FirebaseAdapter({ store, namespace: 'mw' })
expect(adapter.store).toBe(store)
expect(fb.initializeApp).not.toHaveBeenCalled()
expect(fb.getFirestore).not.toHaveBeenCalled()
})
it('performs a write through the injected store', async () => {
const store = { __adminStore: true }
const adapter = new FirebaseAdapter({ store, namespace: 'mw' })
const entry = { name: 'A', score: 42, category: 'beginner', time_stamp: 123 }
await adapter.addScore('beginner', entry)
// The collection was built from OUR store, and the entry was written.
expect(fb.collection).toHaveBeenCalledWith(store, 'mw-scores', 'beginner', 'games')
expect(fb.setDoc).toHaveBeenCalledTimes(1)
expect(fb.setDoc.mock.calls[0][1]).toEqual(entry)
})
it('archives through the injected store', async () => {
const store = { __adminStore: true }
const adapter = new FirebaseAdapter({ store, namespace: 'mw' })
await adapter.archive({ playerId: 'p1', score: 9, category: 'beginner', time_stamp: 5 })
expect(fb.doc).toHaveBeenCalledWith(store, 'mw-all', 'p1', 'games', expect.any(String))
expect(fb.setDoc).toHaveBeenCalledTimes(1)
})
it('does not auto-connect the emulator for an injected store (consumer owns it)', () => {
new FirebaseAdapter({ store: { __s: 1 }, namespace: 'mw', emulator: { host: 'x', port: 1 } })
expect(fb.connectFirestoreEmulator).not.toHaveBeenCalled()
})
it('prefers the injected store when both store and firebaseConfig are given', () => {
const store = { __adminStore: true }
const adapter = new FirebaseAdapter({ store, firebaseConfig: { projectId: 'p' } })
expect(adapter.store).toBe(store)
expect(fb.initializeApp).not.toHaveBeenCalled()
})
})
describe('FirebaseAdapter — internal init (existing path, unbroken)', () => {
it('still initializes its own store from firebaseConfig and writes', async () => {
const adapter = new FirebaseAdapter({ firebaseConfig: { projectId: 'p' }, namespace: 'mw' })
expect(fb.initializeApp).toHaveBeenCalledWith({ projectId: 'p' })
expect(fb.getFirestore).toHaveBeenCalledTimes(1)
expect(adapter.store).toEqual({ __internalStore: true })
await adapter.addScore('beginner', { score: 1 })
expect(fb.setDoc).toHaveBeenCalledTimes(1)
})
it('still connects the emulator when configured', () => {
new FirebaseAdapter({ firebaseConfig: {}, emulator: { host: '127.0.0.1', port: 8080 } })
expect(fb.connectFirestoreEmulator).toHaveBeenCalledWith({ __internalStore: true }, '127.0.0.1', 8080)
})
it('throws a clear error when neither store nor firebaseConfig is given', () => {
expect(() => new FirebaseAdapter({})).toThrow(TypeError)
})
})