package config import ( "os" "path/filepath" "strings" "testing" "time" ) func TestParseValid(t *testing.T) { in := ` # Motor de inferencia API_URL=https://servidor.example.com/v1/ # Puede quedar vacĂ­o API_KEY="sk-1234567890abcdef" MODEL = qwen3.5 CONTEXT_SIZE=32768 FUTURE_OPTION=x ` cfg, err := Parse(strings.NewReader(in)) if err != nil { t.Fatalf("Parse: %v", err) } if cfg.APIURL != "https://servidor.example.com/v1" { t.Errorf("APIURL = %q (trailing slash must be removed)", cfg.APIURL) } if cfg.APIKey != "sk-1234567890abcdef" { t.Errorf("APIKey = %q", cfg.APIKey) } if cfg.Model != "qwen3.5" { t.Errorf("Model = %q", cfg.Model) } if cfg.ContextSize != 32768 { t.Errorf("ContextSize = %d", cfg.ContextSize) } if cfg.RequestTimeout != DefaultRequestTimeout { t.Errorf("RequestTimeout = %v", cfg.RequestTimeout) } if len(cfg.Unknown) != 1 || cfg.Unknown[0] != "FUTURE_OPTION" { t.Errorf("Unknown = %v", cfg.Unknown) } } func TestParseDefaults(t *testing.T) { cfg, err := Parse(strings.NewReader("API_URL=http://127.0.0.1:8080/v1\nMODEL=m\nREQUEST_TIMEOUT=10\n")) if err != nil { t.Fatalf("Parse: %v", err) } if cfg.ContextSize != 0 { t.Errorf("missing CONTEXT_SIZE must stay 0 (server default), got %d", cfg.ContextSize) } if cfg.APIKey != "" { t.Errorf("APIKey should be empty") } if cfg.RequestTimeout != 10*time.Second { t.Errorf("RequestTimeout = %v", cfg.RequestTimeout) } } func TestParseErrors(t *testing.T) { cases := map[string]string{ "no equals": "API_URL=http://x/v1\nMODEL=m\ngarbage\n", "duplicate": "API_URL=http://x/v1\nMODEL=m\nMODEL=n\n", "missing url": "MODEL=m\n", "missing model": "API_URL=http://x/v1\n", "bad scheme": "API_URL=ftp://x/v1\nMODEL=m\n", "bad ctx": "API_URL=http://x/v1\nMODEL=m\nCONTEXT_SIZE=abc\n", "negative ctx": "API_URL=http://x/v1\nMODEL=m\nCONTEXT_SIZE=-1\n", } for name, in := range cases { if _, err := Parse(strings.NewReader(in)); err == nil { t.Errorf("%s: expected error", name) } } } func TestLoadMissingFile(t *testing.T) { _, err := Load(filepath.Join(t.TempDir(), FileName)) if err == nil || !os.IsNotExist(unwrapAll(err)) { t.Fatalf("expected not-exist error, got %v", err) } } func TestMaskSecret(t *testing.T) { if got := MaskSecret(""); got != "(empty)" { t.Errorf("empty: %q", got) } if got := MaskSecret("short"); got != "***" { t.Errorf("short: %q", got) } got := MaskSecret("sk-2f1abcdefghijk8a9") if got != "sk-2f1...8a9" { t.Errorf("long: %q", got) } } func unwrapAll(err error) error { for { u, ok := err.(interface{ Unwrap() error }) if !ok { return err } err = u.Unwrap() } } func TestToolCalling(t *testing.T) { base := "API_URL=http://x/v1\nMODEL=m\n" cfg, err := Parse(strings.NewReader(base)) if err != nil || cfg.ToolCalling != "auto" { t.Fatalf("default: %v %v", cfg, err) } cfg, err = Parse(strings.NewReader(base + "TOOL_CALLING=JSON\n")) if err != nil || cfg.ToolCalling != "json" { t.Fatalf("json: %v %v", cfg, err) } if _, err := Parse(strings.NewReader(base + "TOOL_CALLING=magic\n")); err == nil { t.Error("invalid TOOL_CALLING must fail") } } func TestSearchURL(t *testing.T) { base := "API_URL=http://x/v1\nMODEL=m\n" cfg, err := Parse(strings.NewReader(base + "SEARCH_URL=https://searx.local/search?q={query}&format=json\n")) if err != nil || !strings.Contains(cfg.SearchURL, "{query}") { t.Fatalf("%v %v", cfg, err) } for _, bad := range []string{"SEARCH_URL=https://searx.local/search\n", "SEARCH_URL=ftp://x/{query}\n"} { if _, err := Parse(strings.NewReader(base + bad)); err == nil { t.Errorf("%q must fail", bad) } } }