chore: refactor tests
Some checks failed
CI Workflow / Check GoReleaser config (pull_request) Successful in 7s
CI Workflow / CI (pull_request) Failing after 9m39s
CI Workflow / Code coverage report (pull_request) Has been skipped
CI Workflow / Test readiness for publishing provider (pull_request) Successful in 30m48s
Some checks failed
CI Workflow / Check GoReleaser config (pull_request) Successful in 7s
CI Workflow / CI (pull_request) Failing after 9m39s
CI Workflow / Code coverage report (pull_request) Has been skipped
CI Workflow / Test readiness for publishing provider (pull_request) Successful in 30m48s
This commit is contained in:
parent
3505fb56f0
commit
1a2d04f044
15 changed files with 1088 additions and 294 deletions
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) STACKIT
|
||||
|
||||
package postgresflexalpha_test
|
||||
|
||||
import (
|
||||
|
|
@ -8,7 +6,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
|
|
@ -18,13 +15,12 @@ import (
|
|||
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
postgresflexalpha "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance"
|
||||
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/internal/testutils"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/testutil"
|
||||
|
||||
postgresflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha"
|
||||
// The fwresource import alias is so there is no collision
|
||||
|
|
@ -56,21 +52,15 @@ func TestInstanceResourceSchema(t *testing.T) {
|
|||
}
|
||||
|
||||
var (
|
||||
//go:embed testdata/resource-complete.tf
|
||||
resourceSecurityGroupMinConfig string //nolint:unused // needs implementation
|
||||
//go:embed testdata/resource-no-enc.tf
|
||||
resourceConfigNoEnc string //nolint:unused // needs implementation
|
||||
|
||||
//go:embed testdata/resource-enc.tf
|
||||
resourceConfigEnc string //nolint:unused // needs implementation
|
||||
)
|
||||
|
||||
func setup() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
slog.Info("could not find .env file - not loading .env")
|
||||
return
|
||||
}
|
||||
slog.Info("loaded .env file")
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
setup()
|
||||
testutils.Setup()
|
||||
code := m.Run()
|
||||
// shutdown()
|
||||
os.Exit(code)
|
||||
|
|
@ -84,7 +74,9 @@ func TestMain(m *testing.M) {
|
|||
//)
|
||||
|
||||
func testAccPreCheck(t *testing.T) {
|
||||
// TODO: if needed ...
|
||||
if _, ok := os.LookupEnv("TF_ACC_PROJECT_ID"); !ok {
|
||||
t.Fatalf("could not find env var TF_ACC_PROJECT_ID")
|
||||
}
|
||||
}
|
||||
|
||||
//func TestAccResourceExample_parallel(t *testing.T) {
|
||||
|
|
@ -98,7 +90,7 @@ func testAccPreCheck(t *testing.T) {
|
|||
// }
|
||||
//
|
||||
// resource.Test(t, resource.TestCase{
|
||||
// ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
// ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
|
||||
// Steps: []resource.TestStep{
|
||||
// {
|
||||
// Config: testAccResourceEncryptionExampleConfig(exData),
|
||||
|
|
@ -110,134 +102,105 @@ func testAccPreCheck(t *testing.T) {
|
|||
|
||||
type resData struct {
|
||||
ServiceAccountFilePath string
|
||||
ProjectID string
|
||||
ProjectId string
|
||||
Region string
|
||||
Name string
|
||||
Flavor string
|
||||
TfName string
|
||||
FlavorId string
|
||||
BackupSchedule string
|
||||
UseEncryption bool
|
||||
KekKeyId string
|
||||
KekKeyRingId string
|
||||
KekKeyVersion uint8
|
||||
KekServiceAccount string
|
||||
PerformanceClass string
|
||||
Replicas uint32
|
||||
Size uint32
|
||||
AclString string
|
||||
AccessScope string
|
||||
RetentionDays uint32
|
||||
Version string
|
||||
}
|
||||
|
||||
func getExample() resData {
|
||||
name := acctest.RandomWithPrefix("tf-acc")
|
||||
return resData{
|
||||
Region: testutil.Region,
|
||||
ServiceAccountFilePath: testutil.ServiceAccountFile,
|
||||
ProjectID: testutil.ProjectId,
|
||||
Name: acctest.RandomWithPrefix("tf-acc"),
|
||||
Flavor: "2.4",
|
||||
Region: os.Getenv("TF_ACC_REGION"),
|
||||
ServiceAccountFilePath: os.Getenv("TF_ACC_SERVICE_ACCOUNT_FILE"),
|
||||
ProjectId: os.Getenv("TF_ACC_PROJECT_ID"),
|
||||
Name: name,
|
||||
TfName: name,
|
||||
FlavorId: "2.4",
|
||||
BackupSchedule: "0 0 * * *",
|
||||
UseEncryption: false,
|
||||
RetentionDays: 33,
|
||||
Replicas: 1,
|
||||
PerformanceClass: "premium-perf2-stackit",
|
||||
Size: 10,
|
||||
AclString: "0.0.0.0/0",
|
||||
AccessScope: "PUBLIC",
|
||||
Version: "17",
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccResourceExample_basic(t *testing.T) {
|
||||
exData := getExample()
|
||||
resName := fmt.Sprintf(
|
||||
"stackitprivatepreview_postgresflexalpha_instance.%s",
|
||||
exData.TfName,
|
||||
)
|
||||
|
||||
updNameData := exData
|
||||
updNameData.Name = "name_updated"
|
||||
|
||||
updSizeData := exData
|
||||
updSizeData.Size = 25
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
// Create and verify
|
||||
{
|
||||
Config: testAccResourceNoEncryptionExampleConfig(exData),
|
||||
Config: testutils.StringFromTemplateMust(
|
||||
"testdata/instance_template.gompl",
|
||||
exData,
|
||||
),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("example_resource.test", "name", exData.Name),
|
||||
resource.TestCheckResourceAttrSet("example_resource.test", "id"),
|
||||
resource.TestCheckResourceAttr(resName, "name", exData.Name),
|
||||
resource.TestCheckResourceAttrSet(resName, "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
|
||||
// Update name and verify
|
||||
{
|
||||
ResourceName: "example_resource.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
Config: testutils.StringFromTemplateMust(
|
||||
"testdata/instance_template.gompl",
|
||||
updNameData,
|
||||
),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(resName, "name", updNameData.Name),
|
||||
),
|
||||
},
|
||||
// Update size and verify
|
||||
{
|
||||
Config: testutils.StringFromTemplateMust(
|
||||
"testdata/instance_template.gompl",
|
||||
updSizeData,
|
||||
),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(resName, "size", updNameData.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]
|
||||
|
|
@ -320,7 +283,7 @@ func TestUnitResourceCreate(t *testing.T) {
|
|||
// Name: "testRes",
|
||||
// }
|
||||
// resource.Test(t, resource.TestCase{
|
||||
// ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
// ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
|
||||
// Steps: []resource.TestStep{
|
||||
// {
|
||||
// Config: testAccResourceEncryptionExampleConfig(exData),
|
||||
|
|
@ -352,7 +315,7 @@ func TestUnitResourceCreate(t *testing.T) {
|
|||
|
||||
// Instance resource data
|
||||
var instanceResource = map[string]string{
|
||||
"project_id": testutil.ProjectId,
|
||||
"project_id": testutils.ProjectId,
|
||||
"region": "eu01",
|
||||
"name": fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum)),
|
||||
"acl": "192.168.0.0/16",
|
||||
|
|
@ -378,13 +341,13 @@ var instanceResource = map[string]string{
|
|||
var userResource = map[string]string{
|
||||
"username": fmt.Sprintf("tfaccuser%s", acctest.RandStringFromCharSet(4, acctest.CharSetAlpha)),
|
||||
"role": "createdb",
|
||||
"project_id": testutil.ProjectId,
|
||||
"project_id": testutils.ProjectId,
|
||||
}
|
||||
|
||||
// Database resource data
|
||||
var databaseResource = map[string]string{
|
||||
"name": fmt.Sprintf("tfaccdb%s", acctest.RandStringFromCharSet(4, acctest.CharSetAlphaNum)),
|
||||
"project_id": testutil.ProjectId,
|
||||
"project_id": testutils.ProjectId,
|
||||
}
|
||||
|
||||
func configResources(backupSchedule string, _ *string) string {
|
||||
|
|
@ -432,7 +395,7 @@ func configResources(backupSchedule string, _ *string) string {
|
|||
owner = stackitprivatepreview_postgresflexalpha_user.user.username
|
||||
}
|
||||
`,
|
||||
testutil.PostgresFlexProviderConfig(
|
||||
testutils.PostgresFlexProviderConfig(
|
||||
utils.GetEnvOrDefault("TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_FILE", "~/service-account.json"),
|
||||
),
|
||||
instanceResource["project_id"],
|
||||
|
|
@ -464,7 +427,7 @@ func configResources(backupSchedule string, _ *string) string {
|
|||
func TestAccPostgresFlexFlexResource(t *testing.T) {
|
||||
resource.Test(
|
||||
t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckPostgresFlexDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
// Creation
|
||||
|
|
@ -474,7 +437,7 @@ func TestAccPostgresFlexFlexResource(t *testing.T) {
|
|||
|
||||
// testdata/<Test_Name>/<step_number>
|
||||
// ConfigDirectory: config.TestStepDirectory(),
|
||||
Config: configResources(instanceResource["backup_schedule"], &testutil.Region),
|
||||
Config: configResources(instanceResource["backup_schedule"], &testutils.Region),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Instance
|
||||
resource.TestCheckResourceAttr(
|
||||
|
|
@ -547,7 +510,7 @@ func TestAccPostgresFlexFlexResource(t *testing.T) {
|
|||
resource.TestCheckResourceAttr(
|
||||
"stackitprivatepreview_postgresflexalpha_instance.instance",
|
||||
"region",
|
||||
testutil.Region,
|
||||
testutils.Region,
|
||||
),
|
||||
|
||||
// User
|
||||
|
|
@ -745,7 +708,7 @@ func TestAccPostgresFlexFlexResource(t *testing.T) {
|
|||
return "", fmt.Errorf("couldn't find attribute instance_id")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, testutil.Region, instanceId), nil
|
||||
return fmt.Sprintf("%s,%s,%s", testutils.ProjectId, testutils.Region, instanceId), nil
|
||||
},
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
|
|
@ -767,7 +730,7 @@ func TestAccPostgresFlexFlexResource(t *testing.T) {
|
|||
return "", fmt.Errorf("couldn't find attribute user_id")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, instanceId, userId), nil
|
||||
return fmt.Sprintf("%s,%s,%s,%s", testutils.ProjectId, testutils.Region, instanceId, userId), nil
|
||||
},
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
|
|
@ -791,8 +754,8 @@ func TestAccPostgresFlexFlexResource(t *testing.T) {
|
|||
|
||||
return fmt.Sprintf(
|
||||
"%s,%s,%s,%s",
|
||||
testutil.ProjectId,
|
||||
testutil.Region,
|
||||
testutils.ProjectId,
|
||||
testutils.Region,
|
||||
instanceId,
|
||||
databaseId,
|
||||
), nil
|
||||
|
|
@ -884,11 +847,11 @@ func testAccCheckPostgresFlexDestroy(s *terraform.State) error {
|
|||
ctx := context.Background()
|
||||
var client *postgresflex.APIClient
|
||||
var err error
|
||||
if testutil.PostgresFlexCustomEndpoint == "" {
|
||||
if testutils.PostgresFlexCustomEndpoint == "" {
|
||||
client, err = postgresflex.NewAPIClient()
|
||||
} else {
|
||||
client, err = postgresflex.NewAPIClient(
|
||||
config.WithEndpoint(testutil.PostgresFlexCustomEndpoint),
|
||||
config.WithEndpoint(testutils.PostgresFlexCustomEndpoint),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
|
|
@ -905,7 +868,7 @@ func testAccCheckPostgresFlexDestroy(s *terraform.State) error {
|
|||
instancesToDestroy = append(instancesToDestroy, instanceId)
|
||||
}
|
||||
|
||||
instancesResp, err := client.ListInstancesRequest(ctx, testutil.ProjectId, testutil.Region).Execute()
|
||||
instancesResp, err := client.ListInstancesRequest(ctx, testutils.ProjectId, testutils.Region).Execute()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting instancesResp: %w", err)
|
||||
}
|
||||
|
|
@ -917,7 +880,7 @@ func testAccCheckPostgresFlexDestroy(s *terraform.State) error {
|
|||
}
|
||||
if utils.Contains(instancesToDestroy, *items[i].Id) {
|
||||
// TODO @mhenselin - does force still exist?
|
||||
err := client.DeleteInstanceRequestExecute(ctx, testutil.ProjectId, testutil.Region, *items[i].Id)
|
||||
err := client.DeleteInstanceRequestExecute(ctx, testutils.ProjectId, testutils.Region, *items[i].Id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting instance %s during CheckDestroy: %w", *items[i].Id, err)
|
||||
}
|
||||
|
|
|
|||
30
stackit/internal/services/postgresflexalpha/testdata/instance_template.gompl
vendored
Normal file
30
stackit/internal/services/postgresflexalpha/testdata/instance_template.gompl
vendored
Normal 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 }}
|
||||
}
|
||||
|
|
@ -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
|
||||
19
stackit/internal/services/postgresflexalpha/testdata/resource-no-enc.tf
vendored
Normal file
19
stackit/internal/services/postgresflexalpha/testdata/resource-no-enc.tf
vendored
Normal 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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue