From d8e165a261f428fffb5d16f5bf129ec06d09ec3d Mon Sep 17 00:00:00 2001 From: Alistair Yan <52126234+1riatsila1@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:01:11 +0400 Subject: [PATCH] feat: sensor acceptance tests (#83) --- internal/provider/resources/sensor.go | 43 ++++------ test/live/datasources/sensor_test.go | 6 +- test/live/resources/sensor_test.go | 116 ++++++++++++++++++++++++++ test/live/util/sensor.go | 58 +++++++------ 4 files changed, 168 insertions(+), 55 deletions(-) create mode 100644 test/live/resources/sensor_test.go diff --git a/internal/provider/resources/sensor.go b/internal/provider/resources/sensor.go index cc3a971d..8c88abbd 100644 --- a/internal/provider/resources/sensor.go +++ b/internal/provider/resources/sensor.go @@ -13,7 +13,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) -// Ensure the implementation satisfies the expected interfaces. var ( _ resource.Resource = &sensorResource{} _ resource.ResourceWithConfigure = &sensorResource{} @@ -61,12 +60,18 @@ func (r *sensorResource) Schema( }, "address_note": schema.StringAttribute{ Optional: true, + // computed because goes from nil -> "" when sensor becomes configured + Computed: true, }, "notes": schema.StringAttribute{ Optional: true, + // computed because goes from from nil -> "" when sensor becomes configured + Computed: true, }, "pcap_mode": schema.StringAttribute{ Optional: true, + // computed because goes from from nil -> "light" when sensor becomes configured + Computed: true, }, }, } @@ -77,8 +82,6 @@ func (r *sensorResource) Configure( req resource.ConfigureRequest, resp *resource.ConfigureResponse, ) { - // Add a nil check when handling ProviderData because Terraform - // sets that data after it calls the ConfigureProvider RPC. if req.ProviderData == nil { return } @@ -101,7 +104,6 @@ func (r *sensorResource) Create( req resource.CreateRequest, resp *resource.CreateResponse, ) { - // Retrieve values from plan var plan sensorResourceModel diags := req.Plan.Get(ctx, &plan) diags.AddError( @@ -116,7 +118,6 @@ func (r *sensorResource) Read( req resource.ReadRequest, resp *resource.ReadResponse, ) { - // Get current state var state sensorResourceModel diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) @@ -143,14 +144,12 @@ func (r *sensorResource) Read( } sensor := sensorResponse.Items[0] - // Update state from client response state.ID = types.StringValue(sensor.Id) state.Name = types.StringValue(sensor.Name) state.AddressNote = types.StringPointerValue(sensor.AddressNote.Get()) state.Notes = types.StringPointerValue(sensor.Notes.Get()) state.PCapMode = types.StringPointerValue(sensor.PcapMode.Get()) - // Set refreshed state diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -163,7 +162,6 @@ func (r *sensorResource) Update( req resource.UpdateRequest, resp *resource.UpdateResponse, ) { - // Retrieve values from plan var plan sensorResourceModel diags := req.Plan.Get(ctx, &plan) resp.Diagnostics.Append(diags...) @@ -172,15 +170,11 @@ func (r *sensorResource) Update( } patchRequest := config_api_client.NewSensorsPatchRequest() - if !plan.AddressNote.IsUnknown() { - patchRequest.AddressNote = plan.AddressNote.ValueStringPointer() - } - if !plan.Notes.IsUnknown() { - patchRequest.Notes = plan.Notes.ValueStringPointer() - } - if !plan.PCapMode.IsUnknown() { - patchRequest.PcapMode = plan.PCapMode.ValueStringPointer() - } + patchRequest.Name = plan.Name.ValueStringPointer() + patchRequest.AddressNote = plan.AddressNote.ValueStringPointer() + patchRequest.Notes = plan.Notes.ValueStringPointer() + patchRequest.PcapMode = plan.PCapMode.ValueStringPointer() + request := r.client.ConfigurationAPI. SensorsPatch(ctx, plan.ID.ValueString()). SensorsPatchRequest(*patchRequest) @@ -193,20 +187,12 @@ func (r *sensorResource) Update( return } - // Update the state to match the plan (replace with response from client) plan.ID = types.StringValue(sensor.Id) plan.Name = types.StringValue(sensor.Name) - if sensor.AddressNote.Get() != nil { - plan.AddressNote = types.StringValue(*sensor.AddressNote.Get()) - } - if sensor.Notes.Get() != nil { - plan.Notes = types.StringValue(*sensor.Notes.Get()) - } - if sensor.PcapMode.Get() != nil { - plan.PCapMode = types.StringValue(*sensor.PcapMode.Get()) - } + plan.AddressNote = types.StringPointerValue(sensor.AddressNote.Get()) + plan.Notes = types.StringPointerValue(sensor.Notes.Get()) + plan.PCapMode = types.StringPointerValue(sensor.PcapMode.Get()) - // Set state to fully populated data diags = resp.State.Set(ctx, plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -219,7 +205,6 @@ func (r *sensorResource) Delete( req resource.DeleteRequest, resp *resource.DeleteResponse, ) { - // Retrieve values from state var state sensorResourceModel diags := req.State.Get(ctx, &state) diags.AddError( diff --git a/test/live/datasources/sensor_test.go b/test/live/datasources/sensor_test.go index d9c3475b..d31133b8 100644 --- a/test/live/datasources/sensor_test.go +++ b/test/live/datasources/sensor_test.go @@ -22,7 +22,11 @@ func TestSensorDataSource(t *testing.T) { } } `, - Check: util.CheckStateAgainstSensor(t, sensor), + Check: util.CheckDataSourceStateAgainstSensor( + t, + "data.uxi_sensor.my_sensor", + sensor, + ), }, }, }) diff --git a/test/live/resources/sensor_test.go b/test/live/resources/sensor_test.go new file mode 100644 index 00000000..9bdfbfe9 --- /dev/null +++ b/test/live/resources/sensor_test.go @@ -0,0 +1,116 @@ +package resource_test + +import ( + config_api_client "github.com/aruba-uxi/terraform-provider-hpeuxi/pkg/config-api-client" + "github.com/aruba-uxi/terraform-provider-hpeuxi/test/live/config" + "github.com/aruba-uxi/terraform-provider-hpeuxi/test/live/provider" + "github.com/aruba-uxi/terraform-provider-hpeuxi/test/live/util" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + + "regexp" + "testing" +) + +func TestSensorResource(t *testing.T) { + originalSensor := util.GetSensorProperties(config.SensorUid) + updatedNotes := "tf_provider_acceptance_test_update_notes" + updatedAddressNote := "tf_provider_acceptance_test_update_address_note" + updatedPcapMode := "off" + updatedSensor := config_api_client.SensorItem{ + Id: config.SensorUid, + Name: "tf_provider_acceptance_test_update_name", + Notes: *config_api_client.NewNullableString(&updatedNotes), + AddressNote: *config_api_client.NewNullableString(&updatedAddressNote), + PcapMode: *config_api_client.NewNullableString(&updatedPcapMode), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: provider.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + // we required terraform 1.7.0 and above for the `removed` block + tfversion.RequireAbove(tfversion.Version1_7_0), + }, + Steps: []resource.TestStep{ + // Creating a sensor is not allowed + { + Config: provider.ProviderConfig + ` + resource "uxi_sensor" "my_sensor" { + name = "` + originalSensor.Name + `" + }`, + + ExpectError: regexp.MustCompile( + `creating a sensor is not supported; sensors can only be imported`, + ), + }, + // Importing a sensor + { + Config: provider.ProviderConfig + ` + resource "uxi_sensor" "my_sensor" { + name = "` + originalSensor.Name + `" + } + + import { + to = uxi_sensor.my_sensor + id = "` + config.SensorUid + `" + }`, + + Check: resource.ComposeAggregateTestCheckFunc(), + }, + // ImportState testing + { + ResourceName: "uxi_sensor.my_sensor", + ImportState: true, + ImportStateVerify: true, + }, + // Update testing + { + Config: provider.ProviderConfig + ` + resource "uxi_sensor" "my_sensor" { + name = "` + updatedSensor.Name + `" + address_note = "` + updatedSensor.GetAddressNote() + `" + notes = "` + updatedSensor.GetNotes() + `" + pcap_mode = "` + updatedSensor.GetPcapMode() + `" + }`, + Check: util.CheckResourceStateAgainstSensor( + t, + "uxi_sensor.my_sensor", + updatedSensor, + ), + }, + // Update sensor back to original + { + Config: provider.ProviderConfig + ` + resource "uxi_sensor" "my_sensor" { + name = "` + originalSensor.Name + `" + address_note = "` + originalSensor.GetAddressNote() + `" + notes = "` + originalSensor.GetNotes() + `" + pcap_mode = "` + originalSensor.GetPcapMode() + `" + }`, + Check: util.CheckResourceStateAgainstSensor( + t, + "uxi_sensor.my_sensor", + originalSensor, + ), + }, + // Deleting a sensor is not allowed + { + Config: provider.ProviderConfig + ``, + ExpectError: regexp.MustCompile( + `deleting a sensor is not supported; sensors can only removed from state`, + ), + }, + // Remove sensor from state + { + Config: provider.ProviderConfig + ` + removed { + from = uxi_sensor.my_sensor + + lifecycle { + destroy = false + } + }`, + }, + }, + }) +} diff --git a/test/live/util/sensor.go b/test/live/util/sensor.go index b1e6209b..2e8068c6 100644 --- a/test/live/util/sensor.go +++ b/test/live/util/sensor.go @@ -23,42 +23,50 @@ func GetSensorProperties(id string) config_api_client.SensorItem { return result.Items[0] } -func CheckStateAgainstSensor( +func CheckDataSourceStateAgainstSensor( t st.Fatalf, + entity string, sensor config_api_client.SensorItem, ) resource.TestCheckFunc { return resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("data.uxi_sensor.my_sensor", "id", config.SensorUid), - resource.TestCheckResourceAttr("data.uxi_sensor.my_sensor", "serial", sensor.Serial), - resource.TestCheckResourceAttr( - "data.uxi_sensor.my_sensor", - "model_number", - sensor.ModelNumber, - ), + resource.TestCheckResourceAttr(entity, "id", config.SensorUid), + resource.TestCheckResourceAttr(entity, "serial", sensor.Serial), + resource.TestCheckResourceAttr(entity, "model_number", sensor.ModelNumber), resource.TestCheckResourceAttrWith( - "data.uxi_sensor.my_sensor", + entity, "name", func(value string) error { st.Assert(t, value, sensor.Name) return nil }, ), - TestOptionalValue( - t, - "data.uxi_sensor.my_sensor", - "wifi_mac_address", - sensor.WifiMacAddress.Get(), - ), - TestOptionalValue( - t, - "data.uxi_sensor.my_sensor", - "ethernet_mac_address", - sensor.EthernetMacAddress.Get(), + TestOptionalValue(t, entity, "wifi_mac_address", sensor.WifiMacAddress.Get()), + TestOptionalValue(t, entity, "ethernet_mac_address", sensor.EthernetMacAddress.Get()), + TestOptionalValue(t, entity, "address_note", sensor.AddressNote.Get()), + TestOptionalFloatValue(t, entity, "latitude", sensor.Latitude.Get()), + TestOptionalFloatValue(t, entity, "longitude", sensor.Longitude.Get()), + TestOptionalValue(t, entity, "notes", sensor.Notes.Get()), + TestOptionalValue(t, entity, "pcap_mode", sensor.PcapMode.Get()), + ) +} + +func CheckResourceStateAgainstSensor( + t st.Fatalf, + entity string, + sensor config_api_client.SensorItem, +) resource.TestCheckFunc { + return resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(entity, "id", config.SensorUid), + resource.TestCheckResourceAttrWith( + entity, + "name", + func(value string) error { + st.Assert(t, value, sensor.Name) + return nil + }, ), - TestOptionalValue(t, "data.uxi_sensor.my_sensor", "address_note", sensor.AddressNote.Get()), - TestOptionalFloatValue(t, "data.uxi_sensor.my_sensor", "latitude", sensor.Latitude.Get()), - TestOptionalFloatValue(t, "data.uxi_sensor.my_sensor", "longitude", sensor.Longitude.Get()), - TestOptionalValue(t, "data.uxi_sensor.my_sensor", "notes", sensor.Notes.Get()), - TestOptionalValue(t, "data.uxi_sensor.my_sensor", "pcap_mode", sensor.PcapMode.Get()), + TestOptionalValue(t, entity, "address_note", sensor.AddressNote.Get()), + TestOptionalValue(t, entity, "notes", sensor.Notes.Get()), + TestOptionalValue(t, entity, "pcap_mode", sensor.PcapMode.Get()), ) }