mirror of
https://github.com/mark3labs/kit.git
synced 2026-06-14 03:30:26 +00:00
4e82fac442
TestDetectMediaType/.go fails on CI images (Ubuntu mime-support) where /etc/mime.types registers '.go → text/x-go', because mime.TypeByExtension reads those files at init. The test intended to exercise the 'unknown extension falls through to text/plain' branch but used a real extension, making the assertion environment-dependent. Replace '.go' with '.kitsyntheticext', an invented extension that no system MIME database registers. The fallback path is now exercised deterministically on any host.
210 lines
6.9 KiB
Go
210 lines
6.9 KiB
Go
package fileutil
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestProcessFileAttachments_TextFile(t *testing.T) {
|
|
// Create a temp text file
|
|
dir := t.TempDir()
|
|
textFile := filepath.Join(dir, "hello.txt")
|
|
if err := os.WriteFile(textFile, []byte("hello world"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
text := "@" + textFile + " check this out"
|
|
result := ProcessFileAttachments(text, dir)
|
|
|
|
if len(result.FileParts) != 0 {
|
|
t.Errorf("expected 0 FileParts for text file, got %d", len(result.FileParts))
|
|
}
|
|
if result.ProcessedText == text {
|
|
t.Error("expected text file to be XML-wrapped, but got original text unchanged")
|
|
}
|
|
// Should contain XML wrapping
|
|
if !contains(result.ProcessedText, "<file path=") {
|
|
t.Error("expected XML <file> wrapping in processed text")
|
|
}
|
|
if !contains(result.ProcessedText, "hello world") {
|
|
t.Error("expected file content in processed text")
|
|
}
|
|
}
|
|
|
|
func TestProcessFileAttachments_BinaryFile(t *testing.T) {
|
|
// Create a minimal PNG file (binary)
|
|
dir := t.TempDir()
|
|
pngFile := filepath.Join(dir, "image.png")
|
|
// Minimal valid PNG header
|
|
pngData := []byte{
|
|
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, // PNG signature
|
|
0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, // IHDR chunk
|
|
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, // 1x1
|
|
0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xDE, // 8bit RGB
|
|
0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, // IDAT chunk
|
|
0x08, 0xD7, 0x63, 0xF8, 0xCF, 0xC0, 0x00, 0x00,
|
|
0x00, 0x02, 0x00, 0x01, 0xE2, 0x21, 0xBC, 0x33,
|
|
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, // IEND chunk
|
|
0xAE, 0x42, 0x60, 0x82,
|
|
}
|
|
if err := os.WriteFile(pngFile, pngData, 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
text := "@" + pngFile + " what is this image?"
|
|
result := ProcessFileAttachments(text, dir)
|
|
|
|
if len(result.FileParts) != 1 {
|
|
t.Fatalf("expected 1 FilePart for binary file, got %d", len(result.FileParts))
|
|
}
|
|
if result.FileParts[0].MediaType != "image/png" {
|
|
t.Errorf("expected media type image/png, got %s", result.FileParts[0].MediaType)
|
|
}
|
|
if result.FileParts[0].Filename != "image.png" {
|
|
t.Errorf("expected filename image.png, got %s", result.FileParts[0].Filename)
|
|
}
|
|
// The @token should be removed from the text
|
|
if contains(result.ProcessedText, "@") && contains(result.ProcessedText, pngFile) {
|
|
t.Error("expected @token to be removed from processed text for binary file")
|
|
}
|
|
if contains(result.ProcessedText, "what is this image?") {
|
|
// Good, the prompt text should remain
|
|
} else {
|
|
t.Error("expected prompt text to remain in processed text")
|
|
}
|
|
}
|
|
|
|
func TestProcessFileAttachments_MCPResource(t *testing.T) {
|
|
// Test @mcp:server:uri token processing with a mock reader
|
|
text := "@mcp:test-server:docs://readme tell me about this"
|
|
reader := func(serverName, uri string) (string, []byte, string, bool, error) {
|
|
if serverName != "test-server" || uri != "docs://readme" {
|
|
t.Errorf("unexpected server/uri: %s/%s", serverName, uri)
|
|
}
|
|
return "Hello from MCP resource", nil, "text/plain", false, nil
|
|
}
|
|
|
|
result := ProcessFileAttachments(text, "/tmp", reader)
|
|
|
|
if len(result.FileParts) != 0 {
|
|
t.Errorf("expected 0 FileParts for text MCP resource, got %d", len(result.FileParts))
|
|
}
|
|
if !contains(result.ProcessedText, "<resource uri=\"docs://readme\" server=\"test-server\">") {
|
|
t.Error("expected <resource> XML wrapping in processed text")
|
|
}
|
|
if !contains(result.ProcessedText, "Hello from MCP resource") {
|
|
t.Error("expected MCP resource content in processed text")
|
|
}
|
|
}
|
|
|
|
func TestProcessFileAttachments_MCPResource_Binary(t *testing.T) {
|
|
// Test @mcp:server:uri token processing for a binary resource
|
|
text := "@mcp:test-server:images://logo describe this"
|
|
reader := func(serverName, uri string) (string, []byte, string, bool, error) {
|
|
if serverName != "test-server" || uri != "images://logo" {
|
|
t.Errorf("unexpected server/uri: %s/%s", serverName, uri)
|
|
}
|
|
return "", []byte{0x89, 0x50, 0x4E, 0x47}, "image/png", true, nil
|
|
}
|
|
|
|
result := ProcessFileAttachments(text, "/tmp", reader)
|
|
|
|
if len(result.FileParts) != 1 {
|
|
t.Fatalf("expected 1 FilePart for binary MCP resource, got %d", len(result.FileParts))
|
|
}
|
|
if result.FileParts[0].MediaType != "image/png" {
|
|
t.Errorf("expected media type image/png, got %s", result.FileParts[0].MediaType)
|
|
}
|
|
if result.FileParts[0].Filename != "logo" {
|
|
t.Errorf("expected filename 'logo', got %s", result.FileParts[0].Filename)
|
|
}
|
|
// The @token should be removed from the text
|
|
if contains(result.ProcessedText, "@mcp:") {
|
|
t.Error("expected @mcp: token to be removed from processed text for binary resource")
|
|
}
|
|
}
|
|
|
|
func TestProcessFileAttachments_NoReader(t *testing.T) {
|
|
// Without an MCP reader, @mcp: tokens should be left as-is
|
|
text := "@mcp:server:resource this is a test"
|
|
result := ProcessFileAttachments(text, "/tmp")
|
|
|
|
if len(result.FileParts) != 0 {
|
|
t.Errorf("expected 0 FileParts, got %d", len(result.FileParts))
|
|
}
|
|
// The @mcp: token should remain unchanged since no reader was provided
|
|
if result.ProcessedText != text {
|
|
t.Errorf("expected text unchanged without reader, got: %s", result.ProcessedText)
|
|
}
|
|
}
|
|
|
|
func TestDetectMediaType(t *testing.T) {
|
|
tests := []struct {
|
|
ext string
|
|
content []byte
|
|
expected string
|
|
}{
|
|
// An intentionally-synthetic extension that is not registered
|
|
// in any system MIME database. Exercises the "unknown ext +
|
|
// no content" branch, which must return the text/plain default.
|
|
// Do not use real extensions (e.g. .go) here: CI images often
|
|
// ship /etc/mime.types with entries like ".go → text/x-go",
|
|
// which would make the assertion environment-dependent.
|
|
{".kitsyntheticext", nil, "text/plain"},
|
|
{".png", []byte{0x89, 0x50, 0x4E, 0x47}, "image/png"},
|
|
{".jpg", []byte{0xFF, 0xD8, 0xFF}, "image/jpeg"},
|
|
{".pdf", []byte{0x25, 0x50, 0x44, 0x46}, "application/pdf"},
|
|
{".txt", []byte("hello"), "text/plain"},
|
|
{".wav", nil, "audio/wav"},
|
|
{".webp", nil, "image/webp"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.ext, func(t *testing.T) {
|
|
got := detectMediaType("test"+tt.ext, tt.content)
|
|
if got != tt.expected {
|
|
t.Errorf("detectMediaType(%q) = %q, want %q", tt.ext, got, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsBinaryMediaType(t *testing.T) {
|
|
tests := []struct {
|
|
mimeType string
|
|
expected bool
|
|
}{
|
|
{"image/png", true},
|
|
{"image/jpeg", true},
|
|
{"audio/wav", true},
|
|
{"video/mp4", true},
|
|
{"application/pdf", true},
|
|
{"text/plain", false},
|
|
{"text/go", false},
|
|
{"application/json", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.mimeType, func(t *testing.T) {
|
|
got := isBinaryMediaType(tt.mimeType)
|
|
if got != tt.expected {
|
|
t.Errorf("isBinaryMediaType(%q) = %v, want %v", tt.mimeType, got, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func contains(s, substr string) bool {
|
|
return len(s) >= len(substr) && (s == substr || len(s) > 0 && containsStr(s, substr))
|
|
}
|
|
|
|
func containsStr(s, substr string) bool {
|
|
for i := 0; i <= len(s)-len(substr); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|