From 7920befd680b479dec5a3a50171164cb8a76e8c8 Mon Sep 17 00:00:00 2001 From: David Bloss Date: Wed, 23 Oct 2024 16:32:29 -0500 Subject: [PATCH] WIP: update tests and CodeIssue update logic --- .../resource_opslevel_check_code_issue.go | 30 ++- tests/check_code_issue.tftest.hcl | 206 ++++++++++++++---- tests/cli/main.tf | 9 +- tests/cli/variables.tf | 11 + 4 files changed, 196 insertions(+), 60 deletions(-) diff --git a/opslevel/resource_opslevel_check_code_issue.go b/opslevel/resource_opslevel_check_code_issue.go index a68bae33..d8da3574 100644 --- a/opslevel/resource_opslevel_check_code_issue.go +++ b/opslevel/resource_opslevel_check_code_issue.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/path" @@ -121,6 +122,7 @@ func (r *CheckCodeIssueResource) Schema(ctx context.Context, req resource.Schema "max_allowed": schema.Int64Attribute{ Description: "The threshold count of code issues beyond which the check starts failing.", Optional: true, + Validators: []validator.Int64{int64validator.AtLeast(0)}, }, "resolution_time": schema.SingleNestedAttribute{ Description: "Defines the minimum frequency of the updates.", @@ -146,9 +148,6 @@ func (r *CheckCodeIssueResource) Schema(ctx context.Context, req resource.Schema } } -// func (r *CheckCodeIssueResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) { -// } - func (r *CheckCodeIssueResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { var planModel CheckCodeIssueResourceModel @@ -249,6 +248,7 @@ func (r *CheckCodeIssueResource) Update(ctx context.Context, req resource.Update Enabled: planModel.Enabled.ValueBoolPointer(), FilterId: opslevel.RefOf(asID(planModel.Filter)), Id: asID(planModel.Id), + IssueName: planModel.IssueName.ValueStringPointer(), LevelId: opslevel.RefOf(asID(planModel.Level)), Name: opslevel.RefOf(planModel.Name.ValueString()), Notes: opslevel.RefOf(planModel.Notes.ValueString()), @@ -261,23 +261,31 @@ func (r *CheckCodeIssueResource) Update(ctx context.Context, req resource.Update } input.EnableOn = &iso8601.Time{Time: enabledOn} } - if !planModel.IssueType.IsNull() { + if planModel.IssueType.IsNull() { + input.IssueType = opslevel.NewNullOf[[]string]() + } else { issueType, _ := ListValueToStringSlice(ctx, planModel.IssueType) - input.IssueType = opslevel.RefOf(issueType) + input.IssueType = opslevel.NewNullableFrom(issueType) } - if !planModel.MaxAllowed.IsNull() { - input.MaxAllowed = opslevel.RefOf(int(planModel.MaxAllowed.ValueInt64())) + if planModel.MaxAllowed.IsNull() { + input.MaxAllowed = opslevel.NewNullOf[int]() + } else { + input.MaxAllowed = opslevel.NewNullableFrom(int(planModel.MaxAllowed.ValueInt64())) } - if !planModel.ResolutionTime.IsNull() { + if planModel.ResolutionTime.IsNull() { + input.ResolutionTime = opslevel.NewNullOf[opslevel.CodeIssueResolutionTimeInput]() + } else { attrs := planModel.ResolutionTime.Attributes() - input.ResolutionTime = opslevel.RefOf(opslevel.CodeIssueResolutionTimeInput{ + input.ResolutionTime = opslevel.NewNullableFrom(opslevel.CodeIssueResolutionTimeInput{ Unit: opslevel.CodeIssueResolutionTimeUnitEnum(attrs["unit"].(basetypes.StringValue).ValueString()), Value: int(attrs["value"].(basetypes.Int64Value).ValueInt64()), }) } - if !planModel.Severity.IsNull() { + if planModel.Severity.IsNull() { + input.Severity = opslevel.NewNullOf[[]string]() + } else { severity, _ := ListValueToStringSlice(ctx, planModel.Severity) - input.Severity = opslevel.RefOf(severity) + input.Severity = opslevel.NewNullableFrom(severity) } data, err := r.client.UpdateCheckCodeIssue(input) diff --git a/tests/check_code_issue.tftest.hcl b/tests/check_code_issue.tftest.hcl index 0cd5be1c..9e93edfd 100644 --- a/tests/check_code_issue.tftest.hcl +++ b/tests/check_code_issue.tftest.hcl @@ -6,7 +6,7 @@ variables { constraint = "any" # optional fields - issue_name = null + issue_name = "idk" issue_type = ["snyk:code"] max_allowed = 5 resolution_time = { @@ -45,14 +45,16 @@ run "from_data_module" { } } -run "resource_check_code_issue_create_with_all_fields" { +run "resource_check_code_issue_create_with_constraint_any" { variables { # other fields from file scoped variables block - category = run.from_data_module.first_rubric_category.id - filter = run.from_data_module.first_filter.id - level = run.from_data_module.max_index_rubric_level.id - owner = run.from_data_module.first_team.id + category = run.from_data_module.first_rubric_category.id + constraint = "any" + issue_name = null # not allowed when constraint is "any" + filter = run.from_data_module.first_filter.id + level = run.from_data_module.max_index_rubric_level.id + owner = run.from_data_module.first_team.id } module { @@ -62,15 +64,21 @@ run "resource_check_code_issue_create_with_all_fields" { assert { condition = alltrue([ can(opslevel_check_code_issue.this.category), + can(opslevel_check_code_issue.this.constraint), can(opslevel_check_code_issue.this.description), can(opslevel_check_code_issue.this.enable_on), can(opslevel_check_code_issue.this.enabled), can(opslevel_check_code_issue.this.filter), can(opslevel_check_code_issue.this.id), + can(opslevel_check_code_issue.this.issue_name), + can(opslevel_check_code_issue.this.issue_type), can(opslevel_check_code_issue.this.level), + can(opslevel_check_code_issue.this.max_allowed), can(opslevel_check_code_issue.this.name), can(opslevel_check_code_issue.this.notes), can(opslevel_check_code_issue.this.owner), + can(opslevel_check_code_issue.this.resolution_time), + can(opslevel_check_code_issue.this.severity), ]) error_message = replace(var.error_unexpected_resource_fields, "TYPE", var.resource_name) } @@ -85,6 +93,15 @@ run "resource_check_code_issue_create_with_all_fields" { ) } + assert { + condition = opslevel_check_code_issue.this.constraint == var.constraint + error_message = format( + "expected '%v' but got '%v'", + var.constraint, + opslevel_check_code_issue.this.constraint, + ) + } + assert { condition = opslevel_check_code_issue.this.enable_on == var.enable_on error_message = format( @@ -108,6 +125,24 @@ run "resource_check_code_issue_create_with_all_fields" { error_message = replace(var.error_wrong_id, "TYPE", var.resource_name) } + assert { + condition = opslevel_check_code_issue.this.issue_name == var.issue_name + error_message = format( + "expected '%v' but got '%v'", + var.issue_name, + opslevel_check_code_issue.this.issue_name, + ) + } + + assert { + condition = opslevel_check_code_issue.this.issue_type == var.issue_type + error_message = format( + "expected '%v' but got '%v'", + var.issue_type, + opslevel_check_code_issue.this.issue_type, + ) + } + assert { condition = opslevel_check_code_issue.this.filter == var.filter error_message = format( @@ -126,6 +161,15 @@ run "resource_check_code_issue_create_with_all_fields" { ) } + assert { + condition = opslevel_check_code_issue.this.max_allowed == var.max_allowed + error_message = format( + "expected '%v' but got '%v'", + var.max_allowed, + opslevel_check_code_issue.this.max_allowed, + ) + } + assert { condition = opslevel_check_code_issue.this.name == var.name error_message = format( @@ -153,19 +197,42 @@ run "resource_check_code_issue_create_with_all_fields" { ) } + assert { + condition = opslevel_check_code_issue.this.resolution_time == var.resolution_time + error_message = format( + "expected '%v' but got '%v'", + var.resolution_time, + opslevel_check_code_issue.this.resolution_time, + ) + } + + assert { + condition = opslevel_check_code_issue.this.severity == var.severity + error_message = format( + "expected '%v' but got '%v'", + var.severity, + opslevel_check_code_issue.this.severity, + ) + } + } run "resource_check_code_issue_unset_optional_fields" { variables { # other fields from file scoped variables block - category = run.from_data_module.first_rubric_category.id - enable_on = null - enabled = null - filter = null - level = run.from_data_module.max_index_rubric_level.id - notes = null - owner = null + category = run.from_data_module.first_rubric_category.id + constraint = "any" + enable_on = null + enabled = null + issue_name = null # not allowed when constraint is "any" + issue_type = null + filter = null + level = run.from_data_module.max_index_rubric_level.id + max_allowed = null + notes = null + owner = null + # resolution_time = null } module { @@ -226,10 +293,16 @@ run "resource_check_code_issue_unset_optional_fields" { } -run "delete_check_code_issue_outside_of_terraform" { +run "delete_check_code_issue_with_constraint_any_outside_of_terraform" { + + plan_options { + target = [ + terraform_data.opslevel_cli, + ] + } variables { - command = "delete check ${run.resource_check_code_issue_create_with_all_fields.this.id}" + command = "delete check ${run.resource_check_code_issue_create_with_constraint_any.this.id}" } module { @@ -237,38 +310,51 @@ run "delete_check_code_issue_outside_of_terraform" { } } -run "resource_check_code_issue_create_with_required_fields" { +run "resource_check_code_issue_create_with_constraint_contains" { variables { # other fields from file scoped variables block - category = run.from_data_module.first_rubric_category.id - enable_on = null - enabled = null - filter = null - level = run.from_data_module.max_index_rubric_level.id - notes = null - owner = null + category = run.from_data_module.first_rubric_category.id + constraint = "contains" + enable_on = null + enabled = null + filter = null + issue_name = "w" # required when constraint is "contains" + issue_type = null # not allowed when constraint is "contains" + level = run.from_data_module.max_index_rubric_level.id + notes = null + owner = null + severity = null # not allowed when constraint is "contains" } module { source = "./opslevel_modules/modules/check/code_issue" } + # assert { + # condition = run.resource_check_code_issue_create_with_constraint_any.this.id != opslevel_check_code_issue.this.id + # error_message = format( + # "expected old id '%v' to be different from new id '%v'", + # run.resource_check_code_issue_create_with_constraint_any.this.id, + # opslevel_check_code_issue.this.id, + # ) + # } + assert { - condition = run.resource_check_code_issue_create_with_all_fields.this.id != opslevel_check_code_issue.this.id + condition = opslevel_check_code_issue.this.category == var.category error_message = format( - "expected old id '%v' to be different from new id '%v'", - run.resource_check_code_issue_create_with_all_fields.this.id, - opslevel_check_code_issue.this.id, + "expected '%v' but got '%v'", + var.category, + opslevel_check_code_issue.this.category, ) } assert { - condition = opslevel_check_code_issue.this.category == var.category + condition = opslevel_check_code_issue.this.constraint == var.constraint error_message = format( "expected '%v' but got '%v'", - var.category, - opslevel_check_code_issue.this.category, + var.constraint, + opslevel_check_code_issue.this.constraint, ) } @@ -317,14 +403,36 @@ run "resource_check_code_issue_create_with_required_fields" { } -run "resource_check_code_issue_set_all_fields" { +run "delete_check_code_issue_with_constraint_contains_outside_of_terraform" { + + plan_options { + target = [ + terraform_data.delete_command, + ] + + } + variables { + resource_id = run.resource_check_code_issue_create_with_constraint_contains.this.id + resource_type = "check" + } + + module { + source = "./cli" + } +} + +run "resource_check_code_issue_create_with_constraint_exact" { variables { # other fields from file scoped variables block - category = run.from_data_module.first_rubric_category.id - filter = run.from_data_module.first_filter.id - level = run.from_data_module.max_index_rubric_level.id - owner = run.from_data_module.first_team.id + category = run.from_data_module.first_rubric_category.id + constraint = "exact" + filter = run.from_data_module.first_filter.id + issue_type = null # not allowed when constraint is "exact" + level = run.from_data_module.max_index_rubric_level.id + max_allowed = null # not allowed when constraint is "exact" + owner = run.from_data_module.first_team.id + severity = null # not allowed when constraint is "exact" } module { @@ -332,19 +440,12 @@ run "resource_check_code_issue_set_all_fields" { } assert { - condition = alltrue([ - can(opslevel_check_code_issue.this.category), - can(opslevel_check_code_issue.this.description), - can(opslevel_check_code_issue.this.enable_on), - can(opslevel_check_code_issue.this.enabled), - can(opslevel_check_code_issue.this.filter), - can(opslevel_check_code_issue.this.id), - can(opslevel_check_code_issue.this.level), - can(opslevel_check_code_issue.this.name), - can(opslevel_check_code_issue.this.notes), - can(opslevel_check_code_issue.this.owner), - ]) - error_message = replace(var.error_unexpected_resource_fields, "TYPE", var.resource_name) + condition = run.resource_check_code_issue_create_with_constraint_contains.this.id != opslevel_check_code_issue.this.id + error_message = format( + "expected old id '%v' to be different from new id '%v'", + run.resource_check_code_issue_create_with_constraint_contains.this.id, + opslevel_check_code_issue.this.id, + ) } assert { @@ -356,6 +457,15 @@ run "resource_check_code_issue_set_all_fields" { ) } + assert { + condition = opslevel_check_code_issue.this.constraint == var.constraint + error_message = format( + "expected '%v' but got '%v'", + var.constraint, + opslevel_check_code_issue.this.constraint, + ) + } + assert { condition = opslevel_check_code_issue.this.enable_on == var.enable_on error_message = format( diff --git a/tests/cli/main.tf b/tests/cli/main.tf index 366a09f8..e13e3fbe 100644 --- a/tests/cli/main.tf +++ b/tests/cli/main.tf @@ -1,5 +1,12 @@ resource "terraform_data" "opslevel_cli" { provisioner "local-exec" { - command = "opslevel ${var.command}" + command = var.command == null ? ":" : "opslevel ${var.command}" + } +} + +resource "terraform_data" "delete_command" { + provisioner "local-exec" { + # NOTE: ':' is a no-op bash command + command = var.resource_type == null ? ":" : "opslevel delete ${var.resource_type} ${var.resource_id}" } } diff --git a/tests/cli/variables.tf b/tests/cli/variables.tf index 9ae1a7f6..210e0514 100644 --- a/tests/cli/variables.tf +++ b/tests/cli/variables.tf @@ -1,4 +1,15 @@ variable "command" { type = string description = "OpsLevel CLI command to run - everything after 'opslevel'" + default = null +} + +variable "resource_id" { + type = string + default = null +} + +variable "resource_type" { + type = string + default = null }