package services import ( "testing" "clawreef/internal/models" ) func TestNormalizeEnvironmentOverrides(t *testing.T) { overrides, err := normalizeEnvironmentOverrides(map[string]string{ " FOO ": "bar", "BAR_2": "", }) if err != nil { t.Fatalf("normalizeEnvironmentOverrides returned error: %v", err) } if overrides["FOO"] != "bar" { t.Fatalf("expected trimmed key FOO to be preserved") } if value, ok := overrides["BAR_2"]; !ok || value != "" { t.Fatalf("expected empty override value to be preserved") } } func TestNormalizeEnvironmentOverridesRejectsInvalidNames(t *testing.T) { if _, err := normalizeEnvironmentOverrides(map[string]string{ "1INVALID": "value", }); err == nil { t.Fatalf("expected invalid environment variable name to fail validation") } } func TestBuildInstancePodEnvAppliesOverridesAfterDefaults(t *testing.T) { t.Setenv("CLAWMANAGER_EGRESS_PROXY_URL", "") t.Setenv("CLAWMANAGER_SYSTEM_NAMESPACE", "") t.Setenv("K8S_NAMESPACE", "") raw, err := marshalEnvironmentOverrides(map[string]string{ "SUBFOLDER": "/custom-proxy", "KASM_SVC_ACCEPT_CUT_TEXT": "-AcceptCutText 1", "CUSTOM": "enabled", }) if err != nil { t.Fatalf("marshalEnvironmentOverrides returned error: %v", err) } instance := &models.Instance{ ID: 42, Type: "webtop", EnvironmentOverridesJSON: raw, } env, err := buildInstancePodEnv(instance, map[string]string{ "TITLE": "ClawManager Webtop", "SUBFOLDER": "/", }, nil, nil) if err != nil { t.Fatalf("buildInstancePodEnv returned error: %v", err) } if env["SUBFOLDER"] != "/custom-proxy" { t.Fatalf("expected SUBFOLDER override to win, got %q", env["SUBFOLDER"]) } if env["KASM_SVC_ACCEPT_CUT_TEXT"] != "-AcceptCutText 1" { t.Fatalf("expected clipboard policy override to win, got %q", env["KASM_SVC_ACCEPT_CUT_TEXT"]) } if env["CUSTOM"] != "enabled" { t.Fatalf("expected custom environment variable to be merged") } if env["TITLE"] != "ClawManager Webtop" { t.Fatalf("expected default environment variable to remain available") } } func TestBuildInstancePodEnvNormalizesDesktopStreamProfile(t *testing.T) { raw, err := marshalEnvironmentOverrides(map[string]string{ "CLAWMANAGER_DESKTOP_STREAM_PROFILE": "standard", "SELKIES_ENCODER": "x264enc", "SELKIES_FRAMERATE": "35", "SELKIES_H264_CRF": "34", }) if err != nil { t.Fatalf("marshalEnvironmentOverrides returned error: %v", err) } env, err := buildInstancePodEnv(&models.Instance{ ID: 42, Type: "openclaw", RuntimeType: RuntimeBackendDesktop, EnvironmentOverridesJSON: raw, }, nil, nil, nil) if err != nil { t.Fatalf("buildInstancePodEnv returned error: %v", err) } if got := env["SELKIES_ENCODER"]; got != "x264enc,jpeg" { t.Fatalf("SELKIES_ENCODER = %q, want x264enc,jpeg", got) } if got := env["SELKIES_USE_CSS_SCALING"]; got != "true" { t.Fatalf("SELKIES_USE_CSS_SCALING = %q, want true", got) } } func TestPopSHMSizeGB(t *testing.T) { tests := []struct { name string value string hasValue bool runtime string memoryGB int want int }{ {name: "desktop minimum preset", runtime: "desktop", memoryGB: 4, want: defaultInstanceSHMSizeGB}, {name: "desktop medium preset", runtime: "desktop", memoryGB: 8, want: 2}, {name: "desktop large preset", runtime: "desktop", memoryGB: 12, want: 4}, {name: "desktop small fallback", runtime: "desktop", memoryGB: 2, want: defaultInstanceSHMSizeGB}, {name: "shell default unchanged", runtime: "shell", memoryGB: 8, want: defaultInstanceSHMSizeGB}, {name: "disable", value: "0", hasValue: true, runtime: "desktop", memoryGB: 8, want: 0}, {name: "custom", value: "4", hasValue: true, runtime: "desktop", memoryGB: 4, want: 4}, {name: "clamp", value: "128", hasValue: true, runtime: "desktop", memoryGB: 8, want: maxInstanceSHMSizeGB}, {name: "invalid keeps dynamic desktop default", value: "nope", hasValue: true, runtime: "desktop", memoryGB: 8, want: 2}, {name: "negative keeps dynamic desktop default", value: "-1", hasValue: true, runtime: "desktop", memoryGB: 4, want: defaultInstanceSHMSizeGB}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { extraEnv := map[string]string{"KEEP": "value"} if tt.hasValue { extraEnv["SHM_SIZE_GB"] = tt.value } got := popSHMSizeGB(extraEnv, tt.runtime, tt.memoryGB) if got != tt.want { t.Fatalf("expected shm size %d, got %d", tt.want, got) } if _, ok := extraEnv["SHM_SIZE_GB"]; ok { t.Fatalf("expected SHM_SIZE_GB to be removed from extra env") } if extraEnv["KEEP"] != "value" { t.Fatalf("expected unrelated env to be preserved") } }) } }