package mirror import ( "context" "errors" "net/http" "net/http/httptest" "path/filepath" "strings" "testing" "gitea.mynodes.xyz/ilker/llmgw/internal/tlsconf" ) // testClient puts a real API behind a real HTTP server and points a real client // at it, because the thing worth testing is the round trip: a handler and a // client that each look right on their own can still disagree about a field // name or a status code, and nothing but the two together catches that. func testClient(t *testing.T) (*Client, string) { t.Helper() a, root := testAPI(t) srv := httptest.NewServer(a.Handler()) t.Cleanup(srv.Close) c, err := NewClient(srv.URL, tlsconf.Config{}, 0) if err != nil { t.Fatal(err) } return c, root } func TestTheClientReadsWhatTheMirrorHolds(t *testing.T) { c, root := testClient(t) writeFile(t, filepath.Join(root, "llama", "2026-01", "model.gguf"), "weights") got, err := c.List(t.Context()) if err != nil { t.Fatal(err) } // The base comes back from the mirror rather than being assembled here: the // catalog entry has to be spelled the way the mirror spells it, and two // spellings of one address is what makes a file in use look unused. if got.Base != "http://mirror:8081" { t.Errorf("expected the mirror's own base, got %q", got.Base) } if len(got.Models) != 1 || got.Models[0].Model != "llama" { t.Fatalf("expected the one model on disk, got %+v", got.Models) } if got.Models[0].State != Present { t.Errorf("expected present, got %q", got.Models[0].State) } } func TestFetchReturnsBeforeTheDownloadFinishes(t *testing.T) { c, _ := testClient(t) got, err := c.Fetch(t.Context(), FetchRequest{ Source: serving(t, "weights"), Model: "m", Version: "v", }) if err != nil { t.Fatal(err) } // Under way, not done. A caller that waited for the bytes would hold a // connection open for an hour on a real model and time out in between. if got.Status.State != Fetching && got.Status.State != Present { t.Errorf("expected the download to be under way, got %q", got.Status.State) } if got.URL != "http://mirror:8081/m/v/model.gguf" { t.Errorf("the answer should say where the catalog will point; got %q", got.URL) } waitFor(t, "the download to finish", func() bool { st, err := c.Status(t.Context(), "m", "v") return err == nil && st.Status.State == Present }) } func TestTheMirrorsReasonSurvivesTheHop(t *testing.T) { c, _ := testClient(t) _, err := c.Fetch(t.Context(), FetchRequest{Source: "http://x/model.gguf"}) // The whole point of the mirror putting its reason in the body is that the // far end can show it. A client that kept only the status code would turn // "model and version are required" into "the mirror said 400". var apiErr *APIError if !errors.As(err, &apiErr) { t.Fatalf("expected an APIError, got %v", err) } if apiErr.Status != http.StatusBadRequest { t.Errorf("expected 400, got %d", apiErr.Status) } if !strings.Contains(apiErr.Error(), "version") { t.Errorf("the reason should name what was missing; got %q", apiErr.Error()) } } func TestAnUnknownModelComesBackAsNotFound(t *testing.T) { c, _ := testClient(t) _, err := c.Status(t.Context(), "nope", "v1") var apiErr *APIError if !errors.As(err, &apiErr) || apiErr.Status != http.StatusNotFound { t.Fatalf("expected a 404 APIError, got %v", err) } } func TestRemoveReportsWhatWent(t *testing.T) { c, root := testClient(t) writeFile(t, filepath.Join(root, "m", "v", "part-1.safetensors"), "a") writeFile(t, filepath.Join(root, "m", "v", "part-2.safetensors"), "b") removed, err := c.Remove(t.Context(), "m", "v") if err != nil { t.Fatal(err) } if len(removed) != 2 { t.Errorf("both files of the version should have gone; got %v", removed) } } func TestHealthFailsWhenTheMirrorIsNotThere(t *testing.T) { // A closed port, not a slow one: the page has to be able to say the mirror // is unreachable rather than render an empty table that looks like a mirror // holding nothing. srv := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})) addr := srv.URL srv.Close() c, err := NewClient(addr, tlsconf.Config{}, 0) if err != nil { t.Fatal(err) } if err := c.Health(context.Background()); err == nil { t.Fatal("expected an error from a mirror that is not listening") } else if !strings.Contains(err.Error(), addr) { t.Errorf("the error should name the address that refused; got %q", err) } } func TestAnAddressWithoutAHostIsRefusedAtConstruction(t *testing.T) { // Caught here rather than at the first request: a typo in configuration // should stop the control plane starting, not surface as a broken page // hours later. if _, err := NewClient("10.0.0.146:8082", tlsconf.Config{}, 0); err == nil { t.Error("expected an address with no scheme to be refused") } }