diff --git a/internal/provider/notificationruleproject/notification_rule_project_resource_test.go b/internal/provider/notificationruleproject/notification_rule_project_resource_test.go new file mode 100644 index 0000000..963da73 --- /dev/null +++ b/internal/provider/notificationruleproject/notification_rule_project_resource_test.go @@ -0,0 +1,238 @@ +package notificationruleproject_test + +import ( + "fmt" + notificationruletestutils "github.com/futurice/terraform-provider-dependencytrack/internal/testutils/notificationrule" + "os" + "testing" + + "github.com/futurice/terraform-provider-dependencytrack/internal/testutils" + "github.com/futurice/terraform-provider-dependencytrack/internal/testutils/projecttestutils" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +var testDependencyTrack *testutils.TestDependencyTrack + +func TestMain(m *testing.M) { + if os.Getenv(resource.EnvTfAcc) != "" { + var cleanup func() + testDependencyTrack, cleanup = testutils.InitTestDependencyTrack() + defer cleanup() + } + + m.Run() +} + +func TestAccNotificationRuleProjectResource_basic(t *testing.T) { + ctx := testutils.CreateTestContext(t) + + publisherName := acctest.RandomWithPrefix("test-notification-publisher") + ruleName := acctest.RandomWithPrefix("test-notification-rule") + projectName := acctest.RandomWithPrefix("test-project") + + ruleResourceName := notificationruletestutils.CreateNotificationRuleResourceName("test") + otherRuleResourceName := notificationruletestutils.CreateNotificationRuleResourceName("test-other") + + projectResourceName := projecttestutils.CreateProjectResourceName("test") + otherProjectResourceName := projecttestutils.CreateProjectResourceName("test-other") + + notificationRuleProjectResourceName := notificationruletestutils.CreateNotificationRuleProjectResourceName("test") + + var ruleID, projectID, otherRuleID, otherProjectID string + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testutils.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccNotificationRuleProjectConfigBasic(testDependencyTrack, publisherName, ruleName, projectName), + Check: resource.ComposeAggregateTestCheckFunc( + testutils.TestAccCheckGetResourceID(ruleResourceName, &ruleID), + testutils.TestAccCheckGetResourceID(projectResourceName, &projectID), + notificationruletestutils.TestAccCheckNotificationRuleHasExpectedProjects(ctx, testDependencyTrack, ruleResourceName, []*string{&projectID}), + resource.TestCheckResourceAttrPtr(notificationRuleProjectResourceName, "rule_id", &ruleID), + resource.TestCheckResourceAttrPtr(notificationRuleProjectResourceName, "project_id", &projectID), + ), + }, + // TODO + //{ + // ResourceName: notificationRuleProjectResourceName, + // ImportState: true, + // ImportStateVerify: true, + //}, + { + Config: testAccNotificationRuleProjectConfigOtherRuleAndProject(testDependencyTrack, publisherName, ruleName, projectName), + Check: resource.ComposeAggregateTestCheckFunc( + testutils.TestAccCheckGetResourceID(otherRuleResourceName, &otherRuleID), + testutils.TestAccCheckGetResourceID(otherProjectResourceName, &otherProjectID), + notificationruletestutils.TestAccCheckNotificationRuleHasExpectedProjects(ctx, testDependencyTrack, ruleResourceName, []*string{}), + notificationruletestutils.TestAccCheckNotificationRuleHasExpectedProjects(ctx, testDependencyTrack, otherRuleResourceName, []*string{&otherProjectID}), + resource.TestCheckResourceAttrPtr(notificationRuleProjectResourceName, "rule_id", &otherRuleID), + resource.TestCheckResourceAttrPtr(notificationRuleProjectResourceName, "project_id", &otherProjectID), + ), + }, + { + Config: testAccNotificationRuleProjectConfigNoProject(testDependencyTrack, publisherName, ruleName, projectName), + Check: resource.ComposeAggregateTestCheckFunc( + notificationruletestutils.TestAccCheckNotificationRuleHasExpectedProjects(ctx, testDependencyTrack, ruleResourceName, []*string{}), + notificationruletestutils.TestAccCheckNotificationRuleHasExpectedProjects(ctx, testDependencyTrack, otherRuleResourceName, []*string{}), + ), + }, + }, + // CheckDestroy is not practical here since the notification rule is destroyed as well, and we can no longer query its projects + }) +} + +func testAccNotificationRuleProjectConfigBasic(testDependencyTrack *testutils.TestDependencyTrack, providerName, ruleName, projectName string) string { + return testDependencyTrack.AddProviderConfiguration( + testutils.ComposeConfigs( + fmt.Sprintf(` +resource "dependencytrack_notification_publisher" "test" { + name = %[1]q + publisher_class = "org.dependencytrack.notification.publisher.SlackPublisher" + template_mime_type = "application/json" + template = "{}" +} +`, + providerName, + ), + fmt.Sprintf(` +resource "dependencytrack_notification_rule" "test" { + name = %[1]q + publisher_id = dependencytrack_notification_publisher.test.id + scope = "PORTFOLIO" + notification_level = "INFORMATIONAL" +} +`, + ruleName, + ), + fmt.Sprintf(` +resource "dependencytrack_project" "test" { + name = %[1]q + classifier = "APPLICATION" +} +`, + projectName, + ), + ` +resource "dependencytrack_notification_rule_project" "test" { + rule_id = dependencytrack_notification_rule.test.id + project_id = dependencytrack_project.test.id +} +`, + ), + ) +} + +func testAccNotificationRuleProjectConfigOtherRuleAndProject(testDependencyTrack *testutils.TestDependencyTrack, providerName, ruleName, projectName string) string { + return testDependencyTrack.AddProviderConfiguration( + testutils.ComposeConfigs( + fmt.Sprintf(` +resource "dependencytrack_notification_publisher" "test" { + name = %[1]q + publisher_class = "org.dependencytrack.notification.publisher.SlackPublisher" + template_mime_type = "application/json" + template = "{}" +} +`, + providerName, + ), + fmt.Sprintf(` +resource "dependencytrack_notification_rule" "test" { + name = %[1]q + publisher_id = dependencytrack_notification_publisher.test.id + scope = "PORTFOLIO" + notification_level = "INFORMATIONAL" +} +`, + ruleName, + ), + fmt.Sprintf(` +resource "dependencytrack_notification_rule" "test-other" { + name = "%[1]s-other" + publisher_id = dependencytrack_notification_publisher.test.id + scope = "PORTFOLIO" + notification_level = "INFORMATIONAL" +} +`, + ruleName, + ), + fmt.Sprintf(` +resource "dependencytrack_project" "test" { + name = %[1]q + classifier = "APPLICATION" +} +`, + projectName, + ), + fmt.Sprintf(` +resource "dependencytrack_project" "test-other" { + name = "%[1]s-other" + classifier = "APPLICATION" +} +`, + projectName, + ), + ` +resource "dependencytrack_notification_rule_project" "test" { + rule_id = dependencytrack_notification_rule.test-other.id + project_id = dependencytrack_project.test-other.id +} +`, + ), + ) +} + +func testAccNotificationRuleProjectConfigNoProject(testDependencyTrack *testutils.TestDependencyTrack, providerName, ruleName, projectName string) string { + return testDependencyTrack.AddProviderConfiguration( + testutils.ComposeConfigs( + fmt.Sprintf(` +resource "dependencytrack_notification_publisher" "test" { + name = %[1]q + publisher_class = "org.dependencytrack.notification.publisher.SlackPublisher" + template_mime_type = "application/json" + template = "{}" +} +`, + providerName, + ), + fmt.Sprintf(` +resource "dependencytrack_notification_rule" "test" { + name = %[1]q + publisher_id = dependencytrack_notification_publisher.test.id + scope = "PORTFOLIO" + notification_level = "INFORMATIONAL" +} +`, + ruleName, + ), + fmt.Sprintf(` +resource "dependencytrack_notification_rule" "test-other" { + name = "%[1]s-other" + publisher_id = dependencytrack_notification_publisher.test.id + scope = "PORTFOLIO" + notification_level = "INFORMATIONAL" +} +`, + ruleName, + ), + fmt.Sprintf(` +resource "dependencytrack_project" "test" { + name = %[1]q + classifier = "APPLICATION" +} +`, + projectName, + ), + fmt.Sprintf(` +resource "dependencytrack_project" "test-other" { + name = "%[1]s-other" + classifier = "APPLICATION" +} +`, + projectName, + ), + ), + ) +} diff --git a/internal/testutils/notificationrule/notification_rule_test_utils.go b/internal/testutils/notificationrule/notification_rule_test_utils.go index b9cf0b7..dc0fae7 100644 --- a/internal/testutils/notificationrule/notification_rule_test_utils.go +++ b/internal/testutils/notificationrule/notification_rule_test_utils.go @@ -3,6 +3,8 @@ package notificationruletestutils import ( "context" "fmt" + "slices" + dtrack "github.com/futurice/dependency-track-client-go" "github.com/futurice/terraform-provider-dependencytrack/internal/testutils" "github.com/google/go-cmp/cmp" @@ -77,6 +79,39 @@ func FindNotificationRule(ctx context.Context, testDependencyTrack *testutils.Te return nil, nil } +func TestAccCheckNotificationRuleHasExpectedProjects(ctx context.Context, testDependencyTrack *testutils.TestDependencyTrack, resourceName string, expectedProjectIDs []*string) resource.TestCheckFunc { + return func(state *terraform.State) error { + rule, err := FindNotificationRuleByResourceName(ctx, testDependencyTrack, state, resourceName) + if err != nil { + return err + } + if rule == nil { + return fmt.Errorf("notification rule for resource %s does not exist in Dependency-Track", resourceName) + } + + if len(rule.Projects) != len(expectedProjectIDs) { + return fmt.Errorf("notification rule for resource %s has %d projects instead of the expected %d", resourceName, len(rule.Projects), len(expectedProjectIDs)) + } + + actualProjectIDs := make([]string, len(rule.Projects)) + for i, project := range rule.Projects { + actualProjectIDs[i] = project.UUID.String() + } + + for _, expectedProjectID := range expectedProjectIDs { + if !slices.Contains(actualProjectIDs, *expectedProjectID) { + return fmt.Errorf("notification rule for resource %s is missing expected project %s, got [%v]", resourceName, *expectedProjectID, actualProjectIDs) + } + } + + return nil + } +} + func CreateNotificationRuleResourceName(localName string) string { return fmt.Sprintf("dependencytrack_notification_rule.%s", localName) } + +func CreateNotificationRuleProjectResourceName(localName string) string { + return fmt.Sprintf("dependencytrack_notification_rule_project.%s", localName) +} diff --git a/internal/testutils/teamtestutils/team_test_utils.go b/internal/testutils/teamtestutils/team_test_utils.go index d1b50c3..3d0ac40 100644 --- a/internal/testutils/teamtestutils/team_test_utils.go +++ b/internal/testutils/teamtestutils/team_test_utils.go @@ -107,7 +107,7 @@ func TestAccCheckTeamHasExpectedACLMappings(ctx context.Context, testDependencyT for _, expectedACLMappingProjectID := range expectedACLMappingProjectIDs { if !slices.Contains(actualACLMappingProjectIDs, *expectedACLMappingProjectID) { - return fmt.Errorf("team for resource %s is missing expected permission %s, got [%v]", resourceName, *expectedACLMappingProjectID, actualACLMappingProjectIDs) + return fmt.Errorf("team for resource %s is missing expected ACL mapping project %s, got [%v]", resourceName, *expectedACLMappingProjectID, actualACLMappingProjectIDs) } }