Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions internal/persistence/opensearch/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation"
)

type (
Expand Down Expand Up @@ -227,6 +228,9 @@ func (o *OpenSearchMetadataInput) ValidationErrors(ctx context.Context) *validat
if o.Name == "" {
verr.Add("name", "Name must not be empty.")
}
if errs := validation.IsDNS1123Subdomain(o.Name); len(errs) > 0 {
verr.Add("name", "Name must consist of lowercase letters, numbers, and hyphens only. It cannot start or end with a hyphen.")
}
if o.EnvironmentName == "" {
verr.Add("environmentName", "Environment name must not be empty.")
}
Expand Down
175 changes: 175 additions & 0 deletions internal/persistence/opensearch/models_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package opensearch_test

import (
"context"
"strings"
"testing"

"github.com/nais/api/internal/persistence/opensearch"
"github.com/nais/api/internal/slug"
)

func TestOpenSearchMetadataInput_Validate(t *testing.T) {
dnsError := "Name must consist of lowercase letters, numbers, and hyphens only. It cannot start or end with a hyphen."

tests := []struct {
name string
input opensearch.OpenSearchMetadataInput
wantErr string
}{
{
name: "valid input",
input: opensearch.OpenSearchMetadataInput{
Name: "my-opensearch",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "",
},
{
name: "valid input with trimmed whitespace",
input: opensearch.OpenSearchMetadataInput{
Name: " my-opensearch ",
EnvironmentName: " dev ",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "",
},
{
name: "empty name",
input: opensearch.OpenSearchMetadataInput{
Name: "",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "name: Name must not be empty.\nname: " + dnsError,
},
{
name: "whitespace only name",
input: opensearch.OpenSearchMetadataInput{
Name: " ",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "name: Name must not be empty.\nname: " + dnsError,
},
{
name: "invalid DNS name - uppercase",
input: opensearch.OpenSearchMetadataInput{
Name: "MyOpenSearch",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "name: " + dnsError,
},
{
name: "invalid DNS name - starts with hyphen",
input: opensearch.OpenSearchMetadataInput{
Name: "-my-opensearch",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "name: " + dnsError,
},
{
name: "invalid DNS name - ends with hyphen",
input: opensearch.OpenSearchMetadataInput{
Name: "my-opensearch-",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "name: " + dnsError,
},
{
name: "invalid DNS name - contains underscore",
input: opensearch.OpenSearchMetadataInput{
Name: "my_opensearch",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "name: " + dnsError,
},
{
name: "invalid DNS name - too long (exceeds 253 chars)",
input: opensearch.OpenSearchMetadataInput{
Name: strings.Repeat("a", 254),
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "name: " + dnsError,
},
{
name: "empty environment name",
input: opensearch.OpenSearchMetadataInput{
Name: "my-opensearch",
EnvironmentName: "",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "environmentName: Environment name must not be empty.",
},
{
name: "whitespace only environment name",
input: opensearch.OpenSearchMetadataInput{
Name: "my-opensearch",
EnvironmentName: " ",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "environmentName: Environment name must not be empty.",
},
{
name: "empty team slug",
input: opensearch.OpenSearchMetadataInput{
Name: "my-opensearch",
EnvironmentName: "dev",
TeamSlug: slug.Slug(""),
},
wantErr: "teamSlug: Team slug must not be empty.",
},
{
name: "all fields empty",
input: opensearch.OpenSearchMetadataInput{
Name: "",
EnvironmentName: "",
TeamSlug: slug.Slug(""),
},
wantErr: "name: Name must not be empty.\nname: " + dnsError + "\nenvironmentName: Environment name must not be empty.\nteamSlug: Team slug must not be empty.",
},
{
name: "valid DNS name with numbers",
input: opensearch.OpenSearchMetadataInput{
Name: "opensearch-123",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "",
},
{
name: "valid DNS name - max length (63 chars)",
input: opensearch.OpenSearchMetadataInput{
Name: strings.Repeat("a", 63),
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.input.Validate(context.Background())
if tt.wantErr == "" {
if err != nil {
t.Errorf("unexpected error: %v", err)
}
} else {
if err == nil {
t.Error("expected error but got nil")
return
}
if err.Error() != tt.wantErr {
t.Errorf("error mismatch\ngot: %q\nwant: %q", err.Error(), tt.wantErr)
}
}
})
}
}
4 changes: 4 additions & 0 deletions internal/persistence/valkey/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation"
)

type (
Expand Down Expand Up @@ -216,6 +217,9 @@ func (v *ValkeyMetadataInput) ValidationErrors(ctx context.Context) *validate.Va
if v.Name == "" {
verr.Add("name", "Name must not be empty.")
}
if errs := validation.IsDNS1123Subdomain(v.Name); len(errs) > 0 {
verr.Add("name", "Name must consist of lowercase letters, numbers, and hyphens only. It cannot start or end with a hyphen.")
}
if v.EnvironmentName == "" {
verr.Add("environmentName", "Environment name must not be empty.")
}
Expand Down
175 changes: 175 additions & 0 deletions internal/persistence/valkey/models_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package valkey_test

import (
"context"
"strings"
"testing"

"github.com/nais/api/internal/persistence/valkey"
"github.com/nais/api/internal/slug"
)

func TestValkeyMetadataInput_Validate(t *testing.T) {
dnsError := "Name must consist of lowercase letters, numbers, and hyphens only. It cannot start or end with a hyphen."

tests := []struct {
name string
input valkey.ValkeyMetadataInput
wantErr string
}{
{
name: "valid input",
input: valkey.ValkeyMetadataInput{
Name: "my-valkey",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "",
},
{
name: "valid input with trimmed whitespace",
input: valkey.ValkeyMetadataInput{
Name: " my-valkey ",
EnvironmentName: " dev ",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "",
},
{
name: "empty name",
input: valkey.ValkeyMetadataInput{
Name: "",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "name: Name must not be empty.\nname: " + dnsError,
},
{
name: "whitespace only name",
input: valkey.ValkeyMetadataInput{
Name: " ",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "name: Name must not be empty.\nname: " + dnsError,
},
{
name: "invalid DNS name - uppercase",
input: valkey.ValkeyMetadataInput{
Name: "My-Valkey",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "name: " + dnsError,
},
{
name: "invalid DNS name - starts with hyphen",
input: valkey.ValkeyMetadataInput{
Name: "-my-valkey",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "name: " + dnsError,
},
{
name: "invalid DNS name - ends with hyphen",
input: valkey.ValkeyMetadataInput{
Name: "my-valkey-",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "name: " + dnsError,
},
{
name: "invalid DNS name - contains underscore",
input: valkey.ValkeyMetadataInput{
Name: "my_valkey",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "name: " + dnsError,
},
{
name: "invalid DNS name - too long",
input: valkey.ValkeyMetadataInput{
Name: strings.Repeat("a", 254),
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "name: " + dnsError,
},
{
name: "empty environment name",
input: valkey.ValkeyMetadataInput{
Name: "my-valkey",
EnvironmentName: "",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "environmentName: Environment name must not be empty.",
},
{
name: "whitespace only environment name",
input: valkey.ValkeyMetadataInput{
Name: "my-valkey",
EnvironmentName: " ",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "environmentName: Environment name must not be empty.",
},
{
name: "empty team slug",
input: valkey.ValkeyMetadataInput{
Name: "my-valkey",
EnvironmentName: "dev",
TeamSlug: slug.Slug(""),
},
wantErr: "teamSlug: Team slug must not be empty.",
},
{
name: "multiple validation errors",
input: valkey.ValkeyMetadataInput{
Name: "",
EnvironmentName: "",
TeamSlug: slug.Slug(""),
},
wantErr: "name: Name must not be empty.\nname: " + dnsError + "\nenvironmentName: Environment name must not be empty.\nteamSlug: Team slug must not be empty.",
},
{
name: "valid DNS name with numbers",
input: valkey.ValkeyMetadataInput{
Name: "valkey-123",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "",
},
{
name: "valid DNS name starting with number",
input: valkey.ValkeyMetadataInput{
Name: "123-valkey",
EnvironmentName: "dev",
TeamSlug: slug.Slug("my-team"),
},
wantErr: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.input.Validate(context.Background())
if tt.wantErr == "" {
if err != nil {
t.Errorf("unexpected error: %v", err)
}
} else {
if err == nil {
t.Error("expected error but got nil")
return
}
if err.Error() != tt.wantErr {
t.Errorf("error mismatch\ngot: %q\nwant: %q", err.Error(), tt.wantErr)
}
}
})
}
}