terraform-provider-stackitp.../stackit/internal/services/iaasalpha/routingtable/route/resource_test.go
Ruben Hönle 9ff9b8f610
feat(iaas): add experimental support for routing tables and routes (#896)
* Merged PR 788126: feat(iaas): Onboard routing tables

feat(iaas): Onboard routing tables

Signed-off-by: Alexander Dahmen <alexander.dahmen@inovex.de>

* Merged PR 793350: fix(routingtable): region attribute is missing in scheme

fix(routingtable): region attribute is missing in scheme

Signed-off-by: Alexander Dahmen <alexander.dahmen@inovex.de>

* Merged PR 797968: feat(iaas): onboarding of routing table routes

relates to STACKITTPR-241

* use iaasalpha sdk from github

* resolve todos

* remove routes from routing table model

* restructure packages

* acc tests routing tables

* add acc tests for routes

* chore(iaas): mark routing table resources as experimental

* chore(deps): use iaasalpha sdk v0.1.19-alpha

* Review feedback

Signed-off-by: Alexander Dahmen <alexander.dahmen@inovex.de>

---------

Signed-off-by: Alexander Dahmen <alexander.dahmen@inovex.de>
Co-authored-by: Alexander Dahmen (EXT) <Alexander.Dahmen_ext@external.mail.schwarz>
Co-authored-by: Alexander Dahmen <alexander.dahmen@inovex.de>
2025-07-02 10:30:50 +02:00

452 lines
11 KiB
Go

package route
import (
"context"
"fmt"
"reflect"
"testing"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaasalpha/routingtable/shared"
"github.com/google/go-cmp/cmp"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha"
)
const (
testRegion = "eu02"
)
var (
organizationId = uuid.New()
networkAreaId = uuid.New()
routingTableId = uuid.New()
routeId = uuid.New()
)
func Test_mapFieldsFromList(t *testing.T) {
type args struct {
routeResp *iaasalpha.RouteListResponse
model *shared.RouteModel
region string
}
tests := []struct {
name string
args args
wantErr bool
expectedModel *shared.RouteModel
}{
{
name: "response is nil",
args: args{
model: &shared.RouteModel{},
routeResp: nil,
},
wantErr: true,
},
{
name: "response items is nil",
args: args{
model: &shared.RouteModel{},
routeResp: &iaasalpha.RouteListResponse{
Items: nil,
},
},
wantErr: true,
},
{
name: "model is nil",
args: args{
model: nil,
routeResp: &iaasalpha.RouteListResponse{
Items: nil,
},
},
wantErr: true,
},
{
name: "response items is empty",
args: args{
model: &shared.RouteModel{},
routeResp: &iaasalpha.RouteListResponse{
Items: &[]iaasalpha.Route{},
},
},
wantErr: true,
},
{
name: "response items contains more than one route",
args: args{
model: &shared.RouteModel{},
routeResp: &iaasalpha.RouteListResponse{
Items: &[]iaasalpha.Route{
{
Id: utils.Ptr(uuid.NewString()),
},
{
Id: utils.Ptr(uuid.NewString()),
},
},
},
},
wantErr: true,
},
{
name: "success",
args: args{
model: &shared.RouteModel{
RouteReadModel: shared.RouteReadModel{
RouteId: types.StringNull(),
},
RoutingTableId: types.StringValue(routingTableId.String()),
OrganizationId: types.StringValue(organizationId.String()),
NetworkAreaId: types.StringValue(networkAreaId.String()),
},
routeResp: &iaasalpha.RouteListResponse{
Items: &[]iaasalpha.Route{
{
Id: utils.Ptr(routeId.String()),
Destination: utils.Ptr(iaasalpha.DestinationCIDRv4AsRouteDestination(
iaasalpha.NewDestinationCIDRv4("cidrv4", "58.251.236.138/32"),
)),
Nexthop: utils.Ptr(iaasalpha.NexthopIPv4AsRouteNexthop(
iaasalpha.NewNexthopIPv4("ipv4", "10.20.42.2"),
)),
Labels: &map[string]interface{}{
"foo": "bar",
},
CreatedAt: nil,
UpdatedAt: nil,
},
},
},
region: testRegion,
},
wantErr: false,
expectedModel: &shared.RouteModel{
RouteReadModel: shared.RouteReadModel{
RouteId: types.StringValue(routeId.String()),
NextHop: types.ObjectValueMust(shared.RouteNextHopTypes, map[string]attr.Value{
"type": types.StringValue("ipv4"),
"value": types.StringValue("10.20.42.2"),
}),
Destination: types.ObjectValueMust(shared.RouteDestinationTypes, map[string]attr.Value{
"type": types.StringValue("cidrv4"),
"value": types.StringValue("58.251.236.138/32"),
}),
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
"foo": types.StringValue("bar"),
}),
CreatedAt: types.StringNull(),
UpdatedAt: types.StringNull(),
},
Id: types.StringValue(fmt.Sprintf("%s,%s,%s,%s,%s", organizationId.String(), testRegion, networkAreaId.String(), routingTableId.String(), routeId.String())),
RoutingTableId: types.StringValue(routingTableId.String()),
OrganizationId: types.StringValue(organizationId.String()),
NetworkAreaId: types.StringValue(networkAreaId.String()),
Region: types.StringValue(testRegion),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
if err := mapFieldsFromList(ctx, tt.args.routeResp, tt.args.model, tt.args.region); (err != nil) != tt.wantErr {
t.Errorf("mapFieldsFromList() error = %v, wantErr %v", err, tt.wantErr)
return
}
diff := cmp.Diff(tt.args.model, tt.expectedModel)
if diff != "" && !tt.wantErr {
t.Fatalf("mapFieldsFromList(): %s", diff)
}
})
}
}
func Test_toUpdatePayload(t *testing.T) {
type args struct {
model *shared.RouteModel
currentLabels types.Map
}
tests := []struct {
name string
args args
want *iaasalpha.UpdateRouteOfRoutingTablePayload
wantErr bool
}{
{
name: "model is nil",
args: args{
model: nil,
},
wantErr: true,
},
{
name: "max",
args: args{
model: &shared.RouteModel{
RouteReadModel: shared.RouteReadModel{
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
"foo1": types.StringValue("bar1"),
"foo2": types.StringValue("bar2"),
}),
},
},
currentLabels: types.MapValueMust(types.StringType, map[string]attr.Value{
"foo1": types.StringValue("foobar"),
"foo3": types.StringValue("bar3"),
}),
},
want: &iaasalpha.UpdateRouteOfRoutingTablePayload{
Labels: &map[string]interface{}{
"foo1": "bar1",
"foo2": "bar2",
"foo3": nil,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
got, err := toUpdatePayload(ctx, tt.args.model, tt.args.currentLabels)
if (err != nil) != tt.wantErr {
t.Errorf("toUpdatePayload() error = %v, wantErr %v", err, tt.wantErr)
return
}
diff := cmp.Diff(got, tt.want)
if diff != "" {
t.Fatalf("toUpdatePayload(): %s", diff)
}
})
}
}
func Test_toNextHopPayload(t *testing.T) {
type args struct {
model *shared.RouteReadModel
}
tests := []struct {
name string
args args
want *iaasalpha.RouteNexthop
wantErr bool
}{
{
name: "model is nil",
args: args{
model: nil,
},
wantErr: true,
},
{
name: "ipv4",
args: args{
model: &shared.RouteReadModel{
NextHop: types.ObjectValueMust(shared.RouteNextHopTypes, map[string]attr.Value{
"type": types.StringValue("ipv4"),
"value": types.StringValue("10.20.42.2"),
}),
},
},
wantErr: false,
want: utils.Ptr(iaasalpha.NexthopIPv4AsRouteNexthop(
iaasalpha.NewNexthopIPv4("ipv4", "10.20.42.2"),
)),
},
{
name: "ipv6",
args: args{
model: &shared.RouteReadModel{
NextHop: types.ObjectValueMust(shared.RouteNextHopTypes, map[string]attr.Value{
"type": types.StringValue("ipv6"),
"value": types.StringValue("172b:f881:46fe:d89a:9332:90f7:3485:236d"),
}),
},
},
wantErr: false,
want: utils.Ptr(iaasalpha.NexthopIPv6AsRouteNexthop(
iaasalpha.NewNexthopIPv6("ipv6", "172b:f881:46fe:d89a:9332:90f7:3485:236d"),
)),
},
{
name: "internet",
args: args{
model: &shared.RouteReadModel{
NextHop: types.ObjectValueMust(shared.RouteNextHopTypes, map[string]attr.Value{
"type": types.StringValue("internet"),
"value": types.StringNull(),
}),
},
},
wantErr: false,
want: utils.Ptr(iaasalpha.NexthopInternetAsRouteNexthop(
iaasalpha.NewNexthopInternet("internet"),
)),
},
{
name: "blackhole",
args: args{
model: &shared.RouteReadModel{
NextHop: types.ObjectValueMust(shared.RouteNextHopTypes, map[string]attr.Value{
"type": types.StringValue("blackhole"),
"value": types.StringNull(),
}),
},
},
wantErr: false,
want: utils.Ptr(iaasalpha.NexthopBlackholeAsRouteNexthop(
iaasalpha.NewNexthopBlackhole("blackhole"),
)),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
got, err := toNextHopPayload(ctx, tt.args.model)
if (err != nil) != tt.wantErr {
t.Errorf("toNextHopPayload() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("toNextHopPayload() got = %v, want %v", got, tt.want)
}
})
}
}
func Test_toDestinationPayload(t *testing.T) {
type args struct {
model *shared.RouteReadModel
}
tests := []struct {
name string
args args
want *iaasalpha.RouteDestination
wantErr bool
}{
{
name: "model is nil",
args: args{
model: nil,
},
wantErr: true,
},
{
name: "cidrv4",
args: args{
model: &shared.RouteReadModel{
Destination: types.ObjectValueMust(shared.RouteDestinationTypes, map[string]attr.Value{
"type": types.StringValue("cidrv4"),
"value": types.StringValue("58.251.236.138/32"),
}),
},
},
wantErr: false,
want: utils.Ptr(iaasalpha.DestinationCIDRv4AsRouteDestination(
iaasalpha.NewDestinationCIDRv4("cidrv4", "58.251.236.138/32"),
)),
},
{
name: "cidrv6",
args: args{
model: &shared.RouteReadModel{
Destination: types.ObjectValueMust(shared.RouteDestinationTypes, map[string]attr.Value{
"type": types.StringValue("cidrv6"),
"value": types.StringValue("2001:0db8:3c4d:1a2b::/64"),
}),
},
},
wantErr: false,
want: utils.Ptr(iaasalpha.DestinationCIDRv6AsRouteDestination(
iaasalpha.NewDestinationCIDRv6("cidrv6", "2001:0db8:3c4d:1a2b::/64"),
)),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
got, err := toDestinationPayload(ctx, tt.args.model)
if (err != nil) != tt.wantErr {
t.Errorf("toDestinationPayload() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("toDestinationPayload() got = %v, want %v", got, tt.want)
}
})
}
}
func Test_toCreatePayload(t *testing.T) {
type args struct {
model *shared.RouteReadModel
}
tests := []struct {
name string
args args
want *iaasalpha.AddRoutesToRoutingTablePayload
wantErr bool
}{
{
name: "model is nil",
args: args{
model: nil,
},
wantErr: true,
},
{
name: "max",
args: args{
model: &shared.RouteReadModel{
NextHop: types.ObjectValueMust(shared.RouteNextHopTypes, map[string]attr.Value{
"type": types.StringValue("ipv4"),
"value": types.StringValue("10.20.42.2"),
}),
Destination: types.ObjectValueMust(shared.RouteDestinationTypes, map[string]attr.Value{
"type": types.StringValue("cidrv4"),
"value": types.StringValue("58.251.236.138/32"),
}),
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
"foo1": types.StringValue("bar1"),
"foo2": types.StringValue("bar2"),
}),
},
},
want: &iaasalpha.AddRoutesToRoutingTablePayload{
Items: &[]iaasalpha.Route{
{
Labels: &map[string]interface{}{
"foo1": "bar1",
"foo2": "bar2",
},
Nexthop: utils.Ptr(iaasalpha.NexthopIPv4AsRouteNexthop(
iaasalpha.NewNexthopIPv4("ipv4", "10.20.42.2"),
)),
Destination: utils.Ptr(iaasalpha.DestinationCIDRv4AsRouteDestination(
iaasalpha.NewDestinationCIDRv4("cidrv4", "58.251.236.138/32"),
)),
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
got, err := toCreatePayload(ctx, tt.args.model)
if (err != nil) != tt.wantErr {
t.Errorf("toCreatePayload() error = %v, wantErr %v", err, tt.wantErr)
return
}
diff := cmp.Diff(got, tt.want)
if diff != "" {
t.Fatalf("toCreatePayload(): %s", diff)
}
})
}
}