diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eacfcda4..901fad26 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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: diff --git a/firebase.go b/firebase.go index 9373ae23..33a0ae65 100644 --- a/firebase.go +++ b/firebase.go @@ -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. diff --git a/firebase_test.go b/firebase_test.go index 2699146a..ddc130a1 100644 --- a/firebase_test.go +++ b/firebase_test.go @@ -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) @@ -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) { diff --git a/integration/firestore/firestore_test.go b/integration/firestore/firestore_test.go index 3e6d2da9..3300f882 100644 --- a/integration/firestore/firestore_test.go +++ b/integration/firestore/firestore_test.go @@ -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 { @@ -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) { + 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) + } }