test: add mock-based site extractor test infrastructure
Create exported extractortest package with MockBrowser, MockDocument, and MockNode that support selector-based responses for testing site extractors without a real browser. Add extraction tests for DuckDuckGo (result parsing, empty results, no links, full search flow) and Powerball (drawing parsing, next drawing parsing with billion/million, error cases, full GetCurrent flow). Closes #21 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
103
extractortest/mock.go
Normal file
103
extractortest/mock.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// Package extractortest provides mock implementations of the extractor
|
||||
// interfaces for use in unit tests. The mocks support selector-based
|
||||
// responses so site extractors can exercise their parsing logic without
|
||||
// a real browser.
|
||||
package extractortest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gitea.stevedudenhoeffer.com/steve/go-extractor"
|
||||
)
|
||||
|
||||
// MockNode implements extractor.Node with configurable selector responses.
|
||||
type MockNode struct {
|
||||
TextValue string
|
||||
TextErr error
|
||||
ContentValue string
|
||||
Attrs map[string]string
|
||||
// Children maps CSS selectors to the nodes that should be returned.
|
||||
Children map[string]extractor.Nodes
|
||||
}
|
||||
|
||||
var _ extractor.Node = &MockNode{}
|
||||
|
||||
func (m *MockNode) Content() (string, error) { return m.ContentValue, nil }
|
||||
func (m *MockNode) Text() (string, error) { return m.TextValue, m.TextErr }
|
||||
|
||||
func (m *MockNode) Attr(name string) (string, error) {
|
||||
if m.Attrs == nil {
|
||||
return "", nil
|
||||
}
|
||||
v, ok := m.Attrs[name]
|
||||
if !ok {
|
||||
return "", nil
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (m *MockNode) Screenshot() ([]byte, error) { return nil, nil }
|
||||
func (m *MockNode) Type(_ string) error { return nil }
|
||||
func (m *MockNode) Click() error { return nil }
|
||||
func (m *MockNode) SetHidden(_ bool) error { return nil }
|
||||
func (m *MockNode) SetAttribute(_, _ string) error { return nil }
|
||||
|
||||
func (m *MockNode) Select(selector string) extractor.Nodes {
|
||||
if m.Children == nil {
|
||||
return nil
|
||||
}
|
||||
return m.Children[selector]
|
||||
}
|
||||
|
||||
func (m *MockNode) SelectFirst(selector string) extractor.Node {
|
||||
return m.Select(selector).First()
|
||||
}
|
||||
|
||||
func (m *MockNode) ForEach(selector string, fn func(extractor.Node) error) error {
|
||||
nodes := m.Select(selector)
|
||||
for _, n := range nodes {
|
||||
if err := fn(n); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MockDocument implements extractor.Document with configurable selector responses.
|
||||
type MockDocument struct {
|
||||
MockNode
|
||||
URLValue string
|
||||
}
|
||||
|
||||
var _ extractor.Document = &MockDocument{}
|
||||
|
||||
func (m *MockDocument) URL() string { return m.URLValue }
|
||||
func (m *MockDocument) Refresh() error { return nil }
|
||||
func (m *MockDocument) Close() error { return nil }
|
||||
func (m *MockDocument) WaitForNetworkIdle(_ *time.Duration) error { return nil }
|
||||
|
||||
// Content on MockDocument returns its own ContentValue (overrides embedded MockNode).
|
||||
func (m *MockDocument) Content() (string, error) { return m.ContentValue, nil }
|
||||
|
||||
// MockBrowser implements extractor.Browser. Configure Documents to map URLs
|
||||
// to MockDocuments that will be returned from Open.
|
||||
type MockBrowser struct {
|
||||
Documents map[string]*MockDocument
|
||||
}
|
||||
|
||||
var _ extractor.Browser = &MockBrowser{}
|
||||
|
||||
func (m *MockBrowser) Close() error { return nil }
|
||||
|
||||
func (m *MockBrowser) Open(_ context.Context, url string, _ extractor.OpenPageOptions) (extractor.Document, error) {
|
||||
if m.Documents == nil {
|
||||
return nil, fmt.Errorf("no documents configured")
|
||||
}
|
||||
doc, ok := m.Documents[url]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no mock document for URL %q", url)
|
||||
}
|
||||
return doc, nil
|
||||
}
|
||||
Reference in New Issue
Block a user