1package ui23import (4 "encoding/json"5 "net/http"6 "net/http/httptest"7 "os"8 "path/filepath"9 "strings"10 "sync/atomic"11 "testing"1213 tea "charm.land/bubbletea/v2"1415 "src.rest/cat/012/internal/config"16 "src.rest/cat/012/internal/jev"17 "src.rest/cat/012/internal/keyring"18)1920// fakeTypeSafe stands in for the TypeSafe API on this machine: it21// accepts only goodKey and answers yes/no questions with 0.9.22func fakeTypeSafe(t *testing.T, goodKey string) (*httptest.Server, *atomic.Int32) {23 t.Helper()24 var n atomic.Int3225 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {26 n.Add(1)27 if r.URL.Path != "/v1/systemone" || r.Header.Get("Authorization") != "Bearer "+goodKey {28 w.Header().Set("Content-Type", "application/json")29 w.WriteHeader(http.StatusUnauthorized)30 w.Write([]byte(`{"error":{"message":"invalid api key"}}`))31 return32 }33 json.NewEncoder(w).Encode(map[string]any{34 "model": "jev-latest",35 "answers": map[string]any{"answer": map[string]any{"type": "noul", "noul": 0.9}},36 "usage": map[string]int{"input_tokens": 1, "output_tokens": 1},37 })38 }))39 t.Cleanup(srv.Close)40 return srv, &n41}4243// Storing a key makes one test call with it and says how it went,44// without ever showing the key; a refused key is still stored.45func TestAPIKeyChecked(t *testing.T) {46 const good, bad = "ts-good-4242", "ts-bad-1313"47 srv, requests := fakeTypeSafe(t, good)48 path := filepath.Join(t.TempDir(), "config")49 os.WriteFile(path, nil, 0o600)50 store := &keyring.Memory{}51 m := newModel()52 m.Update(tea.WindowSizeMsg{Width: 100, Height: 20})53 m.Configure(Settings{Config: config.Load(path, func(string) string { return "" }, nil), Keys: store,54 Connect: func(key string) (jev.Client, error) {55 return jev.NewClient(jev.Config{APIKey: key, BaseURL: srv.URL})56 }})5758 m.runCommand("settings.jev_key")59 press(t, m, good, "<enter>")60 if got := line(m, contextLine); !strings.Contains(got, "Key saved and checked") || requests.Load() != 1 {61 t.Fatalf("good key: %q after %d requests", got, requests.Load())62 }6364 m.runCommand("settings.jev_key")65 press(t, m, bad, "<enter>")66 got := line(m, contextLine)67 if !strings.Contains(got, "Key saved, but the check failed: the service refused the key (HTTP 401)") {68 t.Errorf("bad key: %q", got)69 }70 if strings.Contains(screen(m), bad) || strings.Contains(screen(m), good) {71 t.Error("a key is on the screen")72 }73 if k, _ := store.Get(t.Context()); k != bad || m.jev == nil {74 t.Errorf("the refused key wasn't kept: %q, JEV on %v", k, m.jev != nil)75 }76 if n := requests.Load(); n != 2 {77 t.Errorf("%d requests; want one per key", n)78 }79}