1package provider
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "net/http"
8 "net/http/httptest"
9 "testing"
10)
11
12func TestCustomModelsURL(t *testing.T) {
13 cases := map[string]string{
14 "http://127.0.0.1:48000": "http://127.0.0.1:48000/v1/models",
15 "http://127.0.0.1:48000/": "http://127.0.0.1:48000/v1/models",
16 "http://127.0.0.1:48000/v1": "http://127.0.0.1:48000/v1/models",
17 "http://127.0.0.1:48000/v1/": "http://127.0.0.1:48000/v1/models",
18 "https://llm.example.com/paas/v4": "https://llm.example.com/paas/v4/models",
19 }
20 for in, want := range cases {
21 got, err := customModelsURL(in)
22 if err != nil || got != want {
23 t.Errorf("customModelsURL(%q) = %q, %v; want %q", in, got, err, want)
24 }
25 }
26 for _, bad := range []string{"", "localhost:1234", "ftp://x", "/v1"} {
27 if _, err := customModelsURL(bad); err == nil {
28 t.Errorf("customModelsURL(%q) accepted", bad)
29 }
30 }
31}
32
33func TestDiscoverCustomProvider(t *testing.T) {
34 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
35 if r.URL.Path != "/v1/models" {
36 t.Errorf("unexpected path %s", r.URL.Path)
37 }
38 if r.Header.Get("Authorization") != "Bearer dummy" {
39 t.Errorf("missing bearer header: %q", r.Header.Get("Authorization"))
40 }
41 fmt.Fprint(w, `{"object":"list","data":[{"id":"qwen3-coder"},{"id":"nomic-embed-text"},{"id":"qwen3-coder"},{"id":""},{"id":"gpt-oss-20b"}]}`)
42 }))
43 defer server.Close()
44
45 models, err := DiscoverCustomProvider(context.Background(), "m4", server.URL+"/v1", "dummy")
46 if err != nil {
47 t.Fatal(err)
48 }
49 if len(models) != 2 {
50 t.Fatalf("models = %+v", models)
51 }
52 first := models[0]
53 if first.Provider != "m4" || first.ID != "qwen3-coder" || first.Source != "live" || first.BaseURL != server.URL+"/v1" {
54 t.Fatalf("model = %+v", first)
55 }
56 if first.ContextWindow != customDiscoveryContextWindow || first.MaxOutput != customDiscoveryMaxOutput {
57 t.Fatalf("defaults = %d/%d", first.ContextWindow, first.MaxOutput)
58 }
59 if models[1].ID != "gpt-oss-20b" {
60 t.Fatalf("second model = %+v", models[1])
61 }
62}
63
64func TestDiscoverCustomProviderOmitsHeaderWithoutKey(t *testing.T) {
65 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
66 if _, ok := r.Header["Authorization"]; ok {
67 t.Error("unexpected Authorization header")
68 }
69 fmt.Fprint(w, `{"data":[{"id":"local"}]}`)
70 }))
71 defer server.Close()
72 models, err := DiscoverCustomProvider(context.Background(), "m5", server.URL, "")
73 if err != nil || len(models) != 1 {
74 t.Fatalf("models=%+v err=%v", models, err)
75 }
76}
77
78func TestDiscoverCustomProviderErrors(t *testing.T) {
79 status := http.StatusUnauthorized
80 body := `{}`
81 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
82 w.WriteHeader(status)
83 fmt.Fprint(w, body)
84 }))
85 defer server.Close()
86
87 if _, err := DiscoverCustomProvider(context.Background(), "m4", server.URL, "k"); err == nil || err.Error() != "m4 discovery HTTP 401" {
88 t.Fatalf("status error = %v", err)
89 }
90 status = http.StatusOK
91 body = `{"object":"list"}`
92 if _, err := DiscoverCustomProvider(context.Background(), "m4", server.URL, "k"); err == nil {
93 t.Fatal("missing data accepted")
94 }
95 body = `not json`
96 if _, err := DiscoverCustomProvider(context.Background(), "m4", server.URL, "k"); err == nil {
97 t.Fatal("invalid JSON accepted")
98 }
99 if _, err := DiscoverCustomProvider(context.Background(), "", server.URL, "k"); err == nil {
100 t.Fatal("empty provider id accepted")
101 }
102
103 ctx, cancel := context.WithCancel(context.Background())
104 cancel()
105 if _, err := DiscoverCustomProvider(ctx, "m4", server.URL, "k"); !errors.Is(err, context.Canceled) {
106 t.Fatalf("cancellation error = %v", err)
107 }
108}