diff --git a/internal/resources/grafana/resource_alerting_message_template_test.go b/internal/resources/grafana/resource_alerting_message_template_test.go index abb7ed1f5..897ee76a5 100644 --- a/internal/resources/grafana/resource_alerting_message_template_test.go +++ b/internal/resources/grafana/resource_alerting_message_template_test.go @@ -27,6 +27,7 @@ func TestAccMessageTemplate_basic(t *testing.T) { alertingMessageTemplateCheckExists.exists("grafana_message_template.my_template", &tmpl), resource.TestCheckResourceAttr("grafana_message_template.my_template", "name", "My Reusable Template"), resource.TestCheckResourceAttr("grafana_message_template.my_template", "template", "{{define \"My Reusable Template\" }}\n template content\n{{ end }}"), + testutils.CheckLister("grafana_message_template.my_template"), ), }, // Test import. diff --git a/internal/resources/grafana/resource_alerting_mute_timing_test.go b/internal/resources/grafana/resource_alerting_mute_timing_test.go index 8c1f2efc4..4a396bf1a 100644 --- a/internal/resources/grafana/resource_alerting_mute_timing_test.go +++ b/internal/resources/grafana/resource_alerting_mute_timing_test.go @@ -37,6 +37,7 @@ func TestAccMuteTiming_basic(t *testing.T) { resource.TestCheckResourceAttr("grafana_mute_timing.my_mute_timing", "intervals.0.years.0", "2030"), resource.TestCheckResourceAttr("grafana_mute_timing.my_mute_timing", "intervals.0.years.1", "2025:2026"), resource.TestCheckResourceAttr("grafana_mute_timing.my_mute_timing", "intervals.0.location", "America/New_York"), + testutils.CheckLister("grafana_mute_timing.my_mute_timing"), ), }, // Test import. diff --git a/internal/resources/grafana/resource_alerting_notification_policy_test.go b/internal/resources/grafana/resource_alerting_notification_policy_test.go index 9c73c7813..0e9b3550c 100644 --- a/internal/resources/grafana/resource_alerting_notification_policy_test.go +++ b/internal/resources/grafana/resource_alerting_notification_policy_test.go @@ -65,6 +65,7 @@ func TestAccNotificationPolicy_basic(t *testing.T) { resource.TestCheckResourceAttr("grafana_notification_policy.my_notification_policy", "policy.1.matcher.0.match", "=~"), resource.TestCheckResourceAttr("grafana_notification_policy.my_notification_policy", "policy.1.matcher.0.value", "another value.*"), resource.TestCheckResourceAttr("grafana_notification_policy.my_notification_policy", "policy.1.group_by.0", "..."), + testutils.CheckLister("grafana_notification_policy.my_notification_policy"), ), }, // Test import. diff --git a/internal/resources/grafana/resource_alerting_rule_group_test.go b/internal/resources/grafana/resource_alerting_rule_group_test.go index e2dc970cd..4a5edf8b6 100644 --- a/internal/resources/grafana/resource_alerting_rule_group_test.go +++ b/internal/resources/grafana/resource_alerting_rule_group_test.go @@ -34,6 +34,7 @@ func TestAccAlertRule_basic(t *testing.T) { resource.TestCheckResourceAttr("grafana_rule_group.my_alert_rule", "org_id", "1"), resource.TestCheckResourceAttr("grafana_rule_group.my_alert_rule", "rule.#", "1"), resource.TestCheckResourceAttr("grafana_rule_group.my_alert_rule", "rule.0.data.0.model", "{\"hide\":false,\"refId\":\"A\"}"), + testutils.CheckLister("grafana_rule_group.my_alert_rule"), ), }, // Test "for: 0s" diff --git a/internal/resources/grafana/resource_annotation_test.go b/internal/resources/grafana/resource_annotation_test.go index 1d36631f4..f8d99338c 100644 --- a/internal/resources/grafana/resource_annotation_test.go +++ b/internal/resources/grafana/resource_annotation_test.go @@ -31,6 +31,7 @@ func TestAccAnnotation_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( annotationsCheckExists.exists("grafana_annotation.test", &annotation), resource.TestCheckResourceAttr("grafana_annotation.test", "text", testAccAnnotationInitialText), + testutils.CheckLister("grafana_annotation.test"), ), }, { diff --git a/internal/resources/grafana/resource_dashboard_test.go b/internal/resources/grafana/resource_dashboard_test.go index 8aa005eaf..1461e8125 100644 --- a/internal/resources/grafana/resource_dashboard_test.go +++ b/internal/resources/grafana/resource_dashboard_test.go @@ -51,6 +51,7 @@ func TestAccDashboard_basic(t *testing.T) { resource.TestCheckResourceAttr( "grafana_dashboard.test", "config_json", expectedInitialConfig, ), + testutils.CheckLister("grafana_dashboard.test"), ), }, { @@ -238,6 +239,7 @@ func TestAccDashboard_inOrg(t *testing.T) { checkResourceIsInOrg("grafana_dashboard.test", "grafana_organization.test"), testAccDashboardCheckExistsInFolder(&dashboard, &folder), + testutils.CheckLister("grafana_dashboard.test"), ), }, }, diff --git a/internal/resources/grafana/resource_data_source_test.go b/internal/resources/grafana/resource_data_source_test.go index 99c7d4699..5bb14b96a 100644 --- a/internal/resources/grafana/resource_data_source_test.go +++ b/internal/resources/grafana/resource_data_source_test.go @@ -59,6 +59,7 @@ func TestAccDataSource_Loki(t *testing.T) { resource.TestCheckResourceAttr("grafana_data_source.loki", "name", dsName), resource.TestCheckResourceAttr("grafana_data_source.loki", "type", "loki"), resource.TestCheckResourceAttr("grafana_data_source.loki", "url", "http://acc-test.invalid/"), + testutils.CheckLister("grafana_data_source.loki"), func(s *terraform.State) error { jsonData := dataSource.JSONData.(map[string]interface{}) if jsonData["derivedFields"] == nil { diff --git a/internal/resources/grafana/resource_folder_test.go b/internal/resources/grafana/resource_folder_test.go index c9422b0e1..30d027d3f 100644 --- a/internal/resources/grafana/resource_folder_test.go +++ b/internal/resources/grafana/resource_folder_test.go @@ -46,6 +46,7 @@ func TestAccFolder_basic(t *testing.T) { resource.TestCheckResourceAttr("grafana_folder.test_folder_with_uid", "uid", "test-folder-uid"), resource.TestCheckResourceAttr("grafana_folder.test_folder_with_uid", "title", "Terraform Test Folder With UID"), resource.TestCheckResourceAttr("grafana_folder.test_folder_with_uid", "url", strings.TrimRight(os.Getenv("GRAFANA_URL"), "/")+"/dashboards/f/test-folder-uid/terraform-test-folder-with-uid"), + testutils.CheckLister("grafana_folder.test_folder_with_uid"), ), }, { diff --git a/internal/resources/grafana/resource_library_panel_test.go b/internal/resources/grafana/resource_library_panel_test.go index 079159122..b004e3c0e 100644 --- a/internal/resources/grafana/resource_library_panel_test.go +++ b/internal/resources/grafana/resource_library_panel_test.go @@ -32,6 +32,7 @@ func TestAccLibraryPanel_basic(t *testing.T) { resource.TestCheckResourceAttr("grafana_library_panel.test", "name", name), resource.TestCheckResourceAttr("grafana_library_panel.test", "version", "1"), resource.TestCheckResourceAttr("grafana_library_panel.test", "model_json", fmt.Sprintf(`{"description":"","title":"%s","type":""}`, name)), + testutils.CheckLister("grafana_library_panel.test"), ), }, { diff --git a/internal/resources/grafana/resource_playlist_test.go b/internal/resources/grafana/resource_playlist_test.go index 39ee0aab8..3eb5f5156 100644 --- a/internal/resources/grafana/resource_playlist_test.go +++ b/internal/resources/grafana/resource_playlist_test.go @@ -39,6 +39,7 @@ func TestAccPlaylist_basic(t *testing.T) { "order": "2", "title": "Terraform Dashboard By ID", }), + testutils.CheckLister(paylistResource), ), }, { diff --git a/internal/resources/grafana/resource_report_test.go b/internal/resources/grafana/resource_report_test.go index b468c0cd4..d1150bd28 100644 --- a/internal/resources/grafana/resource_report_test.go +++ b/internal/resources/grafana/resource_report_test.go @@ -54,6 +54,7 @@ func TestAccResourceReport_Multiple_Dashboards(t *testing.T) { resource.TestCheckResourceAttr("grafana_report.test", "dashboards.1.time_range.0.from", ""), resource.TestCheckResourceAttr("grafana_report.test", "dashboards.1.time_range.0.to", ""), resource.TestCheckResourceAttr("grafana_report.test", "dashboards.1.uid", randomUID2), + testutils.CheckLister("grafana_report.test"), ), }, }, diff --git a/internal/resources/grafana/resource_service_account_test.go b/internal/resources/grafana/resource_service_account_test.go index 548e883f2..db1ece995 100644 --- a/internal/resources/grafana/resource_service_account_test.go +++ b/internal/resources/grafana/resource_service_account_test.go @@ -34,6 +34,7 @@ func TestAccServiceAccount_basic(t *testing.T) { resource.TestCheckResourceAttr("grafana_service_account.test", "role", "Editor"), resource.TestCheckResourceAttr("grafana_service_account.test", "is_disabled", "false"), resource.TestMatchResourceAttr("grafana_service_account.test", "id", defaultOrgIDRegexp), + testutils.CheckLister("grafana_service_account.test"), ), }, // Change the name. Check that the ID stays the same. diff --git a/internal/resources/grafana/resource_team_test.go b/internal/resources/grafana/resource_team_test.go index 4d88ae802..48e9b8158 100644 --- a/internal/resources/grafana/resource_team_test.go +++ b/internal/resources/grafana/resource_team_test.go @@ -33,6 +33,7 @@ func TestAccTeam_basic(t *testing.T) { resource.TestCheckResourceAttr("grafana_team.test", "email", teamName+"@example.com"), resource.TestMatchResourceAttr("grafana_team.test", "id", defaultOrgIDRegexp), resource.TestCheckResourceAttr("grafana_team.test", "org_id", "1"), + testutils.CheckLister("grafana_team.test"), ), }, { diff --git a/internal/resources/machinelearning/resource_holiday_test.go b/internal/resources/machinelearning/resource_holiday_test.go index 93fe3ebfe..b0e20f176 100644 --- a/internal/resources/machinelearning/resource_holiday_test.go +++ b/internal/resources/machinelearning/resource_holiday_test.go @@ -32,6 +32,7 @@ func TestAccResourceHoliday(t *testing.T) { testAccMLHolidayCheckExists("grafana_machine_learning_holiday.ical", &holiday), resource.TestCheckResourceAttrSet("grafana_machine_learning_holiday.ical", "id"), resource.TestCheckResourceAttr("grafana_machine_learning_holiday.ical", "name", randomName), + testutils.CheckLister("grafana_machine_learning_holiday.ical"), ), }, { diff --git a/internal/resources/machinelearning/resource_job_test.go b/internal/resources/machinelearning/resource_job_test.go index 43684d7df..8bd86dd45 100644 --- a/internal/resources/machinelearning/resource_job_test.go +++ b/internal/resources/machinelearning/resource_job_test.go @@ -42,6 +42,7 @@ func TestAccResourceJob(t *testing.T) { resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "query_params.expr", "grafanacloud_grafana_instance_active_user_count"), resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "interval", "300"), resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "training_window", "7776000"), + testutils.CheckLister("grafana_machine_learning_job.test_job"), ), }, { diff --git a/internal/resources/machinelearning/resource_outlier_detector_test.go b/internal/resources/machinelearning/resource_outlier_detector_test.go index 5b05840aa..5b13d3d23 100644 --- a/internal/resources/machinelearning/resource_outlier_detector_test.go +++ b/internal/resources/machinelearning/resource_outlier_detector_test.go @@ -39,6 +39,7 @@ func TestAccResourceOutlierDetector(t *testing.T) { resource.TestCheckResourceAttr("grafana_machine_learning_outlier_detector.my_mad_outlier_detector", "interval", "300"), resource.TestCheckResourceAttr("grafana_machine_learning_outlier_detector.my_mad_outlier_detector", "algorithm.0.name", "mad"), resource.TestCheckResourceAttr("grafana_machine_learning_outlier_detector.my_mad_outlier_detector", "algorithm.0.sensitivity", "0.7"), + testutils.CheckLister("grafana_machine_learning_outlier_detector.my_mad_outlier_detector"), ), }, { diff --git a/internal/resources/slo/resource_slo_test.go b/internal/resources/slo/resource_slo_test.go index 73697e4ae..90381ea8c 100644 --- a/internal/resources/slo/resource_slo_test.go +++ b/internal/resources/slo/resource_slo_test.go @@ -40,6 +40,7 @@ func TestAccResourceSlo(t *testing.T) { resource.TestCheckResourceAttr("grafana_slo.test", "objectives.0.value", "0.995"), resource.TestCheckResourceAttr("grafana_slo.test", "objectives.0.window", "30d"), resource.TestCheckNoResourceAttr("grafana_slo.test", "folder_uid"), + testutils.CheckLister("grafana_slo.test"), ), }, { diff --git a/internal/resources/syntheticmonitoring/resource_check_test.go b/internal/resources/syntheticmonitoring/resource_check_test.go index 56509a6e1..3dabbe7e1 100644 --- a/internal/resources/syntheticmonitoring/resource_check_test.go +++ b/internal/resources/syntheticmonitoring/resource_check_test.go @@ -43,6 +43,7 @@ func TestAccResourceCheck_dns(t *testing.T) { resource.TestCheckResourceAttr("grafana_synthetic_monitoring_check.dns", "settings.0.dns.0.port", "53"), resource.TestCheckResourceAttr("grafana_synthetic_monitoring_check.dns", "settings.0.dns.0.record_type", "A"), resource.TestCheckResourceAttr("grafana_synthetic_monitoring_check.dns", "settings.0.dns.0.protocol", "UDP"), + testutils.CheckLister("grafana_synthetic_monitoring_check.dns"), ), }, { diff --git a/internal/resources/syntheticmonitoring/resource_probe_test.go b/internal/resources/syntheticmonitoring/resource_probe_test.go index 475d351fa..4bd795ac4 100644 --- a/internal/resources/syntheticmonitoring/resource_probe_test.go +++ b/internal/resources/syntheticmonitoring/resource_probe_test.go @@ -36,6 +36,7 @@ func TestAccResourceProbe(t *testing.T) { resource.TestCheckResourceAttr("grafana_synthetic_monitoring_probe.main", "public", "false"), resource.TestCheckResourceAttr("grafana_synthetic_monitoring_probe.main", "labels.type", "mountain"), resource.TestCheckResourceAttr("grafana_synthetic_monitoring_probe.main", "disable_scripted_checks", "false"), + testutils.CheckLister("grafana_synthetic_monitoring_probe.main"), ), }, { diff --git a/internal/testutils/lister.go b/internal/testutils/lister.go new file mode 100644 index 000000000..102c55d55 --- /dev/null +++ b/internal/testutils/lister.go @@ -0,0 +1,67 @@ +package testutils + +import ( + "context" + "fmt" + "os" + + "github.com/grafana/terraform-provider-grafana/v3/internal/common" + "github.com/grafana/terraform-provider-grafana/v3/internal/resources/cloud" + "github.com/grafana/terraform-provider-grafana/v3/internal/resources/grafana" + "github.com/grafana/terraform-provider-grafana/v3/pkg/provider" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +// CheckLister is a resource.TestCheckFunc that checks that the resource's lister +// function returns the given ID. +// This is meant to be used at least once in every resource's tests to ensure that +// the resource's lister function is working correctly. +func CheckLister(terraformResource string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Get the resource from the state + rs, ok := s.RootModule().Resources[terraformResource] + if !ok { + return fmt.Errorf("resource not found: %s", terraformResource) + } + id := rs.Primary.ID + + // Find the resource info + var resource *common.Resource + for _, r := range provider.Resources() { + if r.Name == rs.Type { + resource = r + break + } + } + if resource == nil { + return fmt.Errorf("resource type %s not found", rs.Type) + } + + // Get the resource's lister function + lister := resource.ListIDsFunc + if lister == nil { + return fmt.Errorf("resource %s does not have a lister function", terraformResource) + } + + // Get the list of IDs from the lister function + ctx := context.Background() + var listerData any = grafana.NewListerData(false) + if resource.Category == common.CategoryCloud { + listerData = cloud.NewListerData(os.Getenv("GRAFANA_CLOUD_ORG")) + } + ids, err := lister(ctx, Provider.Meta().(*common.Client), listerData) + if err != nil { + return fmt.Errorf("error listing %s: %w", terraformResource, err) + } + + // Check that the ID is in the list + for _, i := range ids { + if i == id { + return nil + } + } + + return fmt.Errorf("resource %s with ID %s not found in list: %v", terraformResource, id, ids) + } +}