Security News
New Python Packaging Proposal Aims to Solve Phantom Dependency Problem with SBOMs
PEP 770 proposes adding SBOM support to Python packages to improve transparency and catch hidden non-Python dependencies that security tools often miss.
@getcircuit/firestore-test
Advanced tools
<!-- @import "[TOC]" {cmd="toc" depthFrom=2 depthTo=6 orderedList=false} -->
@getcircuit/firestore-test
This package is comprised of a set of utilities to help you write tests for your Firestore code. It mainly provides two significant features:
jest
test environment.The CLI is a simple wrapper around the firebase
CLI, but with the project
set to test
and pointing to this package's firebase.json
configuration file.
# Start the emulator indefinitely
firestore-test emulator:start
# Start the emulator and run a command
firestore-test emulator:exec "<command>"
Example:
# Start the emulator and run jest
firestore-test emulator:exec "jest"
export {
getTestFirestore,
getTestRefSelectors,
getUniqueTestRootRef,
waitForSnapshotListenersInSync,
fakeId,
fakeEmail
FIREBASE_TEST_PROJECT_ID,
} from '@getcircuit/firestore-test'
getTestFirestore()
returns a Firestore
instance that is connected to the emulator.
getTestRefSelectors()
returns a bag of ref selectors already attached to the test Firestore.
getUniqueTestRootRef()
returns a DocumentReference
that is unique to the current test. This is useful when you want to seed data that is specific to the current test without having to seed a whole team.
waitForSnapshotListenersInSync()
returns a promise that resolves when all active snapshot listeners are in sync with the remote firestore. This is useful when you want to guarantee that a test is waiting for a firestore document to be updated.
fakeId()
returns a random id that can be used to seed data.
fakeEmail()
returns a random email that can be used to seed data.
FIREBASE_TEST_PROJECT_ID
is the id of the test project. It's _firebase_test_
currently.
import { getUniqueTestRootRef } from '@getcircuit/firestore-test'
const ref = getUniqueTestRootRef() // DocumentReference to /test-suite/{randomId}
waitForSnapshotListenersInSync()
is a function that returns a promise that resolves when all active snapshot listeners are in sync with the remote firestore. This is useful when you want to guarantee that a test is waiting for a firestore document to be updated.export {
seedDepots,
seedMembers,
seedPatches,
seedPlan,
seedPlanDrivers,
seedRoute,
seedStops,
seedTeam,
seedUsers,
} from '@getcircuit/firestore-test'
Each seeding utility accepts a DeepPartial
version of the data being seeded. Properties that are not explicitly set will fallback to what has been defined inside the src/firebase/data-factories
directory.
Each seeded model may or may not receive an id
property. If no id
is provided, a random one will be generated.
await seedStops(collection, [
{ id: 'stop-1', name: 'Stop 1' }, // Stop 1 will have a known id
{ name: 'Stop 2' }, // Stop 2 will have a random id
])
While there are individual methods to seed users, members, depots, etc, you don't need to use them directly. For example, to seed a team with members and their users, you can use _only_ the seedTeam
method:
import { seedTeam } from '@getcircuit/firestore-test'
const teamSelectors = await seedTeam({
members: [
{ id: 'john', name: 'John Doe' },
{ id: 'jane', name: 'Jane Doe' },
],
})
This will automatically create the users for the specified members, and link their DocumentReferences
accordingly.
We can then select these references with the help of the teamSelectors
object:
teamSelectors.membersRef() // => CollectionReference<Member>
teamSelectors.memberRef('john') // => DocumentReference<Member>
teamSelectors.userRef('john') // => DocumentReference<User>
Since each test is expected to seed its own data, the user
collection is shared between all tests. Creating an user via their member is done by using the same id given to the member. So if two different tests created a member with id john
, both tests would try to write data to the same firestore document.
To prevent this, the seedTeam
function prefixes the user id with the id of the team.
const teamSelectors = await seedTeam({
id: 'team-1',
members: [
{ id: 'john', name: 'John Doe' },
{ id: 'jane', name: 'Jane Doe' },
],
})
teamSelectors.userRef('john').id // => 'team-1-john'
In case one wants to verify the id of a user, the teamSelectors
object provides a userId
method that will return the prefixed id:
teamSelectors.userId('john') // => 'team-1-john'
This is one of the reasons the returned selectors are particularly useful, as this prefixing logic is encapsulated inside them.
As we saw, some of the seeding methods allow you to seed various firestore models at once. While this can speed up writing tests, it can also lead to some confusion when it comes to cross-references.
For example, imagine that we want to seed a team with one member that is assigned to a depot. At the moment of calling seedTeam
, nothing exists, so there's no way to link the member-to-be to the depot-to-be.
const teamSelectors = await seedTeam({
depots: [{ id: 'depot-1' }],
members: [
{
id: 'john',
name: 'John Doe',
depot: ???, // How to link the member to the depot?
},
],
})
There are two ways to solve this:
This is the most concise way, as it allows you to, instead of providing the seeding arguments directly, pass a function that will be called with the selectors. This function should return the data that will be used to seed the model.
const teamSelectors = await seedTeam((teamSelectors) => ({
depots: [{ id: 'depot-1' }],
members: [
{
id: 'john',
name: 'John Doe',
depot: teamSelectors.depotRef('depot-1'),
},
],
}))
This is the most verbose way, but it allows you to have full control over the references. However, you will need to manually link the references and deal with the "user id" conflict mentioned on the About seeding users via members
section.
const teamSelectors = await seedTeam({
depots: [{ id: 'depot-1' }],
})
const userSelectors = await seedUsers({
teamRef: teamSelectors.teamRef,
users: [
{
id: 'john',
name: 'John Doe',
},
],
mapId: (id) => teamSelectors.userId(id),
})
const memberSelectors = await seedMembers({
teamRef: teamSelectors.teamRef,
members: [
{
id: 'john',
name: 'John Doe',
depot: teamSelectors.depotRef('depot-1'),
},
],
})
Each test that connects to the firestore emulator is expected to be self-contained. This means that each test should seed its own data. Cleaning up is not necessary.
import { seedTeam, seedRoute } from '@getcircuit/firestore-test'
test('route is created', async () => {
// Seeds a team with one member
const { teamRef, memberRef, userRef } = await seedTeam({
members: [{ id: 'chris' }],
})
// Seeds a route with linking it to the driver user and member documents
const { routeRef } = await seedRoute(teamRef, {
route: {
driver: userRef('chris'),
member: memberRef('chris'),
},
})
expect('...').toEqual('...')
})
The first thing to do is to create a setup file for jest and reference it in the jest.config
setupFilesAfterEnv
property. The first expression in this file must be an import expression for either:
@getcircuit/firestore-test/setup/browser
if you want the test firebase app to use the browser SDK.@getcircuit/firestore-test/setup/node
if you want the test firebase app to use the node SDK.The import will take care of creating a test firebase application and exposing it via getters. You can then use getTestFirestore
, getTestFirebaseApp
and getTestFirebaseAuth
.
// client-project/tests/setup.js
import '@getcircuit/firestore-test/setup/browser'
import { getTestFirestore } from '@getcircuit/firestore-test'
getTestFirestore() // => Firestore
⚠️ If you want to use the node SDK, be sure to follow the
firebase-types
package's instructions on how to override the firebase types, as the default types for references, snapshots, etc, are associated with the browser SDK.
@getcircuit/firestore-test
provides a JestConfig file to serve as a base for your project's jest
setup:
@getcircuit/firestore-test/jest/config.browser
for a browser environment@getcircuit/firestore-test/jest/config.node
for a node environment// client-project/jest.config.js
const shared = require('@getcircuit/firestore-test/jest/config.browser')
module.exports = {
...shared,
// your custom config
}
In case your Firestore-related test doesn't need a whole team, you can use the getUniqueTestRootRef
method to get a unique document ref pointing to test-suite/{randomId}
. This is useful to avoid having to create a unique collection in each test to prevent conflicts.
import { getUniqueTestRootRef } from '@getcircuit/firestore-test'
test('test 1', async () => {
const rootRef = getUniqueTestRootRef()
const patches = rootRef.collection('patches')
// do stuff with this patches collection
})
test('test 2', async () => {
const rootRef = getUniqueTestRootRef() // this is different from the previous test
const patches = rootRef.collection('patches') // the collections in both tests have the same name, but live in different paths
// do stuff with this patches collection
})
FAQs
<!-- @import "[TOC]" {cmd="toc" depthFrom=2 depthTo=4 orderedList=false} -->
The npm package @getcircuit/firestore-test receives a total of 0 weekly downloads. As such, @getcircuit/firestore-test popularity was classified as not popular.
We found that @getcircuit/firestore-test demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 19 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
PEP 770 proposes adding SBOM support to Python packages to improve transparency and catch hidden non-Python dependencies that security tools often miss.
Security News
Socket CEO Feross Aboukhadijeh discusses open source security challenges, including zero-day attacks and supply chain risks, on the Cyber Security Council podcast.
Security News
Research
Socket researchers uncover how threat actors weaponize Out-of-Band Application Security Testing (OAST) techniques across the npm, PyPI, and RubyGems ecosystems to exfiltrate sensitive data.