Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,12 @@ Set up your Firebase project as follows:

2. Enable Firestore:
1. Go to the Firebase Console, and select **Firestore Database** from the **Build** menu.
2. Click on the **Create database** button. You can choose to set up Firestore either in
the production mode or in the test mode.
2. Click on the **Create database** button and create a default database. You can choose
to set up Firestore either in the production mode or in the test mode.
> **Note:** Integration tests are run against both the default database and an additional
database named "testing-database".
3. After the default database is created, click the **Add database** button to create a
second database named "testing-database".


3. Enable Realtime Database:
Expand Down
8 changes: 7 additions & 1 deletion firebase.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,16 @@ func (a *App) Storage(ctx context.Context) (*storage.Client, error) {
// Firestore returns a new firestore.Client instance from the https://godoc.org/cloud.google.com/go/firestore
// package.
func (a *App) Firestore(ctx context.Context) (*firestore.Client, error) {
return a.FirestoreWithDatabaseID(ctx, firestore.DefaultDatabaseID)
}

// FirestoreWithDatabaseID returns a new firestore.Client instance with the specified named database from the
// https://godoc.org/cloud.google.com/go/firestore package.
func (a *App) FirestoreWithDatabaseID(ctx context.Context, databaseID string) (*firestore.Client, error) {
if a.projectID == "" {
return nil, errors.New("project id is required to access Firestore")
}
return firestore.NewClient(ctx, a.projectID, a.opts...)
return firestore.NewClientWithDatabase(ctx, a.projectID, databaseID, a.opts...)
}

// InstanceID returns an instance of iid.Client.
Expand Down
16 changes: 16 additions & 0 deletions firebase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,18 @@ func TestFirestore(t *testing.T) {
}
}

func TestFirestoreWithDatabaseID(t *testing.T) {
ctx := context.Background()
app, err := NewApp(ctx, nil, option.WithCredentialsFile("testdata/service_account.json"))
if err != nil {
t.Fatal(err)
}

if c, err := app.FirestoreWithDatabaseID(ctx, "other-db"); c == nil || err != nil {
t.Errorf("FirestoreWithDatabaseID() = (%v, %v); want (client, nil)", c, err)
}
}

func TestFirestoreWithProjectID(t *testing.T) {
verify := func(varName string) {
current := os.Getenv(varName)
Expand Down Expand Up @@ -336,6 +348,10 @@ func TestFirestoreWithNoProjectID(t *testing.T) {
if c, err := app.Firestore(ctx); c != nil || err == nil {
t.Errorf("Firestore() = (%v, %v); want (nil, error)", c, err)
}

if c, err := app.FirestoreWithDatabaseID(ctx, "other-db"); c != nil || err == nil {
t.Errorf("FirestoreWithDatabaseID() = (%v, %v); want (nil, error)", c, err)
}
}

func TestInstanceID(t *testing.T) {
Expand Down
116 changes: 105 additions & 11 deletions integration/firestore/firestore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,42 @@ package firestore

import (
"context"
"flag"
"log"
"os"
"reflect"
"testing"

"firebase.google.com/go/v4/integration/internal"
)

func TestFirestore(t *testing.T) {
const testDatabaseID = "testing-database"

var (
cityData = map[string]interface{}{
"name": "Mountain View",
"country": "USA",
"population": int64(77846),
"capital": false,
}
movieData = map[string]interface{}{
"Name": "Interstellar",
"Year": int64(2014),
"Runtime": "2h 49m",
"Academy Award Winner": true,
}
)

func TestMain(m *testing.M) {
flag.Parse()
if testing.Short() {
log.Println("skipping Firestore integration tests in short mode.")
return
os.Exit(0)
}
os.Exit(m.Run())
}

func TestFirestore(t *testing.T) {
ctx := context.Background()
app, err := internal.NewTestApp(ctx, nil)
if err != nil {
Expand All @@ -40,23 +64,93 @@ func TestFirestore(t *testing.T) {
}

doc := client.Collection("cities").Doc("Mountain View")
data := map[string]interface{}{
"name": "Mountain View",
"country": "USA",
"population": int64(77846),
"capital": false,
if _, err := doc.Set(ctx, cityData); err != nil {
t.Fatal(err)
}
defer doc.Delete(ctx)

snap, err := doc.Get(ctx)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(snap.Data(), cityData) {
t.Errorf("Get() = %v; want %v", snap.Data(), cityData)
}
}

func TestFirestoreWithDatabaseID(t *testing.T) {
ctx := context.Background()
app, err := internal.NewTestApp(ctx, nil)
if err != nil {
t.Fatal(err)
}

// This test requires the target non-default database to exist in the project.
// If it doesn't exist, this test will fail.
client, err := app.FirestoreWithDatabaseID(ctx, testDatabaseID)
if err != nil {
t.Fatal(err)
}
if _, err := doc.Set(ctx, data); err != nil {

doc := client.Collection("cities").NewDoc()
if _, err := doc.Set(ctx, cityData); err != nil {
t.Fatal(err)
}
defer doc.Delete(ctx)

snap, err := doc.Get(ctx)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(snap.Data(), data) {
t.Errorf("Get() = %v; want %v", snap.Data(), data)
if !reflect.DeepEqual(snap.Data(), cityData) {
t.Errorf("Get() = %v; want %v", snap.Data(), cityData)
}
if _, err := doc.Delete(ctx); err != nil {
}

func TestFirestoreMultiDB(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels repetitive. Doesn't TestFirestoreWithDatabaseID and TestFirestore cover this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test in for verifying that two databases clients can be opened and modified at once with no impact on the other.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. We can keep this, but as a clarification, to me this feels like we are trying to test the cloud firestore SDK here. This should be a more fitting test for the Firestore SDK and slightly beyond the Admin SDK scope. The Admin SDK should test the part where we are setting the database ID in the Firestore SDK and everything beyond that should be tested in the Firestore SDK itself (which I think they do).

ctx := context.Background()
app, err := internal.NewTestApp(ctx, nil)
if err != nil {
t.Fatal(err)
}

cityClient, err := app.Firestore(ctx)
if err != nil {
t.Fatal(err)
}
// This test requires the target non-default database to exist in the project.
// If it doesn't exist, this test will fail.
movieClient, err := app.FirestoreWithDatabaseID(ctx, testDatabaseID)
if err != nil {
t.Fatal(err)
}

cityDoc := cityClient.Collection("cities").NewDoc()
movieDoc := movieClient.Collection("movies").NewDoc()

if _, err := cityDoc.Set(ctx, cityData); err != nil {
t.Fatal(err)
}
defer cityDoc.Delete(ctx)

if _, err := movieDoc.Set(ctx, movieData); err != nil {
t.Fatal(err)
}
defer movieDoc.Delete(ctx)

citySnap, err := cityDoc.Get(ctx)
if err != nil {
t.Fatal(err)
}
movieSnap, err := movieDoc.Get(ctx)
if err != nil {
t.Fatal(err)
}

if !reflect.DeepEqual(citySnap.Data(), cityData) {
t.Errorf("City Get() = %v; want %v", citySnap.Data(), cityData)
}
if !reflect.DeepEqual(movieSnap.Data(), movieData) {
t.Errorf("Movie Get() = %v; want %v", movieSnap.Data(), movieData)
}
}
Loading