Files
vessel/backend/internal/api/fetcher_test.go
vikingowl d81430e1aa test: extend test coverage for backend and frontend
Backend:
- Add fetcher_test.go (HTML stripping, URL fetching utilities)
- Add model_registry_test.go (parsing, size ranges, model matching)
- Add database_test.go (CRUD operations, migrations)
- Add tests for geolocation, search, tools, version handlers

Frontend unit tests (469 total):
- OllamaClient: 22 tests for API methods with mocked fetch
- Memory/RAG: tokenizer, chunker, summarizer, embeddings, vector-store
- Services: prompt-resolution, conversation-summary
- Components: Skeleton, BranchNavigator, ConfirmDialog, ThinkingBlock
- Utils: export, import, file-processor, keyboard
- Tools: builtin math parser (44 tests)

E2E tests (28 total):
- Set up Playwright with Chromium
- App loading, sidebar navigation, settings page
- Chat interface, responsive design, accessibility
- Import dialog, project modal interactions

Config changes:
- Add browser conditions to vitest.config.ts for Svelte 5 components
- Add playwright.config.ts for E2E testing
- Add test:e2e scripts to package.json
- Update .gitignore to exclude test artifacts

Closes #8
2026-01-22 11:05:49 +01:00

197 lines
4.4 KiB
Go

package api
import (
"testing"
)
func TestDefaultFetchOptions(t *testing.T) {
opts := DefaultFetchOptions()
if opts.MaxLength != 500000 {
t.Errorf("expected MaxLength 500000, got %d", opts.MaxLength)
}
if opts.Timeout.Seconds() != 30 {
t.Errorf("expected Timeout 30s, got %v", opts.Timeout)
}
if opts.UserAgent == "" {
t.Error("expected non-empty UserAgent")
}
if opts.Headers == nil {
t.Error("expected Headers to be initialized")
}
if !opts.FollowRedirects {
t.Error("expected FollowRedirects to be true")
}
if opts.WaitTime.Seconds() != 2 {
t.Errorf("expected WaitTime 2s, got %v", opts.WaitTime)
}
}
func TestStripHTMLTags(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{
name: "removes simple tags",
input: "<p>Hello World</p>",
expected: "Hello World",
},
{
name: "removes nested tags",
input: "<div><span>Nested</span> content</div>",
expected: "Nested content",
},
{
name: "removes script tags with content",
input: "<p>Before</p><script>alert('xss')</script><p>After</p>",
expected: "Before After",
},
{
name: "removes style tags with content",
input: "<p>Text</p><style>.foo{color:red}</style><p>More</p>",
expected: "Text More",
},
{
name: "collapses whitespace",
input: "<p>Lots of spaces</p>",
expected: "Lots of spaces",
},
{
name: "handles empty input",
input: "",
expected: "",
},
{
name: "handles plain text",
input: "No HTML here",
expected: "No HTML here",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := stripHTMLTags(tt.input)
if result != tt.expected {
t.Errorf("expected %q, got %q", tt.expected, result)
}
})
}
}
func TestIsJSRenderedPage(t *testing.T) {
f := &Fetcher{}
tests := []struct {
name string
content string
expected bool
}{
{
name: "short content indicates JS rendering",
content: "<html><body><div id=\"app\"></div></body></html>",
expected: true,
},
{
name: "React root div with minimal content",
content: "<html><body><div id=\"root\"></div><script>window.__INITIAL_STATE__={}</script></body></html>",
expected: true,
},
{
name: "Next.js pattern",
content: "<html><body><div id=\"__next\"></div></body></html>",
expected: true,
},
{
name: "Nuxt.js pattern",
content: "<html><body><div id=\"__nuxt\"></div></body></html>",
expected: true,
},
{
name: "noscript indicator",
content: "<html><body><noscript>Enable JS</noscript><div></div></body></html>",
expected: true,
},
{
name: "substantial content is not JS-rendered",
content: generateLongContent(2000),
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := f.isJSRenderedPage(tt.content)
if result != tt.expected {
t.Errorf("expected %v, got %v", tt.expected, result)
}
})
}
}
// generateLongContent creates content of specified length
func generateLongContent(length int) string {
base := "<html><body><article>"
content := ""
word := "word "
for len(content) < length {
content += word
}
return base + content + "</article></body></html>"
}
func TestFetchMethod_String(t *testing.T) {
tests := []struct {
method FetchMethod
expected string
}{
{FetchMethodCurl, "curl"},
{FetchMethodWget, "wget"},
{FetchMethodChrome, "chrome"},
{FetchMethodNative, "native"},
}
for _, tt := range tests {
t.Run(string(tt.method), func(t *testing.T) {
if string(tt.method) != tt.expected {
t.Errorf("expected %q, got %q", tt.expected, string(tt.method))
}
})
}
}
func TestFetchResult_Fields(t *testing.T) {
result := FetchResult{
Content: "test content",
ContentType: "text/html",
FinalURL: "https://example.com",
StatusCode: 200,
Method: FetchMethodNative,
Truncated: true,
OriginalSize: 1000000,
}
if result.Content != "test content" {
t.Errorf("Content mismatch")
}
if result.ContentType != "text/html" {
t.Errorf("ContentType mismatch")
}
if result.FinalURL != "https://example.com" {
t.Errorf("FinalURL mismatch")
}
if result.StatusCode != 200 {
t.Errorf("StatusCode mismatch")
}
if result.Method != FetchMethodNative {
t.Errorf("Method mismatch")
}
if !result.Truncated {
t.Errorf("Truncated should be true")
}
if result.OriginalSize != 1000000 {
t.Errorf("OriginalSize mismatch")
}
}