Firebase MCP
by gannonh
Verified
import { listDocuments, addDocument, getDocument, updateDocument, deleteDocument, list_collections } from '../firestoreClient';
import { admin } from '../firebaseConfig';
import { WhereFilterOp } from 'firebase-admin/firestore';
/**
* Firestore Client Tests
*
* These tests verify the functionality of the Firestore client operations.
* Tests run against the Firebase emulator when available.
*/
describe('Firestore Client', () => {
// Test collection and document data
const collectionName = 'test_collection';
const testDocData = {
field1: 'test value',
numberField: 42,
boolField: true,
dateField: new Date().toISOString(),
nestedField: { subField: 'nested value' }
};
let testDocId = '';
// Setup: Create a test document before running tests
beforeAll(async () => {
// Clean up any existing test documents
try {
const db = admin.firestore();
if (db) {
const snapshot = await db.collection(collectionName).get();
const batch = db.batch();
snapshot.docs.forEach(doc => {
batch.delete(doc.ref);
});
await batch.commit();
console.log(`Cleaned up ${snapshot.size} documents from ${collectionName}`);
}
} catch (error) {
console.error('Error in test cleanup:', error);
}
});
// Test adding a document
it('should add a document and return its ID', async () => {
const result = await addDocument(collectionName, testDocData);
// Verify the response format
expect(result.content).toBeDefined();
expect(result.content.length).toBeGreaterThan(0);
expect(result.isError).toBeUndefined();
// Parse the response and verify document data
const responseData = JSON.parse(result.content[0].text);
expect(responseData.id).toBeDefined();
expect(responseData.document).toMatchObject(testDocData);
// Save the document ID for later tests
testDocId = responseData.id;
console.log(`Created test document with ID: ${testDocId}`);
});
// Test listing collections
it('should list collections', async () => {
const result = await list_collections();
// Verify the response format
expect(result.content).toBeDefined();
expect(result.content.length).toBeGreaterThan(0);
expect(result.isError).toBeUndefined();
// Parse the response and verify collections data
const responseData = JSON.parse(result.content[0].text);
expect(responseData.collections).toBeDefined();
expect(Array.isArray(responseData.collections)).toBe(true);
// Verify our test collection exists in the list
const collectionExists = responseData.collections.some(
(collection: { name: string }) => collection.name === collectionName
);
expect(collectionExists).toBe(true);
});
// Test listing documents
it('should list documents with filtering', async () => {
// Create a date filter
const dateFilter = {
field: 'numberField',
operator: '>' as WhereFilterOp,
value: 40
};
const result = await listDocuments(collectionName, [dateFilter]);
// Verify the response format
expect(result.content).toBeDefined();
expect(result.content.length).toBeGreaterThan(0);
expect(result.isError).toBeUndefined();
// Parse the response and verify documents data
const responseData = JSON.parse(result.content[0].text);
expect(responseData.documents).toBeDefined();
expect(Array.isArray(responseData.documents)).toBe(true);
expect(responseData.totalCount).toBeGreaterThan(0);
// Verify our test document is in the results
const docExists = responseData.documents.some(
(doc: { id: string }) => doc.id === testDocId
);
expect(docExists).toBe(true);
});
// Test getting a document
it('should get a document by ID', async () => {
// Skip if we don't have a test document ID
if (!testDocId) {
console.warn('Skipping get document test - no test document ID available');
return;
}
const result = await getDocument(collectionName, testDocId);
// Verify the response format
expect(result.content).toBeDefined();
expect(result.content.length).toBeGreaterThan(0);
expect(result.isError).toBeUndefined();
// Parse the response and verify document data
const responseData = JSON.parse(result.content[0].text);
expect(responseData.id).toBe(testDocId);
expect(responseData.document).toMatchObject(testDocData);
});
// Test updating a document
it('should update a document', async () => {
// Skip if we don't have a test document ID
if (!testDocId) {
console.warn('Skipping update document test - no test document ID available');
return;
}
const updatedData = {
field1: 'updated value',
newField: 'new value'
};
const result = await updateDocument(collectionName, testDocId, updatedData);
// Verify the response format
expect(result.content).toBeDefined();
expect(result.content.length).toBeGreaterThan(0);
expect(result.isError).toBeUndefined();
// Parse the response
const responseData = JSON.parse(result.content[0].text);
expect(responseData.id).toBe(testDocId);
// Verify the document was updated by getting it again
const getResult = await getDocument(collectionName, testDocId);
const getResponseData = JSON.parse(getResult.content[0].text);
// Check that the update was applied
expect(getResponseData.document.field1).toBe('updated value');
expect(getResponseData.document.newField).toBe('new value');
// Original fields should still be there
expect(getResponseData.document.numberField).toBe(42);
});
// Test deleting a document
it('should delete a document', async () => {
// Skip if we don't have a test document ID
if (!testDocId) {
console.warn('Skipping delete document test - no test document ID available');
return;
}
const result = await deleteDocument(collectionName, testDocId);
// Verify the response format
expect(result.content).toBeDefined();
expect(result.content.length).toBeGreaterThan(0);
expect(result.isError).toBeUndefined();
// Verify the document was deleted by trying to get it
const getResult = await getDocument(collectionName, testDocId);
expect(getResult.isError).toBe(true);
expect(getResult.content[0].text).toBe('Document not found');
});
// Test error handling for non-existent document
it('should handle non-existent document gracefully', async () => {
const result = await getDocument(collectionName, 'non-existent-id');
// Verify the error response format
expect(result.isError).toBe(true);
expect(result.content[0].text).toBe('Document not found');
});
// Test error handling for invalid collection
it('should handle invalid collection name gracefully', async () => {
// Using a collection name with invalid characters
const result = await listDocuments('invalid/collection/name');
// Verify the error response format
expect(result.isError).toBe(true);
// Update the expectation to match the actual response
expect(result.content[0].text).toBe('No matching documents found');
});
});