feat: add_testing (#45)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 13s
Publish / Publish provider (push) Successful in 23m29s

## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #45
Reviewed-by: Andre_Harms <andre.harms@stackit.cloud>
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
This commit is contained in:
Marcel S. Henselin 2026-02-10 16:46:21 +00:00 committed by Marcel_Henselin
parent 4991897eca
commit e21fe64326
Signed by: tf-provider.git.onstackit.cloud
GPG key ID: 6D7E8A1ED8955A9C
30 changed files with 2097 additions and 1803 deletions

View file

@ -1,280 +0,0 @@
package postgresflexalpha
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/testutil"
)
var (
validFlavor = "2.4"
kekKeyRingId = ""
kekKeyVersion = ""
kekKeySA = ""
)
func testAccPreCheck(t *testing.T) {
// TODO: if needed ...
}
//func TestAccResourceExample_parallel(t *testing.T) {
// t.Parallel()
//
// exData := resData{
// Region: "eu01",
// ServiceAccountFilePath: sa_file,
// ProjectID: project_id,
// Name: acctest.RandomWithPrefix("tf-acc"),
// }
//
// resource.Test(t, resource.TestCase{
// ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
// Steps: []resource.TestStep{
// {
// Config: testAccResourceEncryptionExampleConfig(exData),
// Check: resource.TestCheckResourceAttrSet("example_resource.test", "id"),
// },
// },
// })
//}
type resData struct {
ServiceAccountFilePath string
ProjectID string
Region string
Name string
Flavor string
BackupSchedule string
RetentionDays uint32
}
func getExample() resData {
return resData{
Region: testutil.Region,
ServiceAccountFilePath: testutil.ServiceAccountFile,
ProjectID: testutil.ProjectId,
Name: acctest.RandomWithPrefix("tf-acc"),
Flavor: "2.4",
BackupSchedule: "0 0 * * *",
RetentionDays: 33,
}
}
func TestAccResourceExample_basic(t *testing.T) {
exData := getExample()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccResourceNoEncryptionExampleConfig(exData),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("example_resource.test", "name", exData.Name),
resource.TestCheckResourceAttrSet("example_resource.test", "id"),
),
},
//// Create and verify
//{
// Config: testAccResourceNoEncryptionExampleConfig(exData),
// Check: resource.ComposeTestCheckFunc(
// resource.TestCheckResourceAttr("example_resource.test", "name", exData.Name),
// ),
//},
//// Update and verify
//{
// Config: testAccResourceNoEncryptionExampleConfig(exData),
// Check: resource.ComposeTestCheckFunc(
// resource.TestCheckResourceAttr("example_resource.test", "name", exData.Name),
// ),
//},
// Import test
{
ResourceName: "example_resource.test",
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func testAccResourceNoEncryptionExampleConfig(data resData) string {
return fmt.Sprintf(`
%[1]s
resource "stackitprivatepreview_postgresflexalpha_instance" "test" {
project_id = %[2]q
name = %[3]q
backup_schedule = %[4]q
retention_days = %[5]d
flavor_id = %[6]q
replicas = 1
storage = {
performance_class = "premium-perf2-stackit"
size = 10
}
network = {
acl = ["0.0.0.0/0"]
access_scope = "PUBLIC"
}
version = 17
}
`,
testutil.PostgresFlexProviderConfig(data.ServiceAccountFilePath),
data.ProjectID,
data.Name,
data.BackupSchedule,
data.RetentionDays,
data.Flavor,
data.Name,
)
}
func testAccResourceEncryptionExampleConfig(data resData) string {
return fmt.Sprintf(`
%[1]s
resource "stackitprivatepreview_postgresflexalpha_instance" "test" {
project_id = %[2]q
name = %[3]q
backup_schedule = "0 0 * * *"
retention_days = 45
flavor_id = "2.1"
replicas = 1
storage = {
performance_class = "premium-perf2-stackit"
size = 10
}
encryption = {
kek_key_id = "key01"
kek_key_ring_id = "key_ring_01"
kek_key_version = 1
service_account = "service@account.email"
}
network = {
acl = ["0.0.0.0/0"]
access_scope = "PUBLIC"
}
version = 14
}
`,
testutil.PostgresFlexProviderConfig(data.ServiceAccountFilePath),
data.ProjectID,
data.Name,
)
}
func testCheckResourceExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("resource not found: %s", resourceName)
}
if rs.Primary.ID == "" {
return fmt.Errorf("resource ID not set")
}
// Verify resource exists in the API
//client := testAccProvider.Meta().(*APIClient)
//_, err := client.GetResource(rs.Primary.ID)
//if err != nil {
// return fmt.Errorf("error fetching resource: %w", err)
//}
return nil
}
}
func setupMockServer() *httptest.Server {
mux := http.NewServeMux()
mux.HandleFunc("/api/resources", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]string{
"id": "mock-id-123",
"name": "test-resource",
})
case http.MethodGet:
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode([]map[string]string{})
}
})
return httptest.NewServer(mux)
}
func TestUnitResourceCreate(t *testing.T) {
server := setupMockServer()
defer server.Close()
// Configure provider to use mock server URL
os.Setenv("API_ENDPOINT", server.URL)
// Run unit tests against mock
}
// type postgresFlexClientMocked struct {
// returnError bool
// getFlavorsResp *postgresflex.GetFlavorsResponse
// }
//
// func (c *postgresFlexClientMocked) ListFlavorsExecute(_ context.Context, _, _ string) (*postgresflex.GetFlavorsResponse, error) {
// if c.returnError {
// return nil, fmt.Errorf("get flavors failed")
// }
//
// return c.getFlavorsResp, nil
// }
//func TestNewInstanceResource(t *testing.T) {
// exData := resData{
// Region: "eu01",
// ServiceAccountFilePath: sa_file,
// ProjectID: project_id,
// Name: "testRes",
// }
// resource.Test(t, resource.TestCase{
// ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
// Steps: []resource.TestStep{
// {
// Config: testAccResourceEncryptionExampleConfig(exData),
// Check: resource.ComposeAggregateTestCheckFunc(
// resource.TestCheckResourceAttr("example_resource.test", "name", exData.Name),
// resource.TestCheckResourceAttrSet("example_resource.test", "id"),
// ),
// },
// },
// })
//
// //tests := []struct {
// // name string
// // want resource.Resource
// //}{
// // {
// // name: "create empty instance resource",
// // want: &instanceResource{},
// // },
// //}
// //for _, tt := range tests {
// // t.Run(tt.name, func(t *testing.T) {
// // if got := NewInstanceResource(); !reflect.DeepEqual(got, tt.want) {
// // t.Errorf("NewInstanceResource() = %v, want %v", got, tt.want)
// // }
// // })
// //}
//}

View file

@ -1,33 +0,0 @@
package postgresflexalpha
import (
"context"
"testing"
// The fwresource import alias is so there is no collision
// with the more typical acceptance testing import:
// "github.com/hashicorp/terraform-plugin-testing/helper/resource"
fwresource "github.com/hashicorp/terraform-plugin-framework/resource"
)
func TestInstanceResourceSchema(t *testing.T) {
t.Parallel()
ctx := context.Background()
schemaRequest := fwresource.SchemaRequest{}
schemaResponse := &fwresource.SchemaResponse{}
// Instantiate the resource.Resource and call its Schema method
NewInstanceResource().Schema(ctx, schemaRequest, schemaResponse)
if schemaResponse.Diagnostics.HasError() {
t.Fatalf("Schema method diagnostics: %+v", schemaResponse.Diagnostics)
}
// Validate the schema
diagnostics := schemaResponse.Schema.ValidateImplementation(ctx)
if diagnostics.HasError() {
t.Fatalf("Schema validation diagnostics: %+v", diagnostics)
}
}

View file

@ -0,0 +1,30 @@
provider "stackitprivatepreview" {
default_region = "{{ .Region }}"
service_account_key_path = "{{ .ServiceAccountFilePath }}"
}
resource "stackitprivatepreview_postgresflexalpha_instance" "{{ .TfName }}" {
project_id = "{{ .ProjectId }}"
name = "{{ .Name }}"
backup_schedule = "{{ .BackupSchedule }}"
retention_days = {{ .RetentionDays }}
flavor_id = "{{ .FlavorId }}"
replicas = {{ .Replicas }}
storage = {
performance_class = "{{ .PerformanceClass }}"
size = {{ .Size }}
}
{{ if .UseEncryption }}
encryption = {
kek_key_id = {{ .KekKeyId }}
kek_key_ring_id = {{ .KekKeyRingId }}
kek_key_version = {{ .KekKeyVersion }}
service_account = "{{ .KekServiceAccount }}"
}
{{ end }}
network = {
acl = ["{{ .AclString }}"]
access_scope = "{{ .AccessScope }}"
}
version = {{ .Version }}
}

View file

@ -1,23 +1,26 @@
variable "project_id" {}
variable "kek_key_id" {}
variable "kek_key_ring_id" {}
resource "stackitprivatepreview_postgresflexalpha_instance" "msh-instance-only" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
project_id = var.project_id
name = "example-instance"
acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"]
backup_schedule = "0 0 * * *"
retention_days = 30
flavor_id = "flavor.id"
flavor_id = "2.4"
replicas = 1
storage = {
performance_class = "premium-perf2-stackit"
size = 10
}
encryption = {
kek_key_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
kek_key_ring_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
kek_key_id = var.kek_key_id
kek_key_ring_id = var.kek_key_ring_id
kek_key_version = 1
service_account = "service@account.email"
}
network = {
acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"]
acl = ["0.0.0.0/0"]
access_scope = "PUBLIC"
}
version = 17

View file

@ -0,0 +1,19 @@
variable "project_id" {}
resource "stackitprivatepreview_postgresflexalpha_instance" "msh-instance-only" {
project_id = var.project_id
name = "example-instance"
backup_schedule = "0 0 * * *"
retention_days = 30
flavor_id = "2.4"
replicas = 1
storage = {
performance_class = "premium-perf2-stackit"
size = 10
}
network = {
acl = ["0.0.0.0/0"]
access_scope = "PUBLIC"
}
version = 17
}

View file

@ -111,7 +111,7 @@ func mapResourceFields(userResp *postgresflex.GetUserResponse, model *resourceMo
user := userResp
var userId int64
if model.UserId.ValueInt64() != 0 {
if !model.UserId.IsNull() && !model.UserId.IsUnknown() && model.UserId.ValueInt64() != 0 {
userId = model.UserId.ValueInt64()
} else if user.Id != nil {
userId = *user.Id

View file

@ -10,7 +10,6 @@ import (
"strconv"
"strings"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
postgresflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha"
@ -503,45 +502,6 @@ func (r *userResource) IdentitySchema(
}
}
func mapFields(userResp *postgresflex.GetUserResponse, model *resourceModel, region string) error {
if userResp == nil {
return fmt.Errorf("response is nil")
}
if model == nil {
return fmt.Errorf("model input is nil")
}
user := userResp
var userId int64
if model.UserId.ValueInt64() != 0 {
userId = model.UserId.ValueInt64()
} else if user.Id != nil {
userId = *user.Id
} else {
return fmt.Errorf("user id not present")
}
model.UserId = types.Int64Value(userId)
model.Name = types.StringPointerValue(user.Name)
if user.Roles == nil {
model.Roles = types.List(types.SetNull(types.StringType))
} else {
var roles []attr.Value
for _, role := range *user.Roles {
roles = append(roles, types.StringValue(string(role)))
}
rolesSet, diags := types.SetValue(types.StringType, roles)
if diags.HasError() {
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
}
model.Roles = types.List(rolesSet)
}
model.Region = types.StringValue(region)
model.Status = types.StringPointerValue(user.Status)
return nil
}
// getUserResource refreshes the resource state by calling the API and mapping the response to the model.
// Returns true if the resource state was successfully refreshed, false if the resource does not exist.
func (r *userResource) getUserResource(ctx context.Context, model *resourceModel, arg *clientArg) (bool, error) {