Skip to content

Commit

Permalink
Firestore: Fix tests (hashicorp#9840) (hashicorp#17070)
Browse files Browse the repository at this point in the history
There's a whole bunch of different constraints that have to be met
for these tests to pass consistently.

* Backup schedules need to be run in a project with billing enabled,
  and the database needs a deletion policy to ensure it is properly swept.
* The default-database examples were marked as skip-test.
  They're valuable as examples but not really as tests - there's nothing
  Terraform-specific about them worth testing - and the Field tests
  will fail if default database creation doesn't work in Terraform
* The Document tests need to run against the (default) database due
  to an existing bug
  (hashicorp#15550).
  So they will now create their own project and their own (default)
  database.
* The Field examples are following the same pattern as
  the backup schedule examples.
* The Firestore-native index example is using the inband project
  creation method, because then we can use the (default) database,
  which means we can use the Document resource (see the above-mentioned bug),
  which means that we don't need to use a manually-created project (because creating
  a Document implicitly creates its collection).
* The Datastore mode Index example can't take that same approach, because it doesn't
  seem like the Document resource works on datastore-mode databases. So we will need
  to use the manually-created FIRESTORE_PROJECT_NAME project for that one, and it will
  need to have a Datastore mode database manually baked into it.

 This exercise has identified a fair number of places for improvement to the Firestore
 Terraform story, but as a stopgap to get the tests passing again, this will have to do.
[upstream:003fa68204d7bcff11e5153364e8c6eb950155ed]

Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
modular-magician authored Jan 22, 2024
1 parent d9d02fd commit 490ea79
Show file tree
Hide file tree
Showing 14 changed files with 509 additions and 155 deletions.
2 changes: 2 additions & 0 deletions .changelog/9840.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
```release-note:none
```
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ For a daily backup recurrence, set this to a value up to 7 days. If you set a we
Type: schema.TypeString,
Computed: true,
Description: `The unique backup schedule identifier across all locations and databases for the given project. Format:
'projects/{{project}}/databases/{{database}}/backupSchedules/{{backupSchedule}}`,
'projects/{{project}}/databases/{{database}}/backupSchedules/{{backupSchedule}}'`,
},
"project": {
Type: schema.TypeString,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ func TestAccFirestoreBackupSchedule_firestoreBackupScheduleDailyExample(t *testi
t.Parallel()

context := map[string]interface{}{
"project_id": envvar.GetTestFirestoreProjectFromEnv(t),
"random_suffix": acctest.RandString(t, 10),
"project_id": envvar.GetTestProjectFromEnv(),
"delete_protection_state": "DELETE_PROTECTION_DISABLED",
"random_suffix": acctest.RandString(t, 10),
}

acctest.VcrTest(t, resource.TestCase{
Expand All @@ -59,8 +60,19 @@ func TestAccFirestoreBackupSchedule_firestoreBackupScheduleDailyExample(t *testi

func testAccFirestoreBackupSchedule_firestoreBackupScheduleDailyExample(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_firestore_database" "database" {
project = "%{project_id}"
name = "tf-test-database-id%{random_suffix}"
location_id = "nam5"
type = "FIRESTORE_NATIVE"
delete_protection_state = "%{delete_protection_state}"
deletion_policy = "DELETE"
}
resource "google_firestore_backup_schedule" "daily-backup" {
project = "%{project_id}"
project = "%{project_id}"
database = google_firestore_database.database.name
retention = "604800s" // 7 days (maximum possible value for daily backups)
Expand All @@ -73,8 +85,9 @@ func TestAccFirestoreBackupSchedule_firestoreBackupScheduleWeeklyExample(t *test
t.Parallel()

context := map[string]interface{}{
"project_id": envvar.GetTestFirestoreProjectFromEnv(t),
"random_suffix": acctest.RandString(t, 10),
"project_id": envvar.GetTestProjectFromEnv(),
"delete_protection_state": "DELETE_PROTECTION_DISABLED",
"random_suffix": acctest.RandString(t, 10),
}

acctest.VcrTest(t, resource.TestCase{
Expand All @@ -97,9 +110,19 @@ func TestAccFirestoreBackupSchedule_firestoreBackupScheduleWeeklyExample(t *test

func testAccFirestoreBackupSchedule_firestoreBackupScheduleWeeklyExample(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_firestore_database" "database" {
project = "%{project_id}"
name = "tf-test-database-id%{random_suffix}"
location_id = "nam5"
type = "FIRESTORE_NATIVE"
delete_protection_state = "%{delete_protection_state}"
deletion_policy = "DELETE"
}
resource "google_firestore_backup_schedule" "weekly-backup" {
project = "%{project_id}"
database = "(default)"
database = google_firestore_database.database.name
retention = "8467200s" // 14 weeks (maximum possible value for weekly backups)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,50 +31,6 @@ import (
transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport"
)

func TestAccFirestoreDatabase_firestoreDefaultDatabaseExample(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"project_id": envvar.GetTestProjectFromEnv(),
"delete_protection_state": "DELETE_PROTECTION_DISABLED",
"random_suffix": acctest.RandString(t, 10),
}

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
ExternalProviders: map[string]resource.ExternalProvider{
"random": {},
"time": {},
},
CheckDestroy: testAccCheckFirestoreDatabaseDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccFirestoreDatabase_firestoreDefaultDatabaseExample(context),
},
{
ResourceName: "google_firestore_database.database",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"project", "etag", "deletion_policy"},
},
},
})
}

func testAccFirestoreDatabase_firestoreDefaultDatabaseExample(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_firestore_database" "database" {
project = "%{project_id}"
name = "(default)"
location_id = "nam5"
type = "FIRESTORE_NATIVE"
delete_protection_state = "%{delete_protection_state}"
deletion_policy = "DELETE"
}
`, context)
}

func TestAccFirestoreDatabase_firestoreDatabaseExample(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -106,7 +62,7 @@ func testAccFirestoreDatabase_firestoreDatabaseExample(context map[string]interf
return acctest.Nprintf(`
resource "google_firestore_database" "database" {
project = "%{project_id}"
name = "tf-test-example-database-id%{random_suffix}"
name = "tf-test-database-id%{random_suffix}"
location_id = "nam5"
type = "FIRESTORE_NATIVE"
concurrency_mode = "OPTIMISTIC"
Expand Down Expand Up @@ -149,7 +105,7 @@ func testAccFirestoreDatabase_firestoreDatabaseInDatastoreModeExample(context ma
return acctest.Nprintf(`
resource "google_firestore_database" "datastore_mode_database" {
project = "%{project_id}"
name = "tf-test-example-database-id%{random_suffix}"
name = "tf-test-database-id%{random_suffix}"
location_id = "nam5"
type = "DATASTORE_MODE"
concurrency_mode = "OPTIMISTIC"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,18 @@ func TestAccFirestoreDocument_firestoreDocumentBasicExample(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"project_id": envvar.GetTestFirestoreProjectFromEnv(t),
"org_id": envvar.GetTestOrgFromEnv(t),
"random_suffix": acctest.RandString(t, 10),
}

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckFirestoreDocumentDestroyProducer(t),
ExternalProviders: map[string]resource.ExternalProvider{
"random": {},
"time": {},
},
CheckDestroy: testAccCheckFirestoreDocumentDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccFirestoreDocument_firestoreDocumentBasicExample(context),
Expand All @@ -59,8 +63,38 @@ func TestAccFirestoreDocument_firestoreDocumentBasicExample(t *testing.T) {

func testAccFirestoreDocument_firestoreDocumentBasicExample(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_project" "project" {
project_id = "tf-test-project-id%{random_suffix}"
name = "tf-test-project-id%{random_suffix}"
org_id = "%{org_id}"
}
resource "time_sleep" "wait_60_seconds" {
depends_on = [google_project.project]
create_duration = "60s"
}
resource "google_project_service" "firestore" {
project = google_project.project.project_id
service = "firestore.googleapis.com"
# Needed for CI tests for permissions to propagate, should not be needed for actual usage
depends_on = [time_sleep.wait_60_seconds]
}
resource "google_firestore_database" "database" {
project = google_project.project.project_id
name = "(default)"
location_id = "nam5"
type = "FIRESTORE_NATIVE"
depends_on = [google_project_service.firestore]
}
resource "google_firestore_document" "mydoc" {
project = "%{project_id}"
project = google_project.project.project_id
database = google_firestore_database.database.name
collection = "somenewcollection"
document_id = "tf-test-my-doc-id%{random_suffix}"
fields = "{\"something\":{\"mapValue\":{\"fields\":{\"akey\":{\"stringValue\":\"avalue\"}}}}}"
Expand All @@ -72,14 +106,18 @@ func TestAccFirestoreDocument_firestoreDocumentNestedDocumentExample(t *testing.
t.Parallel()

context := map[string]interface{}{
"project_id": envvar.GetTestFirestoreProjectFromEnv(t),
"org_id": envvar.GetTestOrgFromEnv(t),
"random_suffix": acctest.RandString(t, 10),
}

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckFirestoreDocumentDestroyProducer(t),
ExternalProviders: map[string]resource.ExternalProvider{
"random": {},
"time": {},
},
CheckDestroy: testAccCheckFirestoreDocumentDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccFirestoreDocument_firestoreDocumentNestedDocumentExample(context),
Expand All @@ -96,22 +134,54 @@ func TestAccFirestoreDocument_firestoreDocumentNestedDocumentExample(t *testing.

func testAccFirestoreDocument_firestoreDocumentNestedDocumentExample(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_project" "project" {
project_id = "tf-test-project-id%{random_suffix}"
name = "tf-test-project-id%{random_suffix}"
org_id = "%{org_id}"
}
resource "time_sleep" "wait_60_seconds" {
depends_on = [google_project.project]
create_duration = "60s"
}
resource "google_project_service" "firestore" {
project = google_project.project.project_id
service = "firestore.googleapis.com"
# Needed for CI tests for permissions to propagate, should not be needed for actual usage
depends_on = [time_sleep.wait_60_seconds]
}
resource "google_firestore_database" "database" {
project = google_project.project.project_id
name = "(default)"
location_id = "nam5"
type = "FIRESTORE_NATIVE"
depends_on = [google_project_service.firestore]
}
resource "google_firestore_document" "mydoc" {
project = "%{project_id}"
project = google_project.project.project_id
database = google_firestore_database.database.name
collection = "somenewcollection"
document_id = "tf-test-my-doc-id%{random_suffix}"
fields = "{\"something\":{\"mapValue\":{\"fields\":{\"akey\":{\"stringValue\":\"avalue\"}}}}}"
}
resource "google_firestore_document" "sub_document" {
project = "%{project_id}"
project = google_project.project.project_id
database = google_firestore_database.database.name
collection = "${google_firestore_document.mydoc.path}/subdocs"
document_id = "bitcoinkey"
fields = "{\"something\":{\"mapValue\":{\"fields\":{\"ayo\":{\"stringValue\":\"val2\"}}}}}"
}
resource "google_firestore_document" "sub_sub_document" {
project = "%{project_id}"
project = google_project.project.project_id
database = google_firestore_database.database.name
collection = "${google_firestore_document.sub_document.path}/subsubdocs"
document_id = "asecret"
fields = "{\"something\":{\"mapValue\":{\"fields\":{\"secret\":{\"stringValue\":\"hithere\"}}}}}"
Expand Down
60 changes: 42 additions & 18 deletions google/services/firestore/resource_firestore_document_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,26 @@ import (
func TestAccFirestoreDocument_update(t *testing.T) {
t.Parallel()

name := fmt.Sprintf("tf-test-%d", acctest.RandInt(t))
project := envvar.GetTestFirestoreProjectFromEnv(t)
orgId := envvar.GetTestOrgFromEnv(t)
randomSuffix := acctest.RandString(t, 10)

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
ExternalProviders: map[string]resource.ExternalProvider{
"time": {},
},
Steps: []resource.TestStep{
{
Config: testAccFirestoreDocument_update(project, name),
Config: testAccFirestoreDocument_update(randomSuffix, orgId, "OPTIMISTIC", "val1"),
},
{
ResourceName: "google_firestore_document.instance",
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccFirestoreDocument_update2(project, name),
Config: testAccFirestoreDocument_update(randomSuffix, orgId, "OPTIMISTIC", "val2"),
},
{
ResourceName: "google_firestore_document.instance",
Expand All @@ -41,26 +44,47 @@ func TestAccFirestoreDocument_update(t *testing.T) {
})
}

func testAccFirestoreDocument_update(project, name string) string {
func testAccFirestoreDocument_update_basicDeps(randomSuffix, orgId string) string {
return fmt.Sprintf(`
resource "google_firestore_document" "instance" {
project = "%s"
database = "(default)"
collection = "somenewcollection"
document_id = "%s"
fields = "{\"something\":{\"mapValue\":{\"fields\":{\"yo\":{\"stringValue\":\"val1\"}}}}}"
resource "google_project" "project" {
project_id = "tf-test%s"
name = "tf-test%s"
org_id = "%s"
}
`, project, name)
resource "time_sleep" "wait_60_seconds" {
depends_on = [google_project.project]
create_duration = "60s"
}
func testAccFirestoreDocument_update2(project, name string) string {
return fmt.Sprintf(`
resource "google_project_service" "firestore" {
project = google_project.project.project_id
service = "firestore.googleapis.com"
# Needed for CI tests for permissions to propagate, should not be needed for actual usage
depends_on = [time_sleep.wait_60_seconds]
}
resource "google_firestore_database" "database" {
project = google_project.project.project_id
name = "(default)"
location_id = "nam5"
type = "FIRESTORE_NATIVE"
depends_on = [google_project_service.firestore]
}
`, randomSuffix, randomSuffix, orgId)
}

func testAccFirestoreDocument_update(randomSuffix, orgId, name, val string) string {
return testAccFirestoreDocument_update_basicDeps(randomSuffix, orgId) + fmt.Sprintf(`
resource "google_firestore_document" "instance" {
project = "%s"
database = "(default)"
project = google_project.project.project_id
database = google_firestore_database.database.name
collection = "somenewcollection"
document_id = "%s"
fields = "{\"something\":{\"mapValue\":{\"fields\":{\"yo\":{\"stringValue\":\"val2\"}}}}}"
fields = "{\"something\":{\"mapValue\":{\"fields\":{\"yo\":{\"stringValue\":\"%s\"}}}}}"
}
`, project, name)
`, name, val)
}
Loading

0 comments on commit 490ea79

Please sign in to comment.