diff --git a/internal/services/r2_bucket_lifecycle/data_source_test.go b/internal/services/r2_bucket_lifecycle/data_source_test.go new file mode 100644 index 0000000000..d18def810d --- /dev/null +++ b/internal/services/r2_bucket_lifecycle/data_source_test.go @@ -0,0 +1,43 @@ +package r2_bucket_lifecycle_test + +import ( + "os" + "testing" + + "github.com/cloudflare/terraform-provider-cloudflare/internal/acctest" + "github.com/cloudflare/terraform-provider-cloudflare/internal/utils" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +func TestAccCloudflareR2BucketLifecycleDataSource_Basic(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + dataSourceName := "data.cloudflare_r2_bucket_lifecycle." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccR2BucketLifecycleDataSourceConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("account_id"), knownvalue.StringExact(accountID)), + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("bucket_name"), knownvalue.StringExact(rnd)), + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("rules"), knownvalue.ListSizeExact(1)), + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("id"), knownvalue.StringExact("datasource-test-rule")), + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("enabled"), knownvalue.Bool(true)), + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("conditions").AtMapKey("prefix"), knownvalue.StringExact("test-data/")), + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("delete_objects_transition").AtMapKey("condition").AtMapKey("type"), knownvalue.StringExact("Age")), + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("delete_objects_transition").AtMapKey("condition").AtMapKey("max_age"), knownvalue.Int64Exact(604800)), + }, + }, + }, + }) +} + +func testAccR2BucketLifecycleDataSourceConfig(rnd, accountID string) string { + return acctest.LoadTestCase("datasource_basic.tf", rnd, accountID) +} diff --git a/internal/services/r2_bucket_lifecycle/resource_test.go b/internal/services/r2_bucket_lifecycle/resource_test.go new file mode 100644 index 0000000000..b85db5b41e --- /dev/null +++ b/internal/services/r2_bucket_lifecycle/resource_test.go @@ -0,0 +1,204 @@ +package r2_bucket_lifecycle_test + +import ( + "os" + "testing" + + "github.com/cloudflare/terraform-provider-cloudflare/internal/acctest" + "github.com/cloudflare/terraform-provider-cloudflare/internal/utils" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +func TestAccCloudflareR2BucketLifecycle_Basic(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + resourceName := "cloudflare_r2_bucket_lifecycle." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccR2BucketLifecycleConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("account_id"), knownvalue.StringExact(accountID)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("bucket_name"), knownvalue.StringExact(rnd)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("jurisdiction"), knownvalue.StringExact("default")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules"), knownvalue.ListSizeExact(1)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("id"), knownvalue.StringExact("delete-old-objects")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("enabled"), knownvalue.Bool(true)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("conditions").AtMapKey("prefix"), knownvalue.StringExact("logs/")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("delete_objects_transition").AtMapKey("condition").AtMapKey("type"), knownvalue.StringExact("Age")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("delete_objects_transition").AtMapKey("condition").AtMapKey("max_age"), knownvalue.Int64Exact(2592000)), + }, + }, + }, + }) +} + +func TestAccCloudflareR2BucketLifecycle_Update(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + resourceName := "cloudflare_r2_bucket_lifecycle." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccR2BucketLifecycleConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules"), knownvalue.ListSizeExact(1)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("delete_objects_transition").AtMapKey("condition").AtMapKey("max_age"), knownvalue.Int64Exact(2592000)), + }, + }, + { + Config: testAccR2BucketLifecycleUpdateConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules"), knownvalue.ListSizeExact(3)), + statecheck.ExpectKnownValue( + resourceName, + tfjsonpath.New("rules"), + knownvalue.SetPartial([]knownvalue.Check{ + knownvalue.ObjectPartial(map[string]knownvalue.Check{ + "id": knownvalue.StringExact("delete-old-objects"), + "enabled": knownvalue.Bool(true), + "conditions": knownvalue.ObjectPartial(map[string]knownvalue.Check{ + "prefix": knownvalue.StringExact("logs/"), + }), + "delete_objects_transition": knownvalue.ObjectPartial(map[string]knownvalue.Check{ + "condition": knownvalue.ObjectPartial(map[string]knownvalue.Check{ + "type": knownvalue.StringExact("Age"), + "max_age": knownvalue.Int64Exact(5184000), + }), + }), + }), + knownvalue.ObjectPartial(map[string]knownvalue.Check{ + "id": knownvalue.StringExact("archive-objects"), + "enabled": knownvalue.Bool(true), + "conditions": knownvalue.ObjectPartial(map[string]knownvalue.Check{ + "prefix": knownvalue.StringExact("archive/"), + }), + "storage_class_transitions": knownvalue.ListPartial(map[int]knownvalue.Check{ + 0: knownvalue.ObjectPartial(map[string]knownvalue.Check{ + "condition": knownvalue.ObjectPartial(map[string]knownvalue.Check{ + "type": knownvalue.StringExact("Age"), + "max_age": knownvalue.Int64Exact(604800), + }), + "storage_class": knownvalue.StringExact("InfrequentAccess"), + }), + }), + }), + knownvalue.ObjectPartial(map[string]knownvalue.Check{ + "id": knownvalue.StringExact("cleanup-multipart"), + "enabled": knownvalue.Bool(true), + "conditions": knownvalue.ObjectPartial(map[string]knownvalue.Check{ + "prefix": knownvalue.StringExact(""), + }), + "abort_multipart_uploads_transition": knownvalue.ObjectPartial(map[string]knownvalue.Check{ + "condition": knownvalue.ObjectPartial(map[string]knownvalue.Check{ + "type": knownvalue.StringExact("Age"), + "max_age": knownvalue.Int64Exact(86400), + }), + }), + }), + }), + ), + }, + }, + }, + }) +} + +func TestAccCloudflareR2BucketLifecycle_JurisdictionEU(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + resourceName := "cloudflare_r2_bucket_lifecycle." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccR2BucketLifecycleJurisdictionEUConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("account_id"), knownvalue.StringExact(accountID)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("bucket_name"), knownvalue.StringExact(rnd)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("jurisdiction"), knownvalue.StringExact("eu")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("id"), knownvalue.StringExact("eu-compliance-delete")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("conditions").AtMapKey("prefix"), knownvalue.StringExact("gdpr/")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("delete_objects_transition").AtMapKey("condition").AtMapKey("max_age"), knownvalue.Int64Exact(7776000)), + }, + }, + }, + }) +} + +func TestAccCloudflareR2BucketLifecycle_DateCondition(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + resourceName := "cloudflare_r2_bucket_lifecycle." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccR2BucketLifecycleDateConditionConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("id"), knownvalue.StringExact("delete-by-date")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("delete_objects_transition").AtMapKey("condition").AtMapKey("type"), knownvalue.StringExact("Date")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("delete_objects_transition").AtMapKey("condition").AtMapKey("date"), knownvalue.StringExact("2024-12-31T23:59:59Z")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("delete_objects_transition").AtMapKey("condition").AtMapKey("max_age"), knownvalue.Null()), + }, + }, + }, + }) +} + +func TestAccCloudflareR2BucketLifecycle_MinimalRules(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + resourceName := "cloudflare_r2_bucket_lifecycle." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccR2BucketLifecycleMinimalRulesConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("id"), knownvalue.StringExact("minimal-rule")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("enabled"), knownvalue.Bool(false)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("conditions").AtMapKey("prefix"), knownvalue.StringExact("")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("delete_objects_transition"), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("storage_class_transitions"), knownvalue.ListSizeExact(0)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("abort_multipart_uploads_transition"), knownvalue.Null()), + }, + }, + }, + }) +} + +func testAccR2BucketLifecycleConfig(rnd, accountID string) string { + return acctest.LoadTestCase("r2_lifecycle_basic.tf", rnd, accountID) +} + +func testAccR2BucketLifecycleUpdateConfig(rnd, accountID string) string { + return acctest.LoadTestCase("r2_lifecycle_multiple_rules.tf", rnd, accountID) +} + +func testAccR2BucketLifecycleJurisdictionEUConfig(rnd, accountID string) string { + return acctest.LoadTestCase("r2_lifecycle_jurisdiction_eu.tf", rnd, accountID) +} + +func testAccR2BucketLifecycleDateConditionConfig(rnd, accountID string) string { + return acctest.LoadTestCase("r2_lifecycle_date_condition.tf", rnd, accountID) +} + +func testAccR2BucketLifecycleMinimalRulesConfig(rnd, accountID string) string { + return acctest.LoadTestCase("r2_lifecycle_minimal_rules.tf", rnd, accountID) +} diff --git a/internal/services/r2_bucket_lifecycle/testdata/datasource_basic.tf b/internal/services/r2_bucket_lifecycle/testdata/datasource_basic.tf new file mode 100644 index 0000000000..ad8eb8152f --- /dev/null +++ b/internal/services/r2_bucket_lifecycle/testdata/datasource_basic.tf @@ -0,0 +1,28 @@ +resource "cloudflare_r2_bucket" "%[1]s" { + account_id = "%[2]s" + name = "%[1]s" +} + +resource "cloudflare_r2_bucket_lifecycle" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket.%[1]s.name + + rules = [{ + id = "datasource-test-rule" + enabled = true + conditions = { + prefix = "test-data/" + } + delete_objects_transition = { + condition = { + type = "Age" + max_age = 604800 # 7 days + } + } + }] +} + +data "cloudflare_r2_bucket_lifecycle" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket_lifecycle.%[1]s.bucket_name +} \ No newline at end of file diff --git a/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_basic.tf b/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_basic.tf new file mode 100644 index 0000000000..f4e670cf84 --- /dev/null +++ b/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_basic.tf @@ -0,0 +1,23 @@ +resource "cloudflare_r2_bucket" "%[1]s" { + account_id = "%[2]s" + name = "%[1]s" +} + +resource "cloudflare_r2_bucket_lifecycle" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket.%[1]s.name + + rules = [{ + id = "delete-old-objects" + enabled = true + conditions = { + prefix = "logs/" + } + delete_objects_transition = { + condition = { + type = "Age" + max_age = 2592000 # 30 days + } + } + }] +} \ No newline at end of file diff --git a/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_date_condition.tf b/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_date_condition.tf new file mode 100644 index 0000000000..7190670054 --- /dev/null +++ b/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_date_condition.tf @@ -0,0 +1,23 @@ +resource "cloudflare_r2_bucket" "%[1]s" { + account_id = "%[2]s" + name = "%[1]s" +} + +resource "cloudflare_r2_bucket_lifecycle" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket.%[1]s.name + + rules = [{ + id = "delete-by-date" + enabled = true + conditions = { + prefix = "temp/" + } + delete_objects_transition = { + condition = { + type = "Date" + date = "2024-12-31T23:59:59Z" + } + } + }] +} diff --git a/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_jurisdiction_eu.tf b/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_jurisdiction_eu.tf new file mode 100644 index 0000000000..85e9fd5b63 --- /dev/null +++ b/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_jurisdiction_eu.tf @@ -0,0 +1,25 @@ +resource "cloudflare_r2_bucket" "%[1]s" { + account_id = "%[2]s" + name = "%[1]s" + jurisdiction = "eu" +} + +resource "cloudflare_r2_bucket_lifecycle" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket.%[1]s.name + jurisdiction = "eu" + + rules = [{ + id = "eu-compliance-delete" + enabled = true + conditions = { + prefix = "gdpr/" + } + delete_objects_transition = { + condition = { + type = "Age" + max_age = 7776000 # 90 days + } + } + }] +} diff --git a/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_minimal_rules.tf b/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_minimal_rules.tf new file mode 100644 index 0000000000..8090478a94 --- /dev/null +++ b/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_minimal_rules.tf @@ -0,0 +1,18 @@ +resource "cloudflare_r2_bucket" "%[1]s" { + account_id = "%[2]s" + name = "%[1]s" +} + +resource "cloudflare_r2_bucket_lifecycle" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket.%[1]s.name + + rules = [{ + id = "minimal-rule" + enabled = false + conditions = { + prefix = "" + } + storage_class_transitions = [] + }] +} diff --git a/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_multiple_rules.tf b/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_multiple_rules.tf new file mode 100644 index 0000000000..a862805365 --- /dev/null +++ b/internal/services/r2_bucket_lifecycle/testdata/r2_lifecycle_multiple_rules.tf @@ -0,0 +1,48 @@ +resource "cloudflare_r2_bucket" "%[1]s" { + account_id = "%[2]s" + name = "%[1]s" +} + +resource "cloudflare_r2_bucket_lifecycle" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket.%[1]s.name + + rules = [{ + id = "archive-objects" + enabled = true + conditions = { + prefix = "archive/" + } + storage_class_transitions = [{ + condition = { + type = "Age" + max_age = 604800 # 7 days + } + storage_class = "InfrequentAccess" + }] + }, { + id = "cleanup-multipart" + enabled = true + conditions = { + prefix = "" + } + abort_multipart_uploads_transition = { + condition = { + type = "Age" + max_age = 86400 + } + } + }, { + id = "delete-old-objects" + enabled = true + conditions = { + prefix = "logs/" + } + delete_objects_transition = { + condition = { + type = "Age" + max_age = 5184000 # 60 days + } + } + }] +} diff --git a/internal/services/r2_bucket_lock/data_source_test.go b/internal/services/r2_bucket_lock/data_source_test.go new file mode 100644 index 0000000000..ed60c05880 --- /dev/null +++ b/internal/services/r2_bucket_lock/data_source_test.go @@ -0,0 +1,43 @@ +package r2_bucket_lock_test + +import ( + "os" + "testing" + + "github.com/cloudflare/terraform-provider-cloudflare/internal/acctest" + "github.com/cloudflare/terraform-provider-cloudflare/internal/utils" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +func TestAccCloudflareR2BucketLockDataSource_Basic(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + dataSourceName := "data.cloudflare_r2_bucket_lock." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccR2BucketLockDataSourceConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("account_id"), knownvalue.StringExact(accountID)), + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("bucket_name"), knownvalue.StringExact(rnd)), + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("rules"), knownvalue.ListSizeExact(1)), + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("id"), knownvalue.StringExact("datasource-test-rule")), + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("enabled"), knownvalue.Bool(true)), + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("prefix"), knownvalue.StringExact("test-data/")), + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("type"), knownvalue.StringExact("Age")), + statecheck.ExpectKnownValue(dataSourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("max_age_seconds"), knownvalue.Int64Exact(604800)), + }, + }, + }, + }) +} + +func testAccR2BucketLockDataSourceConfig(rnd, accountID string) string { + return acctest.LoadTestCase("datasource_basic.tf", rnd, accountID) +} diff --git a/internal/services/r2_bucket_lock/resource_test.go b/internal/services/r2_bucket_lock/resource_test.go new file mode 100644 index 0000000000..5d93a1fe6a --- /dev/null +++ b/internal/services/r2_bucket_lock/resource_test.go @@ -0,0 +1,188 @@ +package r2_bucket_lock_test + +import ( + "os" + "testing" + + "github.com/cloudflare/terraform-provider-cloudflare/internal/acctest" + "github.com/cloudflare/terraform-provider-cloudflare/internal/utils" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +func TestAccCloudflareR2BucketLock_Basic(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + resourceName := "cloudflare_r2_bucket_lock." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccR2BucketLockConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("account_id"), knownvalue.StringExact(accountID)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("bucket_name"), knownvalue.StringExact(rnd)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("jurisdiction"), knownvalue.StringExact("default")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules"), knownvalue.ListSizeExact(1)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("id"), knownvalue.StringExact("retention-rule")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("enabled"), knownvalue.Bool(true)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("prefix"), knownvalue.StringExact("documents/")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("type"), knownvalue.StringExact("Age")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("max_age_seconds"), knownvalue.Int64Exact(86400)), + }, + }, + }, + }) +} + +func TestAccCloudflareR2BucketLock_MultipleRules(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + resourceName := "cloudflare_r2_bucket_lock." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccR2BucketLockConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules"), knownvalue.ListSizeExact(1)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("max_age_seconds"), knownvalue.Int64Exact(86400)), + }, + }, + { + Config: testAccR2BucketLockMultipleRulesConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules"), knownvalue.ListSizeExact(3)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("id"), knownvalue.StringExact("archive-lock")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("max_age_seconds"), knownvalue.Int64Exact(604800)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(1).AtMapKey("id"), knownvalue.StringExact("indefinite-lock")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(1).AtMapKey("enabled"), knownvalue.Bool(false)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(1).AtMapKey("condition").AtMapKey("type"), knownvalue.StringExact("Indefinite")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(2).AtMapKey("id"), knownvalue.StringExact("retention-rule")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(2).AtMapKey("condition").AtMapKey("max_age_seconds"), knownvalue.Int64Exact(172800)), + }, + }, + }, + }) +} + +func TestAccCloudflareR2BucketLock_JurisdictionEU(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + resourceName := "cloudflare_r2_bucket_lock." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccR2BucketLockJurisdictionEUConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("account_id"), knownvalue.StringExact(accountID)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("bucket_name"), knownvalue.StringExact(rnd)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("jurisdiction"), knownvalue.StringExact("eu")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("id"), knownvalue.StringExact("eu-compliance-lock")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("prefix"), knownvalue.StringExact("gdpr/")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("max_age_seconds"), knownvalue.Int64Exact(2592000)), + }, + }, + }, + }) +} + +func TestAccCloudflareR2BucketLock_DateCondition(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + resourceName := "cloudflare_r2_bucket_lock." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccR2BucketLockDateConditionConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("id"), knownvalue.StringExact("date-lock")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("type"), knownvalue.StringExact("Date")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("date"), knownvalue.StringExact("2025-12-31T23:59:59Z")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("max_age_seconds"), knownvalue.Null()), + }, + }, + }, + }) +} + +func TestAccCloudflareR2BucketLock_IndefiniteCondition(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + resourceName := "cloudflare_r2_bucket_lock." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccR2BucketLockIndefiniteConditionConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("id"), knownvalue.StringExact("indefinite-lock")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("type"), knownvalue.StringExact("Indefinite")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("max_age_seconds"), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("date"), knownvalue.Null()), + }, + }, + }, + }) +} + +func TestAccCloudflareR2BucketLock_MinimalRules(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + resourceName := "cloudflare_r2_bucket_lock." + rnd + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccR2BucketLockMinimalRulesConfig(rnd, accountID), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("id"), knownvalue.StringExact("minimal-lock")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("enabled"), knownvalue.Bool(false)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("prefix"), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("type"), knownvalue.StringExact("Age")), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("condition").AtMapKey("max_age_seconds"), knownvalue.Int64Exact(3600)), + }, + }, + }, + }) +} + +func testAccR2BucketLockConfig(rnd, accountID string) string { + return acctest.LoadTestCase("r2_lock_basic.tf", rnd, accountID) +} + +func testAccR2BucketLockMultipleRulesConfig(rnd, accountID string) string { + return acctest.LoadTestCase("r2_lock_multiple_rules.tf", rnd, accountID) +} + +func testAccR2BucketLockJurisdictionEUConfig(rnd, accountID string) string { + return acctest.LoadTestCase("r2_lock_jurisdiction_eu.tf", rnd, accountID) +} + +func testAccR2BucketLockDateConditionConfig(rnd, accountID string) string { + return acctest.LoadTestCase("r2_lock_date_condition.tf", rnd, accountID) +} + +func testAccR2BucketLockIndefiniteConditionConfig(rnd, accountID string) string { + return acctest.LoadTestCase("r2_lock_indefinite_condition.tf", rnd, accountID) +} + +func testAccR2BucketLockMinimalRulesConfig(rnd, accountID string) string { + return acctest.LoadTestCase("r2_lock_minimal_rules.tf", rnd, accountID) +} diff --git a/internal/services/r2_bucket_lock/testdata/datasource_basic.tf b/internal/services/r2_bucket_lock/testdata/datasource_basic.tf new file mode 100644 index 0000000000..23b4196669 --- /dev/null +++ b/internal/services/r2_bucket_lock/testdata/datasource_basic.tf @@ -0,0 +1,24 @@ +resource "cloudflare_r2_bucket" "%[1]s" { + account_id = "%[2]s" + name = "%[1]s" +} + +resource "cloudflare_r2_bucket_lock" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket.%[1]s.name + + rules = [{ + id = "datasource-test-rule" + enabled = true + prefix = "test-data/" + condition = { + type = "Age" + max_age_seconds = 604800 + } + }] +} + +data "cloudflare_r2_bucket_lock" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket_lock.%[1]s.bucket_name +} \ No newline at end of file diff --git a/internal/services/r2_bucket_lock/testdata/r2_lock_basic.tf b/internal/services/r2_bucket_lock/testdata/r2_lock_basic.tf new file mode 100644 index 0000000000..d6835aeebd --- /dev/null +++ b/internal/services/r2_bucket_lock/testdata/r2_lock_basic.tf @@ -0,0 +1,19 @@ +resource "cloudflare_r2_bucket" "%[1]s" { + account_id = "%[2]s" + name = "%[1]s" +} + +resource "cloudflare_r2_bucket_lock" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket.%[1]s.name + + rules = [{ + id = "retention-rule" + enabled = true + prefix = "documents/" + condition = { + type = "Age" + max_age_seconds = 86400 + } + }] +} \ No newline at end of file diff --git a/internal/services/r2_bucket_lock/testdata/r2_lock_date_condition.tf b/internal/services/r2_bucket_lock/testdata/r2_lock_date_condition.tf new file mode 100644 index 0000000000..0a8f3a33ec --- /dev/null +++ b/internal/services/r2_bucket_lock/testdata/r2_lock_date_condition.tf @@ -0,0 +1,19 @@ +resource "cloudflare_r2_bucket" "%[1]s" { + account_id = "%[2]s" + name = "%[1]s" +} + +resource "cloudflare_r2_bucket_lock" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket.%[1]s.name + + rules = [{ + id = "date-lock" + enabled = true + prefix = "temp/" + condition = { + type = "Date" + date = "2025-12-31T23:59:59Z" + } + }] +} diff --git a/internal/services/r2_bucket_lock/testdata/r2_lock_indefinite_condition.tf b/internal/services/r2_bucket_lock/testdata/r2_lock_indefinite_condition.tf new file mode 100644 index 0000000000..d8be9497ae --- /dev/null +++ b/internal/services/r2_bucket_lock/testdata/r2_lock_indefinite_condition.tf @@ -0,0 +1,18 @@ +resource "cloudflare_r2_bucket" "%[1]s" { + account_id = "%[2]s" + name = "%[1]s" +} + +resource "cloudflare_r2_bucket_lock" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket.%[1]s.name + + rules = [{ + id = "indefinite-lock" + enabled = true + prefix = "permanent/" + condition = { + type = "Indefinite" + } + }] +} diff --git a/internal/services/r2_bucket_lock/testdata/r2_lock_jurisdiction_eu.tf b/internal/services/r2_bucket_lock/testdata/r2_lock_jurisdiction_eu.tf new file mode 100644 index 0000000000..d699127a69 --- /dev/null +++ b/internal/services/r2_bucket_lock/testdata/r2_lock_jurisdiction_eu.tf @@ -0,0 +1,21 @@ +resource "cloudflare_r2_bucket" "%[1]s" { + account_id = "%[2]s" + name = "%[1]s" + jurisdiction = "eu" +} + +resource "cloudflare_r2_bucket_lock" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket.%[1]s.name + jurisdiction = "eu" + + rules = [{ + id = "eu-compliance-lock" + enabled = true + prefix = "gdpr/" + condition = { + type = "Age" + max_age_seconds = 2592000 + } + }] +} diff --git a/internal/services/r2_bucket_lock/testdata/r2_lock_minimal_rules.tf b/internal/services/r2_bucket_lock/testdata/r2_lock_minimal_rules.tf new file mode 100644 index 0000000000..6d638b4def --- /dev/null +++ b/internal/services/r2_bucket_lock/testdata/r2_lock_minimal_rules.tf @@ -0,0 +1,18 @@ +resource "cloudflare_r2_bucket" "%[1]s" { + account_id = "%[2]s" + name = "%[1]s" +} + +resource "cloudflare_r2_bucket_lock" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket.%[1]s.name + + rules = [{ + id = "minimal-lock" + enabled = false + condition = { + type = "Age" + max_age_seconds = 3600 + } + }] +} diff --git a/internal/services/r2_bucket_lock/testdata/r2_lock_multiple_rules.tf b/internal/services/r2_bucket_lock/testdata/r2_lock_multiple_rules.tf new file mode 100644 index 0000000000..fbcf59d593 --- /dev/null +++ b/internal/services/r2_bucket_lock/testdata/r2_lock_multiple_rules.tf @@ -0,0 +1,34 @@ +resource "cloudflare_r2_bucket" "%[1]s" { + account_id = "%[2]s" + name = "%[1]s" +} + +resource "cloudflare_r2_bucket_lock" "%[1]s" { + account_id = "%[2]s" + bucket_name = cloudflare_r2_bucket.%[1]s.name + + rules = [{ + id = "archive-lock" + enabled = true + prefix = "archive/" + condition = { + type = "Age" + max_age_seconds = 604800 + } + }, { + id = "indefinite-lock" + enabled = false + prefix = "permanent/" + condition = { + type = "Indefinite" + } + }, { + id = "retention-rule" + enabled = true + prefix = "documents/" + condition = { + type = "Age" + max_age_seconds = 172800 + } + }] +}