Files
Devour/pkg/godocs/parser_test.go
Tomas Dvorak 55885a0e8f first commit
2026-02-22 10:42:17 +01:00

269 lines
7.2 KiB
Go

package godocs
import (
"strings"
"testing"
"github.com/PuerkitoBio/goquery"
)
const testPackageHTML = `
<!DOCTYPE html>
<html>
<head><title>runtime - pkg.go.dev</title></head>
<body>
<nav class="go-Breadcrumb">
<ol>
<li><a href="/">Discover Packages</a></li>
<li><a href="/k8s.io/apimachinery">k8s.io/apimachinery</a></li>
<li><a href="/k8s.io/apimachinery/pkg">pkg</a></li>
<li><a href="/k8s.io/apimachinery/pkg/runtime">runtime</a></li>
</ol>
</nav>
<h1 class="UnitHeader-titleHeading">runtime</h1>
<div class="go-Main-headerDetails">
<span data-test-id="UnitHeader-version"><a href="?tab=versions">v0.35.1</a></span>
<span data-test-id="UnitHeader-importedby"><a href="?tab=importedby">Imported by: 144,729</a></span>
<span data-test-id="UnitHeader-licenses"><a href="?tab=licenses">Apache-2.0</a></span>
</div>
<div class="Documentation">
<p>Package runtime defines conversions between generic types and structs to map query strings to struct objects.</p>
<p>This is additional documentation text for the package.</p>
</div>
<div class="Documentation-function">
<div class="Documentation-functionHeader">func DecodeInto</div>
<pre>func DecodeInto(d Decoder, data []byte, into Object) error</pre>
<p>DecodeInto is a helper function that decodes the given data into the provided object.</p>
</div>
<div class="Documentation-type">
<div class="Documentation-typeHeader">type Codec</div>
<pre>type Codec struct {
Encoder Encoder
Decoder Decoder
}</pre>
<p>Codec is a struct that holds an encoder and decoder.</p>
<div class="Documentation-method">
<div class="Documentation-methodHeader">func (*Codec) Encode</div>
<pre>func (c *Codec) Encode(obj Object) ([]byte, error)</pre>
<p>Encode encodes the given object.</p>
</div>
</div>
<div class="Documentation-constants">
<pre>const (
ContentTypeJSON = "application/json"
ContentTypeYAML = "application/yaml"
)</pre>
<p>Content types for different formats.</p>
</div>
<div class="Documentation-variables">
<pre>var DefaultScheme = NewScheme()</pre>
<p>DefaultScheme is the default scheme used for encoding/decoding.</p>
</div>
</body>
</html>
`
const testSearchHTML = `
<!DOCTYPE html>
<html>
<body>
<div class="SearchSnippet">
<h2><a href="/k8s.io/apimachinery/pkg/runtime">runtime <span class="SearchSnippet-header-path">(k8s.io/apimachinery/pkg/runtime)</span></a></h2>
<p class="SearchSnippet-synopsis">Package runtime defines conversions between generic types and structs.</p>
<div class="SearchSnippet-infoLabel">
<a href="?tab=importedby">Imported by: <strong>144,729</strong></a>
<span>v0.35.1 published on <strong>Dec 4, 2025</strong></span>
<a href="?tab=licenses">Apache-2.0</a>
</div>
</div>
<div class="SearchSnippet">
<h2><a href="/github.com/google/go-querystring/query">query <span class="SearchSnippet-header-path">(github.com/google/go-querystring/query)</span></a></h2>
<p class="SearchSnippet-synopsis">Package query implements encoding of structs into URL query parameters.</p>
<div class="SearchSnippet-infoLabel">
<a href="?tab=importedby">Imported by: <strong>5,111</strong></a>
<span>v1.2.0 published on <strong>Nov 10, 2025</strong></span>
<a href="?tab=licenses">BSD-3-Clause</a>
</div>
</div>
</body>
</html>
`
func TestParsePackagePage(t *testing.T) {
parser := NewParser()
pkg, err := parser.ParsePackagePage(testPackageHTML, "https://pkg.go.dev/k8s.io/apimachinery/pkg/runtime")
if err != nil {
t.Fatalf("ParsePackagePage failed: %v", err)
}
if pkg.Name != "runtime" {
t.Errorf("Expected name 'runtime', got '%s'", pkg.Name)
}
if pkg.ImportPath != "k8s.io/apimachinery/pkg/runtime" {
t.Errorf("Expected import path 'k8s.io/apimachinery/pkg/runtime', got '%s'", pkg.ImportPath)
}
if pkg.Version != "v0.35.1" {
t.Errorf("Expected version 'v0.35.1', got '%s'", pkg.Version)
}
if pkg.ImportedBy != 144729 {
t.Errorf("Expected imported by 144729, got %d", pkg.ImportedBy)
}
if pkg.Synopsis == "" {
t.Error("Expected non-empty synopsis")
}
if len(pkg.Functions) == 0 {
t.Error("Expected at least one function")
}
if len(pkg.Types) == 0 {
t.Error("Expected at least one type")
}
if len(pkg.Constants) == 0 {
t.Error("Expected at least one constant")
}
if len(pkg.Variables) == 0 {
t.Error("Expected at least one variable")
}
}
func TestParseSearchResults(t *testing.T) {
parser := NewParser()
results, err := parser.ParseSearchResults(testSearchHTML)
if err != nil {
t.Fatalf("ParseSearchResults failed: %v", err)
}
if len(results) < 2 {
t.Fatalf("Expected at least 2 results, got %d", len(results))
}
first := results[0]
if first.Synopsis == "" {
t.Error("Expected non-empty synopsis")
}
if first.Path == "" {
t.Error("Expected non-empty path")
}
if first.URL == "" {
t.Error("Expected non-empty URL")
}
}
func TestIsExported(t *testing.T) {
tests := []struct {
name string
expected bool
}{
{"Exported", true},
{"unexported", false},
{"", false},
{"CamelCase", true},
{"camelCase", false},
{"X", true},
{"x", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := isExported(tt.name); got != tt.expected {
t.Errorf("isExported(%q) = %v, want %v", tt.name, got, tt.expected)
}
})
}
}
func TestCleanWhitespace(t *testing.T) {
tests := []struct {
input string
expected string
}{
{" hello world ", "hello world"},
{"single", "single"},
{"multiple spaces here", "multiple spaces here"},
{"\n\ttabs\t\n", "tabs"},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
if got := cleanWhitespace(tt.input); got != tt.expected {
t.Errorf("cleanWhitespace(%q) = %q, want %q", tt.input, got, tt.expected)
}
})
}
}
func TestParseCount(t *testing.T) {
tests := []struct {
input string
expected int
}{
{"144729", 144729},
{"5,111", 5111},
{"0", 0},
{"1,234,567", 1234567},
{"abc", 0},
{"", 0},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
if got := parseCount(tt.input); got != tt.expected {
t.Errorf("parseCount(%q) = %d, want %d", tt.input, got, tt.expected)
}
})
}
}
func TestExtractImportPath(t *testing.T) {
parser := NewParser()
tests := []struct {
html string
url string
expected string
}{
{
html: `<nav class="go-Breadcrumb"><li><a href="/">Discover</a></li><li><a href="/k8s.io/apimachinery">k8s.io/apimachinery</a></li><li><a href="/k8s.io/apimachinery/pkg">pkg</a></li><li><a href="/k8s.io/apimachinery/pkg/runtime">runtime</a></li></nav>`,
url: "https://pkg.go.dev/k8s.io/apimachinery/pkg/runtime",
expected: "k8s.io/apimachinery/pkg/runtime",
},
{
html: `<nav class="go-Breadcrumb"><li><a href="/github.com/user/repo">github.com/user/repo</a></li></nav>`,
url: "https://pkg.go.dev/github.com/user/repo@v1.0.0",
expected: "github.com/user/repo",
},
}
for _, tt := range tests {
t.Run(tt.expected, func(t *testing.T) {
doc, err := goquery.NewDocumentFromReader(strings.NewReader(tt.html))
if err != nil {
t.Fatalf("Failed to parse HTML: %v", err)
}
got := parser.extractImportPath(doc, tt.url)
if got != tt.expected {
t.Errorf("extractImportPath() = %q, want %q", got, tt.expected)
}
})
}
}