build source
This commit is contained in:
commit
ee1fec43ed
4171 changed files with 1351288 additions and 0 deletions
303
internal/model/model_test.go
Normal file
303
internal/model/model_test.go
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func intPtr(v int) *int { return &v }
|
||||
|
||||
func TestCreatePodRequest_Validate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
req CreatePodRequest
|
||||
wantErr bool
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid request",
|
||||
req: CreatePodRequest{User: "alice", Pod: "main", Tools: "go,rust"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "minimal valid request",
|
||||
req: CreatePodRequest{User: "bob", Pod: "dev1"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing user",
|
||||
req: CreatePodRequest{Pod: "main"},
|
||||
wantErr: true,
|
||||
errMsg: "user is required",
|
||||
},
|
||||
{
|
||||
name: "missing pod name",
|
||||
req: CreatePodRequest{User: "alice"},
|
||||
wantErr: true,
|
||||
errMsg: "pod name is required",
|
||||
},
|
||||
{
|
||||
name: "invalid user - uppercase",
|
||||
req: CreatePodRequest{User: "Alice", Pod: "main"},
|
||||
wantErr: true,
|
||||
errMsg: "user must be lowercase alphanumeric with hyphens",
|
||||
},
|
||||
{
|
||||
name: "invalid user - starts with hyphen",
|
||||
req: CreatePodRequest{User: "-alice", Pod: "main"},
|
||||
wantErr: true,
|
||||
errMsg: "user must be lowercase alphanumeric with hyphens",
|
||||
},
|
||||
{
|
||||
name: "invalid user - ends with hyphen",
|
||||
req: CreatePodRequest{User: "alice-", Pod: "main"},
|
||||
wantErr: true,
|
||||
errMsg: "user must be lowercase alphanumeric with hyphens",
|
||||
},
|
||||
{
|
||||
name: "invalid user - special chars",
|
||||
req: CreatePodRequest{User: "al!ce", Pod: "main"},
|
||||
wantErr: true,
|
||||
errMsg: "user must be lowercase alphanumeric with hyphens",
|
||||
},
|
||||
{
|
||||
name: "invalid pod name - spaces",
|
||||
req: CreatePodRequest{User: "alice", Pod: "my pod"},
|
||||
wantErr: true,
|
||||
errMsg: "pod name must be lowercase alphanumeric with hyphens",
|
||||
},
|
||||
{
|
||||
name: "valid user with hyphens",
|
||||
req: CreatePodRequest{User: "alice-dev", Pod: "my-pod"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "user name too long for namespace",
|
||||
req: CreatePodRequest{User: strings.Repeat("a", 60), Pod: "main"},
|
||||
wantErr: true,
|
||||
errMsg: "user name too long",
|
||||
},
|
||||
{
|
||||
name: "pod name too long for service",
|
||||
req: CreatePodRequest{User: "alice", Pod: strings.Repeat("a", 52)},
|
||||
wantErr: true,
|
||||
errMsg: "pod name too long",
|
||||
},
|
||||
{
|
||||
name: "max valid user name (59 chars)",
|
||||
req: CreatePodRequest{User: strings.Repeat("a", 59), Pod: "main"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "max valid pod name (51 chars)",
|
||||
req: CreatePodRequest{User: "alice", Pod: strings.Repeat("a", 51)},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.req.Validate()
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if tt.errMsg != "" && !strings.HasPrefix(err.Error(), tt.errMsg) {
|
||||
t.Errorf("error = %q, want prefix %q", err.Error(), tt.errMsg)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateUserRequest_Validate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
req CreateUserRequest
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid request",
|
||||
req: CreateUserRequest{User: "alice"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid with quotas",
|
||||
req: CreateUserRequest{User: "bob", Quotas: &UpdateQuotasRequest{MaxConcurrentPods: intPtr(5)}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing user",
|
||||
req: CreateUserRequest{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid user name",
|
||||
req: CreateUserRequest{User: "ALICE"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user name too long for namespace",
|
||||
req: CreateUserRequest{User: strings.Repeat("a", 60)},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "max valid user name (59 chars)",
|
||||
req: CreateUserRequest{User: strings.Repeat("a", 59)},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.req.Validate()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateQuotasRequest_Validate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
req UpdateQuotasRequest
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid - all fields",
|
||||
req: UpdateQuotasRequest{MaxConcurrentPods: intPtr(5), MaxCPUPerPod: intPtr(16)},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid - partial update",
|
||||
req: UpdateQuotasRequest{MaxConcurrentPods: intPtr(2)},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid - empty (no-op)",
|
||||
req: UpdateQuotasRequest{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid - zero max pods",
|
||||
req: UpdateQuotasRequest{MaxConcurrentPods: intPtr(0)},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - negative cpu",
|
||||
req: UpdateQuotasRequest{MaxCPUPerPod: intPtr(-1)},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - zero ram",
|
||||
req: UpdateQuotasRequest{MaxRAMGBPerPod: intPtr(0)},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - zero monthly hours",
|
||||
req: UpdateQuotasRequest{MonthlyPodHours: intPtr(0)},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - negative ai requests",
|
||||
req: UpdateQuotasRequest{MonthlyAIRequests: intPtr(-1)},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid - zero ai requests allowed",
|
||||
req: UpdateQuotasRequest{MonthlyAIRequests: intPtr(0)},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.req.Validate()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want bool
|
||||
}{
|
||||
{"simple lowercase", "alice", true},
|
||||
{"with numbers", "alice123", true},
|
||||
{"with hyphens", "alice-dev", true},
|
||||
{"single char", "a", true},
|
||||
{"empty", "", false},
|
||||
{"uppercase", "Alice", false},
|
||||
{"starts with hyphen", "-alice", false},
|
||||
{"ends with hyphen", "alice-", false},
|
||||
{"underscore", "alice_dev", false},
|
||||
{"dot", "alice.dev", false},
|
||||
{"space", "alice dev", false},
|
||||
{"64 chars (too long)", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false},
|
||||
{"63 chars (max)", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := isValidName(tt.input)
|
||||
if got != tt.want {
|
||||
t.Errorf("isValidName(%q) = %v, want %v", tt.input, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultQuota(t *testing.T) {
|
||||
q := DefaultQuota()
|
||||
if q.MaxConcurrentPods != 3 {
|
||||
t.Errorf("MaxConcurrentPods = %d, want 3", q.MaxConcurrentPods)
|
||||
}
|
||||
if q.MaxCPUPerPod != 8 {
|
||||
t.Errorf("MaxCPUPerPod = %d, want 8", q.MaxCPUPerPod)
|
||||
}
|
||||
if q.MaxRAMGBPerPod != 16 {
|
||||
t.Errorf("MaxRAMGBPerPod = %d, want 16", q.MaxRAMGBPerPod)
|
||||
}
|
||||
if q.MonthlyPodHours != 500 {
|
||||
t.Errorf("MonthlyPodHours = %d, want 500", q.MonthlyPodHours)
|
||||
}
|
||||
if q.MonthlyAIRequests != 10000 {
|
||||
t.Errorf("MonthlyAIRequests = %d, want 10000", q.MonthlyAIRequests)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamespaceName(t *testing.T) {
|
||||
if got := NamespaceName("alice"); got != "dev-alice" {
|
||||
t.Errorf("NamespaceName(alice) = %q, want %q", got, "dev-alice")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodFullName(t *testing.T) {
|
||||
if got := PodFullName("main"); got != "dev-pod-main" {
|
||||
t.Errorf("PodFullName(main) = %q, want %q", got, "dev-pod-main")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceName(t *testing.T) {
|
||||
if got := ServiceName("main"); got != "dev-pod-main-svc" {
|
||||
t.Errorf("ServiceName(main) = %q, want %q", got, "dev-pod-main-svc")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodURL(t *testing.T) {
|
||||
got := PodURL("spinoff.dev", "alice", "main")
|
||||
want := "https://spinoff.dev/@alice/main/"
|
||||
if got != want {
|
||||
t.Errorf("PodURL() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue