308 lines
9.2 KiB
Go
308 lines
9.2 KiB
Go
package cdn
|
|
|
|
import (
|
|
"context"
|
|
cryptoRand "crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/base64"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"math/big"
|
|
"testing"
|
|
"time"
|
|
|
|
"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/hashicorp/terraform-plugin-framework/types/basetypes"
|
|
"github.com/stackitcloud/stackit-sdk-go/services/cdn"
|
|
)
|
|
|
|
func TestMapFields(t *testing.T) {
|
|
// Redefine certificateTypes locally for testing, matching the updated schema
|
|
certificateTypes := map[string]attr.Type{
|
|
"version": types.Int64Type,
|
|
"certificate": types.StringType,
|
|
"private_key": types.StringType,
|
|
}
|
|
|
|
const dummyCert = "dummy-cert-pem"
|
|
const dummyKey = "dummy-key-pem"
|
|
|
|
emtpyErrorsList := types.ListValueMust(types.StringType, []attr.Value{})
|
|
|
|
// Expected object when a custom certificate is returned
|
|
certAttributes := map[string]attr.Value{
|
|
"version": types.Int64Value(3),
|
|
"certificate": types.StringValue(dummyCert),
|
|
"private_key": types.StringValue(dummyKey),
|
|
}
|
|
certificateObj, _ := types.ObjectValue(certificateTypes, certAttributes)
|
|
|
|
expectedModel := func(mods ...func(*CustomDomainModel)) *CustomDomainModel {
|
|
model := &CustomDomainModel{
|
|
ID: types.StringValue("test-project-id,test-distribution-id,https://testdomain.com"),
|
|
DistributionId: types.StringValue("test-distribution-id"),
|
|
ProjectId: types.StringValue("test-project-id"),
|
|
Status: types.StringValue("ACTIVE"),
|
|
Errors: emtpyErrorsList,
|
|
Certificate: types.ObjectUnknown(certificateTypes),
|
|
}
|
|
for _, mod := range mods {
|
|
mod(model)
|
|
}
|
|
return model
|
|
}
|
|
|
|
customType := "custom"
|
|
customVersion := int64(3)
|
|
getRespCustom := cdn.GetCustomDomainResponseGetCertificateAttributeType(&cdn.GetCustomDomainResponseCertificate{
|
|
GetCustomDomainCustomCertificate: &cdn.GetCustomDomainCustomCertificate{
|
|
Type: &customType,
|
|
Version: &customVersion,
|
|
},
|
|
})
|
|
|
|
managedType := "managed"
|
|
getRespManaged := cdn.GetCustomDomainResponseGetCertificateAttributeType(&cdn.GetCustomDomainResponseCertificate{
|
|
GetCustomDomainManagedCertificate: &cdn.GetCustomDomainManagedCertificate{
|
|
Type: &managedType,
|
|
},
|
|
})
|
|
|
|
customDomainFixture := func(mods ...func(*cdn.GetCustomDomainResponse)) *cdn.GetCustomDomainResponse {
|
|
distribution := &cdn.CustomDomain{
|
|
Errors: &[]cdn.StatusError{},
|
|
Name: cdn.PtrString("https://testdomain.com"),
|
|
Status: cdn.DOMAINSTATUS_ACTIVE.Ptr(),
|
|
}
|
|
customDomainResponse := &cdn.GetCustomDomainResponse{
|
|
CustomDomain: distribution,
|
|
Certificate: getRespCustom,
|
|
}
|
|
|
|
for _, mod := range mods {
|
|
mod(customDomainResponse)
|
|
}
|
|
return customDomainResponse
|
|
}
|
|
|
|
tests := map[string]struct {
|
|
Input *cdn.GetCustomDomainResponse
|
|
Certificate interface{}
|
|
Expected *CustomDomainModel
|
|
InitialModel *CustomDomainModel
|
|
IsValid bool
|
|
SkipInitialNil bool
|
|
}{
|
|
"happy_path_custom_cert": {
|
|
Expected: expectedModel(func(m *CustomDomainModel) {
|
|
m.Certificate = certificateObj
|
|
}),
|
|
Input: customDomainFixture(),
|
|
IsValid: true,
|
|
InitialModel: expectedModel(func(m *CustomDomainModel) {
|
|
m.Certificate = basetypes.NewObjectValueMust(certificateTypes, map[string]attr.Value{
|
|
"certificate": types.StringValue(dummyCert),
|
|
"private_key": types.StringValue(dummyKey),
|
|
"version": types.Int64Null(),
|
|
})
|
|
}),
|
|
},
|
|
"happy_path_managed_cert": {
|
|
Expected: expectedModel(func(m *CustomDomainModel) {
|
|
m.Certificate = types.ObjectNull(certificateTypes)
|
|
}),
|
|
Input: customDomainFixture(func(gcdr *cdn.GetCustomDomainResponse) {
|
|
gcdr.Certificate = getRespManaged
|
|
}),
|
|
IsValid: true,
|
|
InitialModel: expectedModel(func(m *CustomDomainModel) { m.Certificate = types.ObjectNull(certificateTypes) }),
|
|
},
|
|
"happy_path_status_error": {
|
|
Expected: expectedModel(func(m *CustomDomainModel) {
|
|
m.Status = types.StringValue("ERROR")
|
|
m.Certificate = certificateObj
|
|
}),
|
|
Input: customDomainFixture(func(d *cdn.GetCustomDomainResponse) {
|
|
d.CustomDomain.Status = cdn.DOMAINSTATUS_ERROR.Ptr()
|
|
}),
|
|
IsValid: true,
|
|
InitialModel: expectedModel(func(m *CustomDomainModel) {
|
|
m.Certificate = basetypes.NewObjectValueMust(certificateTypes, map[string]attr.Value{
|
|
"certificate": types.StringValue(dummyCert),
|
|
"private_key": types.StringValue(dummyKey),
|
|
"version": types.Int64Null(),
|
|
})
|
|
}),
|
|
},
|
|
"sad_path_custom_domain_nil": {
|
|
Expected: expectedModel(),
|
|
Input: nil,
|
|
IsValid: false,
|
|
InitialModel: &CustomDomainModel{},
|
|
},
|
|
"sad_path_name_missing": {
|
|
Expected: expectedModel(),
|
|
Input: customDomainFixture(func(d *cdn.GetCustomDomainResponse) {
|
|
d.CustomDomain.Name = nil
|
|
}),
|
|
IsValid: false,
|
|
InitialModel: &CustomDomainModel{},
|
|
},
|
|
}
|
|
for tn, tc := range tests {
|
|
t.Run(tn, func(t *testing.T) {
|
|
model := tc.InitialModel
|
|
model.DistributionId = tc.Expected.DistributionId
|
|
model.ProjectId = tc.Expected.ProjectId
|
|
err := mapCustomDomainResourceFields(tc.Input, model)
|
|
if err != nil && tc.IsValid {
|
|
t.Fatalf("Error mapping fields: %v", err)
|
|
}
|
|
if err == nil && !tc.IsValid {
|
|
t.Fatalf("Should have failed")
|
|
}
|
|
if tc.IsValid {
|
|
diff := cmp.Diff(tc.Expected, model)
|
|
if diff != "" {
|
|
t.Fatalf("Mapped model not as expected (-want +got):\n%s", diff)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func makeCertAndKey(t *testing.T, organization string) (cert, key []byte) {
|
|
privateKey, err := rsa.GenerateKey(cryptoRand.Reader, 2048)
|
|
if err != nil {
|
|
t.Fatalf("failed to generate key: %s", err.Error())
|
|
}
|
|
template := x509.Certificate{
|
|
SerialNumber: big.NewInt(1),
|
|
Issuer: pkix.Name{CommonName: organization},
|
|
Subject: pkix.Name{
|
|
Organization: []string{organization},
|
|
},
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().Add(time.Hour),
|
|
|
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
|
BasicConstraintsValid: true,
|
|
}
|
|
cert, err = x509.CreateCertificate(
|
|
cryptoRand.Reader,
|
|
&template,
|
|
&template,
|
|
&privateKey.PublicKey,
|
|
privateKey,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("failed to generate cert: %s", err.Error())
|
|
}
|
|
|
|
return pem.EncodeToMemory(&pem.Block{
|
|
Type: "CERTIFICATE",
|
|
Bytes: cert,
|
|
}), pem.EncodeToMemory(&pem.Block{
|
|
Type: "RSA PRIVATE KEY",
|
|
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
|
|
})
|
|
}
|
|
func TestToCertificatePayload(t *testing.T) {
|
|
organization := fmt.Sprintf("organization-%s", uuid.NewString())
|
|
cert, key := makeCertAndKey(t, organization)
|
|
certPEM := string(cert)
|
|
keyPEM := string(key)
|
|
certBase64 := base64.StdEncoding.EncodeToString(cert)
|
|
keyBase64 := base64.StdEncoding.EncodeToString(key)
|
|
|
|
tests := map[string]struct {
|
|
model *CustomDomainModel
|
|
expectedPayload *cdn.PutCustomDomainPayloadCertificate
|
|
expectErr bool
|
|
expectedErrMsg string
|
|
}{
|
|
"success_managed_when_certificate_block_is_nil": {
|
|
model: &CustomDomainModel{
|
|
Certificate: types.ObjectNull(certificateTypes),
|
|
},
|
|
expectedPayload: &cdn.PutCustomDomainPayloadCertificate{
|
|
PutCustomDomainManagedCertificate: cdn.NewPutCustomDomainManagedCertificate("managed"),
|
|
},
|
|
expectErr: false,
|
|
},
|
|
"success_custom_certificate": {
|
|
model: &CustomDomainModel{
|
|
Certificate: basetypes.NewObjectValueMust(
|
|
certificateTypes,
|
|
map[string]attr.Value{
|
|
"version": types.Int64Null(),
|
|
"certificate": types.StringValue(certPEM),
|
|
"private_key": types.StringValue(keyPEM),
|
|
},
|
|
),
|
|
},
|
|
expectedPayload: &cdn.PutCustomDomainPayloadCertificate{
|
|
PutCustomDomainCustomCertificate: cdn.NewPutCustomDomainCustomCertificate(certBase64, keyBase64, "custom"),
|
|
},
|
|
expectErr: false,
|
|
},
|
|
"fail_custom_missing_cert_value": {
|
|
model: &CustomDomainModel{
|
|
Certificate: basetypes.NewObjectValueMust(
|
|
certificateTypes,
|
|
map[string]attr.Value{
|
|
"version": types.Int64Null(),
|
|
"certificate": types.StringValue(""), // Empty certificate
|
|
"private_key": types.StringValue(keyPEM),
|
|
},
|
|
),
|
|
},
|
|
expectErr: true,
|
|
expectedErrMsg: "invalid certificate or private key. Please check if the string of the public certificate and private key in PEM format",
|
|
},
|
|
|
|
"success_managed_when_certificate_attributes_are_nil": {
|
|
model: &CustomDomainModel{
|
|
Certificate: basetypes.NewObjectValueMust(
|
|
certificateTypes,
|
|
map[string]attr.Value{
|
|
"version": types.Int64Null(),
|
|
"certificate": types.StringNull(),
|
|
"private_key": types.StringNull(),
|
|
},
|
|
),
|
|
},
|
|
expectErr: true,
|
|
expectedErrMsg: `"certificate" and "private_key" must be set`,
|
|
},
|
|
}
|
|
|
|
for name, tt := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
payload, err := toCertificatePayload(context.Background(), tt.model)
|
|
if tt.expectErr {
|
|
if err == nil {
|
|
t.Fatalf("expected err, but got none")
|
|
}
|
|
if err.Error() != tt.expectedErrMsg {
|
|
t.Fatalf("expected err '%s', got '%s'", tt.expectedErrMsg, err.Error())
|
|
}
|
|
return // Test ends here for failing cases
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatalf("did not expect err, but got: %s", err.Error())
|
|
}
|
|
|
|
if diff := cmp.Diff(tt.expectedPayload, payload); diff != "" {
|
|
t.Errorf("payload mismatch (-want +got):\n%s", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|