From eef1ff433edd88deecb8321a77dd806681eadb1b Mon Sep 17 00:00:00 2001 From: Ilyaas Kapadia <86218345+IlyaasK@users.noreply.github.com> Date: Sat, 11 Jul 2026 08:09:44 -0400 Subject: [PATCH] Test browser pool data source against Kernel Add opt-in acceptance coverage for ID and exact-name lookup, durable state flattening, no-drift planning, project overrides, and cleanup. Run it only from the manual acceptance matrix. --- .github/workflows/acceptance.yml | 3 + docs/acceptance.md | 15 +- .../browserpool/datasource_acc_test.go | 147 ++++++++++++++++++ 3 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 internal/datasources/browserpool/datasource_acc_test.go diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index e9073d5..5503dbd 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -24,6 +24,9 @@ jobs: - name: Browser pool package: ./internal/resources/browserpool project_id_required: true + - name: Browser pool data source + package: ./internal/datasources/browserpool + project_id_required: true - name: Project package: ./internal/resources/project project_id_required: false diff --git a/docs/acceptance.md b/docs/acceptance.md index e2f7b8a..e84fa55 100644 --- a/docs/acceptance.md +++ b/docs/acceptance.md @@ -4,7 +4,7 @@ This document is the live-API release gate for the provider's selected public surface. Unit tests remain the fast default. Acceptance tests run after changes reach `main`, through explicit local opt-in, or by manual workflow dispatch. -The selected surface contains two managed resources and four read-only data +The selected surface contains two managed resources and five read-only data sources. It does not claim coverage for future or unregistered Kernel objects. ## Gate Rules @@ -29,7 +29,7 @@ export KERNEL_ALT_PROJECT_ID=... # optional second project export KERNEL_BASE_URL=... # optional non-production API ``` -`KERNEL_PROJECT_ID` is required for the browser-pool resource and all four data +`KERNEL_PROJECT_ID` is required for the browser-pool resource and all five data sources. The project resource is organization-scoped and does not require it. ## Matrix @@ -38,6 +38,7 @@ sources. The project resource is organization-scoped and does not require it. | --- | --- | --- | | `kernel_project` resource | `./internal/resources/project` | Create, rename with stable ID, no-drift plan, canonical-ID import, post-import no drift, delete, and HTTP 404 verification. | | `kernel_browser_pool` resource | `./internal/resources/browserpool` | Create, durable update with stable ID, no-drift plan, provider-default and explicit project scope, bare and project-qualified import, non-force delete, and HTTP 404 verification. | +| `kernel_browser_pool` data source | `./internal/datasources/browserpool` | Create a unique browser-pool fixture, read it by canonical ID and exact name with explicit project scope, verify normalized durable configuration and no drift, then delete and require coded `not_found`. | | `kernel_project` data source | `./internal/datasources/project` | Create a unique project fixture, read it by ID and exact name, read the provider-default project, verify durable metadata and no drift, then delete and require coded `not_found`. | | `kernel_profile` data source | `./internal/datasources/profile` | Create a durable profile fixture through the SDK, read it by ID and exact name with explicit and default project scope, verify durable metadata and no drift, then delete and require coded `not_found`. | | `kernel_proxy` data source | `./internal/datasources/proxy` | Create a managed datacenter proxy fixture through the SDK, read it by ID and exact name with explicit and default project scope, verify durable masked metadata and no drift, then delete and require coded `not_found`. | @@ -53,21 +54,21 @@ Run packages independently for fast failure isolation: ```sh go test -count=1 -timeout=30m -v ./internal/resources/project -run TestAcc go test -count=1 -timeout=30m -v ./internal/resources/browserpool -run TestAcc +go test -count=1 -timeout=30m -v ./internal/datasources/browserpool -run TestAcc go test -count=1 -timeout=30m -v ./internal/datasources/project -run TestAcc go test -count=1 -timeout=30m -v ./internal/datasources/profile -run TestAcc go test -count=1 -timeout=30m -v ./internal/datasources/proxy -run TestAcc go test -count=1 -timeout=30m -v ./internal/datasources/extension -run TestAcc ``` -The `Acceptance` workflow runs the same six packages as separate matrix jobs +The `Acceptance` workflow runs the same seven packages as separate matrix jobs with `fail-fast: false` after changes reach `main` and on manual dispatch. ## Outside The Selected Surface -The release does not include a browser-pool data source or profile, proxy, -extension, deployment, app, or API-key resources. Runtime/session operations -remain outside Terraform. Unregistered surfaces are not acceptance blockers for -this selected release. +The release does not include profile, proxy, extension, deployment, app, or +API-key resources. Runtime/session operations remain outside Terraform. +Unregistered surfaces are not acceptance blockers for this selected release. ## Release Record diff --git a/internal/datasources/browserpool/datasource_acc_test.go b/internal/datasources/browserpool/datasource_acc_test.go new file mode 100644 index 0000000..002386d --- /dev/null +++ b/internal/datasources/browserpool/datasource_acc_test.go @@ -0,0 +1,147 @@ +package browserpool_test + +import ( + "context" + "fmt" + "os" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/kernel/terraform-provider-kernel/internal/acctest" + "github.com/kernel/terraform-provider-kernel/internal/projectscope" +) + +const browserPoolAcceptanceResourceName = "kernel_browser_pool.data_source_test" + +func TestAccBrowserPoolDataSourceByIDAndName(t *testing.T) { + name := acctest.UniqueName(t, "browser-pool-data") + projectID := os.Getenv(acctest.EnvAltProjectID) + if projectID == "" { + projectID = os.Getenv(acctest.EnvProjectID) + } + config := testAccBrowserPoolDataSourceConfig(name, projectID) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(t) + if projectID == "" { + t.Fatalf("%s or %s must be set for the browser pool data source acceptance test", acctest.EnvProjectID, acctest.EnvAltProjectID) + } + }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories(), + CheckDestroy: testAccCheckBrowserPoolDataSourceDestroyed(), + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCaptureBrowserPoolDataSourceID(t), + resource.TestCheckResourceAttrPair("data.kernel_browser_pool.by_id", "id", browserPoolAcceptanceResourceName, "id"), + resource.TestCheckResourceAttrPair("data.kernel_browser_pool.by_name", "id", browserPoolAcceptanceResourceName, "id"), + resource.TestCheckResourceAttrPair("data.kernel_browser_pool.by_id", "name", browserPoolAcceptanceResourceName, "name"), + resource.TestCheckResourceAttr("data.kernel_browser_pool.by_id", "project_id", projectID), + resource.TestCheckResourceAttr("data.kernel_browser_pool.by_name", "project_id", projectID), + testAccCheckBrowserPoolDataSourceState("data.kernel_browser_pool.by_id", name), + testAccCheckBrowserPoolDataSourceState("data.kernel_browser_pool.by_name", name), + ), + }, + { + Config: config, + PlanOnly: true, + }, + }, + }) +} + +func testAccCheckBrowserPoolDataSourceDestroyed() resource.TestCheckFunc { + return func(state *terraform.State) error { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + client := acctest.ClientFromEnv() + for _, resourceState := range state.RootModule().Resources { + if resourceState.Type != "kernel_browser_pool" || resourceState.Primary == nil || resourceState.Primary.ID == "" { + continue + } + + _, err := client.GetBrowserPool(ctx, resourceState.Primary.Attributes["project_id"], resourceState.Primary.ID) + if projectscope.IsNotFound(err) { + continue + } + if err != nil { + return fmt.Errorf("read Kernel browser pool %s after destroy: %w", resourceState.Primary.ID, err) + } + return fmt.Errorf("Kernel browser pool %s still exists after destroy", resourceState.Primary.ID) + } + return nil + } +} + +func testAccBrowserPoolDataSourceConfig(name, projectID string) string { + return acctest.ProviderConfig() + fmt.Sprintf(` +resource "kernel_browser_pool" "data_source_test" { + name = %[1]q + size = 1 + project_id = %[2]q + start_url = "chrome://newtab" + headless = true + kiosk_mode = false + stealth = false + timeout_seconds = 90 + fill_rate_per_minute = 0 + viewport = { + width = 1280 + height = 800 + refresh_rate = 60 + } + chrome_policy = jsonencode({ + HomepageLocation = "https://example.com" + RestoreOnStartup = 4 + }) +} + +data "kernel_browser_pool" "by_id" { + id = kernel_browser_pool.data_source_test.id + project_id = %[2]q +} + +data "kernel_browser_pool" "by_name" { + name = kernel_browser_pool.data_source_test.name + project_id = %[2]q +} +`, name, projectID) +} + +func testAccCaptureBrowserPoolDataSourceID(t *testing.T) resource.TestCheckFunc { + t.Helper() + + return func(state *terraform.State) error { + resourceState, ok := state.RootModule().Resources[browserPoolAcceptanceResourceName] + if !ok || resourceState.Primary == nil || resourceState.Primary.ID == "" { + return fmt.Errorf("missing ID for %s", browserPoolAcceptanceResourceName) + } + acctest.CleanupBrowserPool(t, resourceState.Primary.Attributes["project_id"], resourceState.Primary.ID) + return nil + } +} + +func testAccCheckBrowserPoolDataSourceState(resourceName, name string) resource.TestCheckFunc { + return resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "size", "1"), + resource.TestCheckResourceAttr(resourceName, "start_url", "chrome://newtab"), + resource.TestCheckResourceAttr(resourceName, "headless", "true"), + resource.TestCheckResourceAttr(resourceName, "kiosk_mode", "false"), + resource.TestCheckResourceAttr(resourceName, "stealth", "false"), + resource.TestCheckResourceAttr(resourceName, "timeout_seconds", "90"), + resource.TestCheckResourceAttr(resourceName, "fill_rate_per_minute", "0"), + resource.TestCheckResourceAttr(resourceName, "viewport.width", "1280"), + resource.TestCheckResourceAttr(resourceName, "viewport.height", "800"), + resource.TestCheckResourceAttr(resourceName, "viewport.refresh_rate", "60"), + resource.TestCheckResourceAttr(resourceName, "chrome_policy", `{"HomepageLocation":"https://example.com","RestoreOnStartup":4}`), + resource.TestCheckResourceAttr(resourceName, "extension_ids.#", "0"), + resource.TestCheckNoResourceAttr(resourceName, "profile_id"), + resource.TestCheckNoResourceAttr(resourceName, "proxy_id"), + ) +}