Skip to content

tests: Remove need for a separate Firestore project #14632

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 17, 2025
Merged
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
15 changes: 0 additions & 15 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,21 +162,6 @@ Running against the emulator requires:
Currently there are no runnable snippets, but when they are added, they will
have the same requirements.

Google.Cloud.Firestore
----------------------

Firestore cannot currently operate on a Google Cloud project that also has a
Datastore database. You will need a second project:

- Create a project on the [Firebase Console](https://console.firebase.google.com/)
- Enable Firestore on that project (within the Firebase Console)
- Set the `FIRESTORE_TEST_PROJECT` environment variable with that
project ID
- Add the account identified in your service account JSON file as an
owner of the project, or download the service account JSON file for
the Firestore project, and change the `GOOGLE_APPLICATION_CREDENTIALS`
environment variable when you want to run the Firestore tests.

Google.Cloud.Storage.V1
-----------------------

Expand Down
4 changes: 2 additions & 2 deletions apis/Google.Cloud.Firestore.Admin.V1/smoketests.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[
{
"client": "FirestoreAdminClient",
"method": "ListIndexes",
"method": "ListDatabases",
"arguments": {
"parent": "projects/${FIRESTORE_PROJECT_ID}/databases/(default)/collectionGroups/collection"
"parent": "projects/${PROJECT_ID}"
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -28,59 +28,10 @@ internal class Program
{
private static async Task Main()
{
string project = Environment.GetEnvironmentVariable("FIRESTORE_TEST_PROJECT");

await DeleteCollectionsAsync(project);
await DeleteIndexesAsync(project);
string project = Environment.GetEnvironmentVariable("TEST_PROJECT");
await DeleteDatabasesAsync(project);
}

private static async Task DeleteCollectionsAsync(string projectId)
{
var db = await FirestoreDb.CreateAsync(projectId);
var collections = await db.ListRootCollectionsAsync()
.Where(collection => collection.Id.StartsWith("test-", StringComparison.Ordinal))
.ToListAsync();
foreach (var collection in collections)
{
// Log which collections we're deleting here rather than in DeleteCollectionAsync,
// as that's called recursively.
Console.WriteLine($"Deleting collection {collection.Id}");
await DeleteCollectionAsync(collection);
}

async Task DeleteCollectionAsync(CollectionReference collection)
{
var allDocs = await collection.ListDocumentsAsync().ToListAsync();
// Note: one batch per collection is less efficient than filling the batch each time,
// but it's not a big problem.
var batch = db.StartBatch();
foreach (var doc in allDocs)
{
foreach (var child in await doc.ListCollectionsAsync().ToListAsync())
{
await DeleteCollectionAsync(child);
}
batch.Delete(doc);
}
await batch.CommitAsync();
}
}

private static async Task DeleteIndexesAsync(string projectId)
{
var adminClient = await FirestoreAdminClient.CreateAsync();
var indexes = await adminClient.ListIndexesAsync(new CollectionGroupName(projectId, "(default)", "-"))
.Where(index => index.IndexName.CollectionId.StartsWith("test-", StringComparison.Ordinal))
.ToListAsync();

foreach (var index in indexes)
{
Console.WriteLine($"Deleting index {index.Name}");
await adminClient.DeleteIndexAsync(index.IndexName);
}
}

private static async Task DeleteDatabasesAsync(string projectId)
{
var adminClient = await FirestoreAdminClient.CreateAsync();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2017, Google Inc. All rights reserved.
// Copyright 2017, Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -45,6 +45,7 @@ public async Task ConverterRegistry()
var db = new FirestoreDbBuilder
{
ProjectId = _fixture.ProjectId,
DatabaseId = _fixture.DatabaseId,
EmulatorDetection = EmulatorDetection.EmulatorOrProduction,
ConverterRegistry = new ConverterRegistry { new GuidConverter() }
}.Build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ public class FirestoreFixture : CloudProjectFixtureBase, ICollectionFixture<Fire

private const string DatabaseLocation = "us-east1";

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

/// <summary>
Expand Down Expand Up @@ -89,7 +86,7 @@ public class FirestoreFixture : CloudProjectFixtureBase, ICollectionFixture<Fire

private int _uniqueCollectionCounter = 0;

public FirestoreFixture() : base(ProjectEnvironmentVariable)
public FirestoreFixture() : base()
{
RunningOnEmulator = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("FIRESTORE_EMULATOR_HOST"));
AdminClient = FirestoreAdminClient.Create();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2017, Google Inc. All rights reserved.
// Copyright 2017, Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -53,8 +53,9 @@ public class City
public async Task AttributedClassUsage()
{
string projectId = _fixture.ProjectId;
string databaseId = _fixture.DatabaseId;
// Sample: AttributedClassUsage
FirestoreDb db = FirestoreDb.Create(projectId);
FirestoreDb db = new FirestoreDbBuilder { ProjectId = projectId, DatabaseId = databaseId }.Build();

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

// Create a document with a random ID in the "cities" collection.
CollectionReference collection = db.Collection("cities");
Expand All @@ -105,8 +107,9 @@ public async Task DictionaryUsage()
public async Task AnonymousTypeUsage()
{
string projectId = _fixture.ProjectId;
string databaseId = _fixture.DatabaseId;
// Sample: AnonymousTypeUsage
FirestoreDb db = FirestoreDb.Create(projectId);
FirestoreDb db = new FirestoreDbBuilder { ProjectId = projectId, DatabaseId = databaseId }.Build();

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

// Create a document with a random ID in the "scores" collection.
CollectionReference collection = db.Collection("scores");
Expand Down Expand Up @@ -300,9 +304,10 @@ public class ChatRoom
[Fact]
public async Task SnapshotAttributes()
{
// Sample: SnapshotAttributesUsage
string projectId = _fixture.ProjectId;
FirestoreDb db = FirestoreDb.Create(projectId);
string databaseId = _fixture.DatabaseId;
// Sample: SnapshotAttributesUsage
FirestoreDb db = new FirestoreDbBuilder { ProjectId = projectId, DatabaseId = databaseId }.Build();

// Create a document with a random ID in the "rooms" collection.
CollectionReference collection = db.Collection("rooms");
Expand All @@ -326,10 +331,13 @@ public async Task SnapshotAttributes()
public async Task ConverterRegistry()
{
string projectId = _fixture.ProjectId;
string databaseId = _fixture.DatabaseId;

// Sample: ConverterRegistry
FirestoreDb db = new FirestoreDbBuilder
{
ProjectId = projectId,
DatabaseId = databaseId,
ConverterRegistry = new ConverterRegistry
{
new GuidConverter()
Expand All @@ -351,7 +359,8 @@ public async Task ConverterRegistry()
public async Task EnumSerializationByName()
{
string projectId = _fixture.ProjectId;
FirestoreDb db = FirestoreDb.Create(projectId);
string databaseId = _fixture.DatabaseId;
FirestoreDb db = new FirestoreDbBuilder { ProjectId = projectId, DatabaseId = databaseId }.Build();
CollectionReference collection = db.Collection("enums");
DocumentReference document = await collection.AddAsync(new { Value = SerializedByName.FirstValue });
DocumentSnapshot snapshot = await document.GetSnapshotAsync();
Expand All @@ -374,7 +383,8 @@ public enum SerializedByName
public async Task ValueTuples()
{
string projectId = _fixture.ProjectId;
FirestoreDb db = FirestoreDb.Create(projectId);
string databaseId = _fixture.DatabaseId;
FirestoreDb db = new FirestoreDbBuilder { ProjectId = projectId, DatabaseId = databaseId }.Build();
CollectionReference collection = db.Collection("snippet-users");
Company company = new Company
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2017, Google Inc. All rights reserved.
// Copyright 2017, Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -12,26 +12,44 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Api.Gax.Grpc;
using Google.Api.Gax.ResourceNames;
using Google.Api.Gax;
using Google.Cloud.ClientTesting;
using Google.Cloud.Firestore.Admin.V1;
using System;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
using static Google.Cloud.Firestore.Admin.V1.Database.Types;

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

public FirestoreDb FirestoreDb { get; }

private const string DatabaseLocation = "us-east1";

/// <summary>
/// A randomly generated prefix for collections.
/// (This isn't strictly necessary as we now use a new database each time, but it doesn't hurt,
/// and we could allow an existing database to be specified for debugging purposes.)
/// </summary>
public string CollectionPrefix { get; }

/// <summary>
/// The database ID to use.
/// </summary>
public string DatabaseId { get; }

/// <summary>
/// A collection with <see cref="HighScore"/> data in. Don't modify in tests!
/// </summary>
Expand All @@ -44,12 +62,15 @@ public class FirestoreFixture : CloudProjectFixtureBase, ICollectionFixture<Fire

private int _uniqueCollectionCounter = 0;

public FirestoreFixture() : base(ProjectEnvironmentVariable)
public bool RunningOnEmulator { get; }

public FirestoreFixture() : base()
{
// Currently, only the default database is supported... so we create all our collections with a randomly-generated prefix.
// When multiple databases are supported, we'll create a new one per test run.
RunningOnEmulator = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("FIRESTORE_EMULATOR_HOST"));
DatabaseId = RunningOnEmulator ? null : IdGenerator.FromDateTime(prefix: "test-");
Task.Run(() => CreateDatabaseAsync(DatabaseId)).Wait();
CollectionPrefix = IdGenerator.FromGuid(prefix: "test-");
FirestoreDb = FirestoreDb.Create(ProjectId);
FirestoreDb = new FirestoreDbBuilder { ProjectId = ProjectId, DatabaseId = DatabaseId }.Build();
NonQueryCollection = FirestoreDb.Collection(CollectionPrefix + "-non-query");
HighScoreCollection = FirestoreDb.Collection(CollectionPrefix + "-high-scores");
}
Expand All @@ -64,7 +85,21 @@ public CollectionReference CreateUniqueCollection()
return FirestoreDb.Collection($"{CollectionPrefix}-unique-{counter}");
}

// No clean-up for now. With multiple-database support, we'll create a database
// on construction and delete it here.
/// <summary>
/// Creates a new Firestore database.
/// </summary>
public async Task CreateDatabaseAsync(string databaseId)
{
// Tests which require different databases should be skipped; this is protection against forgetting that.
Assert.False(RunningOnEmulator);
var client = FirestoreAdminClient.Create();
var operation = await client.CreateDatabaseAsync(
new ProjectName(ProjectId),
new Database { LocationId = DatabaseLocation, Type = DatabaseType.FirestoreNative },
databaseId);
await operation.PollUntilCompletedAsync(AdminOperationPollSettings, CallSettings.FromExpiration(Expiration.FromTimeout(TimeSpan.FromMinutes(5))));
}

// No clean-up here: CleanTestData deletes test collections, indexes and databases.
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2017, Google Inc. All rights reserved.
// Copyright 2017, Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -33,8 +33,9 @@ public class IndexSnippets
public async Task GettingStarted()
{
string projectId = _fixture.ProjectId;
string databaseId = _fixture.DatabaseId;
// Sample: GettingStarted
FirestoreDb db = FirestoreDb.Create(projectId);
FirestoreDb db = new FirestoreDbBuilder { ProjectId = projectId, DatabaseId = databaseId }.Build();

// Create a document with a random ID in the "users" collection.
CollectionReference collection = db.Collection("users");
Expand Down Expand Up @@ -74,6 +75,7 @@ public async Task GettingStarted()
public void DetectEmulator()
{
string projectId = _fixture.ProjectId;
string databaseId = _fixture.DatabaseId;
// Sample: EmulatorDetection
FirestoreDb db = new FirestoreDbBuilder
{
Expand Down
Loading