diff --git a/config/config.go b/config/config.go index 6dd5be84..0ec5b6bc 100644 --- a/config/config.go +++ b/config/config.go @@ -1,6 +1,8 @@ package config import ( + "errors" + "fmt" "log" "os" "regexp" @@ -68,9 +70,20 @@ func Load() (*Config, error) { cfg.LogBufferSize = 1 } - cfg.ApiKey, err = GetEnvAsUUID("API_KEY") - if err != nil { - log.Printf("[Error] Failed to load API Key, error: %v", err) + // The API key is the node's only authentication. A missing, malformed or all-zero key + // would leave the node accepting the all-zero UUID, so Load reports it as an error + // (main exits). cfg is still returned complete for NewTestConfig, which sets its own key. + var apiKeyErr error + if GetEnv("API_KEY", "") == "" { + apiKeyErr = errors.New("API_KEY is not set") + } else { + cfg.ApiKey, apiKeyErr = GetEnvAsUUID("API_KEY") + } + if apiKeyErr == nil && cfg.ApiKey == uuid.Nil { + apiKeyErr = errors.New("must not be the all-zero UUID") + } + if apiKeyErr != nil { + apiKeyErr = fmt.Errorf("invalid API_KEY: %w", apiKeyErr) } nodeHostStr := GetEnv("NODE_HOST", "0.0.0.0") @@ -84,7 +97,7 @@ func Load() (*Config, error) { cfg.NodeHost = "127.0.0.1" } - return cfg, nil + return cfg, apiKeyErr } // NewTestConfig creates a config for testing diff --git a/config/config_test.go b/config/config_test.go new file mode 100644 index 00000000..adb12bb1 --- /dev/null +++ b/config/config_test.go @@ -0,0 +1,62 @@ +package config + +import ( + "os" + "strings" + "testing" +) + +// unsetEnv removes name for the duration of the test (t.Setenv can only set, not unset). +func unsetEnv(t *testing.T, name string) { + t.Helper() + t.Setenv(name, "") // registers the restore of the original value + if err := os.Unsetenv(name); err != nil { + t.Fatalf("unset %s: %v", name, err) + } +} + +func TestLoadRejectsMissingAPIKey(t *testing.T) { + unsetEnv(t, "API_KEY") + + cfg, err := Load() + if err == nil || !strings.Contains(err.Error(), "API_KEY is not set") { + t.Fatalf("expected a not-set error, got %v", err) + } + if cfg == nil { + t.Fatal("Load must still return the config (NewTestConfig relies on it)") + } +} + +func TestLoadRejectsInvalidAPIKey(t *testing.T) { + cases := map[string]string{ + "empty": "", + "not a uuid": "not-a-uuid", + "all zero": "00000000-0000-0000-0000-000000000000", + } + for name, value := range cases { + t.Run(name, func(t *testing.T) { + t.Setenv("API_KEY", value) + + cfg, err := Load() + if err == nil { + t.Fatalf("expected Load to fail for API_KEY=%q", value) + } + if cfg == nil { + t.Fatal("Load must still return the config (NewTestConfig relies on it)") + } + }) + } +} + +func TestLoadAcceptsValidAPIKey(t *testing.T) { + const key = "3fa85f64-5717-4562-b3fc-2c963f66afa6" + t.Setenv("API_KEY", key) + + cfg, err := Load() + if err != nil { + t.Fatalf("Load failed: %v", err) + } + if cfg.ApiKey.String() != key { + t.Fatalf("expected ApiKey %s, got %s", key, cfg.ApiKey) + } +}