From 2154a9f20324657bb9ec1e105308915d8a26efe3 Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Fri, 1 May 2026 20:41:18 -0400 Subject: [PATCH] fix(client_test): unskip vision test with embedded test image - Remove t.Skip for TestSendMessageWithImage (qwen3-8b-vision works with base64) - Add test_image.jpg (400x300) to test directory alongside test file - Load image at runtime, convert to base64 data URL for API - Collect full response in bytes.Buffer and verify non-empty output --- backend/internal/client/client_test.go | 28 +++++++++++++++++-------- backend/internal/client/test_image.jpg | Bin 0 -> 24197 bytes 2 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 backend/internal/client/test_image.jpg diff --git a/backend/internal/client/client_test.go b/backend/internal/client/client_test.go index 29c4655..772c2d5 100644 --- a/backend/internal/client/client_test.go +++ b/backend/internal/client/client_test.go @@ -3,7 +3,9 @@ package client import ( "bytes" "context" + "encoding/base64" "net/url" + "os" "testing" "time" @@ -94,11 +96,6 @@ func TestSummarizeChat(t *testing.T) { } func TestSendMessageWithImage(t *testing.T) { - // Skip: qwen3-8b-vision on LlamaSwap returns 502 for vision requests. - // Run manually when a stable vision backend is available: - // go test -run TestSendMessageWithImage ./internal/client/ - t.Skip("requires stable vision backend - LlamaSwap qwen3-8b-vision returns 502 for image inputs") - // Initialize Client baseURL, err := url.Parse("https://llm-api.va.reichard.io/v1") if err != nil { @@ -110,21 +107,34 @@ func TestSendMessageWithImage(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) defer cancel() - // Build Data URL from embedded test image (10x10 red PNG) - const dataURL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAADklEQVR42mNk+P/k/wEAn6gE9QxN+7QAAAAASUVORK5CYII=" + // Load Test Image and Convert to Base64 Data URL + imgData, err := os.ReadFile("test_image.jpg") + if err != nil { + t.Fatalf("Failed to read test image: %v", err) + } + dataURL := "data:image/jpeg;base64," + base64.StdEncoding.EncodeToString(imgData) // Generate Text Stream + var outputBuf bytes.Buffer _, err = client.SendMessage(ctx, []*store.Message{{ Role: "user", - Content: "What is in this image?", + Content: "Describe this image in detail.", Images: []string{dataURL}, }}, model, func(mc *MessageChunk) error { if mc.Message != nil { - t.Logf("Received: %s", *mc.Message) + outputBuf.WriteString(*mc.Message) } return nil }) if err != nil { t.Fatalf("Failed to generate text stream: %v", err) } + + // Verify Response + output := outputBuf.String() + if output == "" { + t.Error("No content was written to the buffer") + } else { + t.Logf("Model response (%d chars): %s", len(output), output) + } } diff --git a/backend/internal/client/test_image.jpg b/backend/internal/client/test_image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9fc93c51241b71523289d93a78ebaafd8ff009da GIT binary patch literal 24197 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

W9oSsQ|CGwv~ACy>4^_uTX-?z*~as-8Ututt5 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 K$`}_2Pyg98wr^Mf literal 0 HcmV?d00001