Skip to content

Commit a0aa473

Browse files
committed
tests: Remove need for a separate Firestore project
We still can't have a *default* database for Firestore and Datastore in the same project, so this changes all our Firestore integration tests and snippets to use a named database instead. (Changing Datastore would have been more disruptive.)
1 parent 5bd8099 commit a0aa473

File tree

11 files changed

+100
-111
lines changed

11 files changed

+100
-111
lines changed

TESTING.md

-15
Original file line numberDiff line numberDiff line change
@@ -162,21 +162,6 @@ Running against the emulator requires:
162162
Currently there are no runnable snippets, but when they are added, they will
163163
have the same requirements.
164164

165-
Google.Cloud.Firestore
166-
----------------------
167-
168-
Firestore cannot currently operate on a Google Cloud project that also has a
169-
Datastore database. You will need a second project:
170-
171-
- Create a project on the [Firebase Console](https://console.firebase.google.com/)
172-
- Enable Firestore on that project (within the Firebase Console)
173-
- Set the `FIRESTORE_TEST_PROJECT` environment variable with that
174-
project ID
175-
- Add the account identified in your service account JSON file as an
176-
owner of the project, or download the service account JSON file for
177-
the Firestore project, and change the `GOOGLE_APPLICATION_CREDENTIALS`
178-
environment variable when you want to run the Firestore tests.
179-
180165
Google.Cloud.Storage.V1
181166
-----------------------
182167

Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
[
22
{
33
"client": "FirestoreAdminClient",
4-
"method": "ListIndexes",
4+
"method": "ListDatabases",
55
"arguments": {
6-
"parent": "projects/${FIRESTORE_PROJECT_ID}/databases/(default)/collectionGroups/collection"
6+
"parent": "projects/${PROJECT_ID}"
77
}
88
}
99
]

apis/Google.Cloud.Firestore/Google.Cloud.Firestore.CleanTestData/Program.cs

+1-50
Original file line numberDiff line numberDiff line change
@@ -28,59 +28,10 @@ internal class Program
2828
{
2929
private static async Task Main()
3030
{
31-
string project = Environment.GetEnvironmentVariable("FIRESTORE_TEST_PROJECT");
32-
33-
await DeleteCollectionsAsync(project);
34-
await DeleteIndexesAsync(project);
31+
string project = Environment.GetEnvironmentVariable("TEST_PROJECT");
3532
await DeleteDatabasesAsync(project);
3633
}
3734

38-
private static async Task DeleteCollectionsAsync(string projectId)
39-
{
40-
var db = await FirestoreDb.CreateAsync(projectId);
41-
var collections = await db.ListRootCollectionsAsync()
42-
.Where(collection => collection.Id.StartsWith("test-", StringComparison.Ordinal))
43-
.ToListAsync();
44-
foreach (var collection in collections)
45-
{
46-
// Log which collections we're deleting here rather than in DeleteCollectionAsync,
47-
// as that's called recursively.
48-
Console.WriteLine($"Deleting collection {collection.Id}");
49-
await DeleteCollectionAsync(collection);
50-
}
51-
52-
async Task DeleteCollectionAsync(CollectionReference collection)
53-
{
54-
var allDocs = await collection.ListDocumentsAsync().ToListAsync();
55-
// Note: one batch per collection is less efficient than filling the batch each time,
56-
// but it's not a big problem.
57-
var batch = db.StartBatch();
58-
foreach (var doc in allDocs)
59-
{
60-
foreach (var child in await doc.ListCollectionsAsync().ToListAsync())
61-
{
62-
await DeleteCollectionAsync(child);
63-
}
64-
batch.Delete(doc);
65-
}
66-
await batch.CommitAsync();
67-
}
68-
}
69-
70-
private static async Task DeleteIndexesAsync(string projectId)
71-
{
72-
var adminClient = await FirestoreAdminClient.CreateAsync();
73-
var indexes = await adminClient.ListIndexesAsync(new CollectionGroupName(projectId, "(default)", "-"))
74-
.Where(index => index.IndexName.CollectionId.StartsWith("test-", StringComparison.Ordinal))
75-
.ToListAsync();
76-
77-
foreach (var index in indexes)
78-
{
79-
Console.WriteLine($"Deleting index {index.Name}");
80-
await adminClient.DeleteIndexAsync(index.IndexName);
81-
}
82-
}
83-
8435
private static async Task DeleteDatabasesAsync(string projectId)
8536
{
8637
var adminClient = await FirestoreAdminClient.CreateAsync();

apis/Google.Cloud.Firestore/Google.Cloud.Firestore.IntegrationTests/DataModelTest.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2017, Google Inc. All rights reserved.
1+
// Copyright 2017, Google Inc. All rights reserved.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -45,6 +45,7 @@ public async Task ConverterRegistry()
4545
var db = new FirestoreDbBuilder
4646
{
4747
ProjectId = _fixture.ProjectId,
48+
DatabaseId = _fixture.DatabaseId,
4849
EmulatorDetection = EmulatorDetection.EmulatorOrProduction,
4950
ConverterRegistry = new ConverterRegistry { new GuidConverter() }
5051
}.Build();

apis/Google.Cloud.Firestore/Google.Cloud.Firestore.IntegrationTests/FirestoreFixture.cs

+1-4
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@ public class FirestoreFixture : CloudProjectFixtureBase, ICollectionFixture<Fire
4646

4747
private const string DatabaseLocation = "us-east1";
4848

49-
// This is not the test project environment variable used by other integration tests,
50-
// as Datastore and Firestore can't both be active in the same project.
51-
private const string ProjectEnvironmentVariable = "FIRESTORE_TEST_PROJECT";
5249
public FirestoreDb FirestoreDb { get; }
5350

5451
/// <summary>
@@ -89,7 +86,7 @@ public class FirestoreFixture : CloudProjectFixtureBase, ICollectionFixture<Fire
8986

9087
private int _uniqueCollectionCounter = 0;
9188

92-
public FirestoreFixture() : base(ProjectEnvironmentVariable)
89+
public FirestoreFixture() : base()
9390
{
9491
RunningOnEmulator = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("FIRESTORE_EMULATOR_HOST"));
9592
AdminClient = FirestoreAdminClient.Create();

apis/Google.Cloud.Firestore/Google.Cloud.Firestore.Snippets/DataModelSnippets.cs

+18-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2017, Google Inc. All rights reserved.
1+
// Copyright 2017, Google Inc. All rights reserved.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -53,8 +53,9 @@ public class City
5353
public async Task AttributedClassUsage()
5454
{
5555
string projectId = _fixture.ProjectId;
56+
string databaseId = _fixture.DatabaseId;
5657
// Sample: AttributedClassUsage
57-
FirestoreDb db = FirestoreDb.Create(projectId);
58+
FirestoreDb db = new FirestoreDbBuilder { ProjectId = projectId, DatabaseId = databaseId }.Build();
5859

5960
// Create a document with a random ID in the "cities" collection.
6061
CollectionReference collection = db.Collection("cities");
@@ -79,8 +80,9 @@ public async Task AttributedClassUsage()
7980
public async Task DictionaryUsage()
8081
{
8182
string projectId = _fixture.ProjectId;
83+
string databaseId = _fixture.DatabaseId;
8284
// Sample: DictionaryUsage
83-
FirestoreDb db = FirestoreDb.Create(projectId);
85+
FirestoreDb db = new FirestoreDbBuilder { ProjectId = projectId, DatabaseId = databaseId }.Build();
8486

8587
// Create a document with a random ID in the "cities" collection.
8688
CollectionReference collection = db.Collection("cities");
@@ -105,8 +107,9 @@ public async Task DictionaryUsage()
105107
public async Task AnonymousTypeUsage()
106108
{
107109
string projectId = _fixture.ProjectId;
110+
string databaseId = _fixture.DatabaseId;
108111
// Sample: AnonymousTypeUsage
109-
FirestoreDb db = FirestoreDb.Create(projectId);
112+
FirestoreDb db = new FirestoreDbBuilder { ProjectId = projectId, DatabaseId = databaseId }.Build();
110113

111114
// Create a document with a random ID in the "cities" collection.
112115
CollectionReference collection = db.Collection("cities");
@@ -148,7 +151,8 @@ public class HighScore
148151
public async Task AnonymousTypeSentinel()
149152
{
150153
string projectId = _fixture.ProjectId;
151-
FirestoreDb db = FirestoreDb.Create(projectId);
154+
string databaseId = _fixture.DatabaseId;
155+
FirestoreDb db = new FirestoreDbBuilder { ProjectId = projectId, DatabaseId = databaseId }.Build();
152156

153157
// Create a document with a random ID in the "scores" collection.
154158
CollectionReference collection = db.Collection("scores");
@@ -300,9 +304,10 @@ public class ChatRoom
300304
[Fact]
301305
public async Task SnapshotAttributes()
302306
{
303-
// Sample: SnapshotAttributesUsage
304307
string projectId = _fixture.ProjectId;
305-
FirestoreDb db = FirestoreDb.Create(projectId);
308+
string databaseId = _fixture.DatabaseId;
309+
// Sample: SnapshotAttributesUsage
310+
FirestoreDb db = new FirestoreDbBuilder { ProjectId = projectId, DatabaseId = databaseId }.Build();
306311

307312
// Create a document with a random ID in the "rooms" collection.
308313
CollectionReference collection = db.Collection("rooms");
@@ -326,6 +331,8 @@ public async Task SnapshotAttributes()
326331
public async Task ConverterRegistry()
327332
{
328333
string projectId = _fixture.ProjectId;
334+
string databaseId = _fixture.DatabaseId;
335+
329336
// Sample: ConverterRegistry
330337
FirestoreDb db = new FirestoreDbBuilder
331338
{
@@ -351,7 +358,8 @@ public async Task ConverterRegistry()
351358
public async Task EnumSerializationByName()
352359
{
353360
string projectId = _fixture.ProjectId;
354-
FirestoreDb db = FirestoreDb.Create(projectId);
361+
string databaseId = _fixture.DatabaseId;
362+
FirestoreDb db = new FirestoreDbBuilder { ProjectId = projectId, DatabaseId = databaseId }.Build();
355363
CollectionReference collection = db.Collection("enums");
356364
DocumentReference document = await collection.AddAsync(new { Value = SerializedByName.FirstValue });
357365
DocumentSnapshot snapshot = await document.GetSnapshotAsync();
@@ -374,7 +382,8 @@ public enum SerializedByName
374382
public async Task ValueTuples()
375383
{
376384
string projectId = _fixture.ProjectId;
377-
FirestoreDb db = FirestoreDb.Create(projectId);
385+
string databaseId = _fixture.DatabaseId;
386+
FirestoreDb db = new FirestoreDbBuilder { ProjectId = projectId, DatabaseId = databaseId }.Build();
378387
CollectionReference collection = db.Collection("snippet-users");
379388
Company company = new Company
380389
{

apis/Google.Cloud.Firestore/Google.Cloud.Firestore.Snippets/FirestoreFixture.cs

+45-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2017, Google Inc. All rights reserved.
1+
// Copyright 2017, Google Inc. All rights reserved.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -12,26 +12,44 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using Google.Api.Gax.Grpc;
16+
using Google.Api.Gax.ResourceNames;
17+
using Google.Api.Gax;
1518
using Google.Cloud.ClientTesting;
19+
using Google.Cloud.Firestore.Admin.V1;
1620
using System;
1721
using System.Threading;
22+
using System.Threading.Tasks;
1823
using Xunit;
24+
using static Google.Cloud.Firestore.Admin.V1.Database.Types;
1925

2026
namespace Google.Cloud.Firestore.Snippets
2127
{
2228
[CollectionDefinition(nameof(FirestoreFixture))]
2329
public class FirestoreFixture : CloudProjectFixtureBase, ICollectionFixture<FirestoreFixture>
2430
{
25-
// This is not the test project environment variable used by other integration tests,
26-
// as Datastore and Firestore can't both be active in the same project.
27-
private const string ProjectEnvironmentVariable = "FIRESTORE_TEST_PROJECT";
31+
// Creating databases and indexes can take a while.
32+
// We don't want to wait *forever* (which would be the behavior of default poll settings)
33+
// but we need to have a timeout of more than a minute.
34+
private static readonly PollSettings AdminOperationPollSettings =
35+
new PollSettings(expiration: Expiration.FromTimeout(TimeSpan.FromMinutes(5)), delay: TimeSpan.FromSeconds(5));
36+
2837
public FirestoreDb FirestoreDb { get; }
2938

39+
private const string DatabaseLocation = "us-east1";
40+
3041
/// <summary>
3142
/// A randomly generated prefix for collections.
43+
/// (This isn't strictly necessary as we now use a new database each time, but it doesn't hurt,
44+
/// and we could allow an existing database to be specified for debugging purposes.)
3245
/// </summary>
3346
public string CollectionPrefix { get; }
3447

48+
/// <summary>
49+
/// The database ID to use.
50+
/// </summary>
51+
public string DatabaseId { get; }
52+
3553
/// <summary>
3654
/// A collection with <see cref="HighScore"/> data in. Don't modify in tests!
3755
/// </summary>
@@ -44,10 +62,13 @@ public class FirestoreFixture : CloudProjectFixtureBase, ICollectionFixture<Fire
4462

4563
private int _uniqueCollectionCounter = 0;
4664

47-
public FirestoreFixture() : base(ProjectEnvironmentVariable)
65+
public bool RunningOnEmulator { get; }
66+
67+
public FirestoreFixture() : base()
4868
{
49-
// Currently, only the default database is supported... so we create all our collections with a randomly-generated prefix.
50-
// When multiple databases are supported, we'll create a new one per test run.
69+
RunningOnEmulator = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("FIRESTORE_EMULATOR_HOST"));
70+
DatabaseId = RunningOnEmulator ? null : IdGenerator.FromDateTime(prefix: "test-");
71+
Task.Run(() => CreateDatabaseAsync(DatabaseId)).Wait();
5172
CollectionPrefix = IdGenerator.FromGuid(prefix: "test-");
5273
FirestoreDb = FirestoreDb.Create(ProjectId);
5374
NonQueryCollection = FirestoreDb.Collection(CollectionPrefix + "-non-query");
@@ -64,7 +85,22 @@ public CollectionReference CreateUniqueCollection()
6485
return FirestoreDb.Collection($"{CollectionPrefix}-unique-{counter}");
6586
}
6687

67-
// No clean-up for now. With multiple-database support, we'll create a database
68-
// on construction and delete it here.
88+
89+
/// <summary>
90+
/// Creates a new Firestore database.
91+
/// </summary>
92+
public async Task CreateDatabaseAsync(string databaseId)
93+
{
94+
// Tests which require different databases should be skipped; this is protection against forgetting that.
95+
Assert.False(RunningOnEmulator);
96+
var client = FirestoreAdminClient.Create();
97+
var operation = await client.CreateDatabaseAsync(
98+
new ProjectName(ProjectId),
99+
new Database { LocationId = DatabaseLocation, Type = DatabaseType.FirestoreNative },
100+
databaseId);
101+
await operation.PollUntilCompletedAsync(AdminOperationPollSettings, CallSettings.FromExpiration(Expiration.FromTimeout(TimeSpan.FromMinutes(5))));
102+
}
103+
104+
// No clean-up here: CleanTestData deletes test collections, indexes and databases.
69105
}
70106
}

apis/Google.Cloud.Firestore/Google.Cloud.Firestore.Snippets/IndexSnippets.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2017, Google Inc. All rights reserved.
1+
// Copyright 2017, Google Inc. All rights reserved.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -33,8 +33,9 @@ public class IndexSnippets
3333
public async Task GettingStarted()
3434
{
3535
string projectId = _fixture.ProjectId;
36+
string databaseId = _fixture.DatabaseId;
3637
// Sample: GettingStarted
37-
FirestoreDb db = FirestoreDb.Create(projectId);
38+
FirestoreDb db = new FirestoreDbBuilder { ProjectId = projectId, DatabaseId = databaseId }.Build();
3839

3940
// Create a document with a random ID in the "users" collection.
4041
CollectionReference collection = db.Collection("users");
@@ -74,6 +75,7 @@ public async Task GettingStarted()
7475
public void DetectEmulator()
7576
{
7677
string projectId = _fixture.ProjectId;
78+
string databaseId = _fixture.DatabaseId;
7779
// Sample: EmulatorDetection
7880
FirestoreDb db = new FirestoreDbBuilder
7981
{

0 commit comments

Comments
 (0)