package fa
import (
"bytes"
"fmt"
"strings"
"testing"
"github.com/PuerkitoBio/goquery"
)
// syntheticSubmissionHTML is a minimal hand-rolled page that exercises every
// selector the parser cares about. Real FA HTML differs in ways that
// fixture-driven tests will catch; this synthetic input pins the parser
// against a stable, deterministic input independent of FA's mood.
const syntheticSubmissionHTML = `
CategoryThemeSpeciesGenderResolution
Artwork (Digital)DigitalWolfMale1920 x 1080
`
func TestParseSubmission_Synthetic(t *testing.T) {
doc, err := goquery.NewDocumentFromReader(strings.NewReader(syntheticSubmissionHTML))
if err != nil {
t.Fatalf("setup: %v", err)
}
sub, err := parseSubmission(1234, doc)
if err != nil {
t.Fatalf("parseSubmission: %v", err)
}
checks := []struct {
name string
got any
want any
}{
{"ID", sub.ID, SubmissionID(1234)},
{"Title", sub.Title, "My Test Submission"},
{"Author.Name", sub.Author.Name, "somefurry"},
{"Author.DisplayName", sub.Author.DisplayName, "SomeFurry"},
{"Author.AvatarURL", sub.Author.AvatarURL, "https://d.example/avatars/somefurry.png"},
{"Rating", sub.Rating, RatingGeneral},
{"Category", sub.Category, Category("Artwork (Digital)")},
{"Type", sub.Type, Type("Digital")},
{"Species", sub.Species, Species("Wolf")},
{"Gender", sub.Gender, Gender("Male")},
{"FileURL", sub.FileURL, "https://d.example/art/somefurry/1234_full.png"},
{"ThumbURL", sub.ThumbURL, "https://d.example/art/somefurry/1234_thumb.png"},
{"Width", sub.Width, 1920},
{"Height", sub.Height, 1080},
{"Stats.Views", sub.Stats.Views, 1234},
{"Stats.Favorites", sub.Stats.Favorites, 56},
{"Stats.Comments", sub.Stats.Comments, 7},
{"Prev (Newer)", sub.Prev, SubmissionID(1235)},
{"Next (Older)", sub.Next, SubmissionID(1233)},
{"len(Tags)", len(sub.Tags), 2},
}
for _, c := range checks {
if c.got != c.want {
t.Errorf("%s = %v; want %v", c.name, c.got, c.want)
}
}
if !sub.PostedAt.IsZero() && sub.PostedAt.Year() != 2026 {
t.Errorf("PostedAt year = %d; want 2026", sub.PostedAt.Year())
}
if !strings.Contains(sub.Description, "world") {
t.Errorf("Description missing expected content: %q", sub.Description)
}
}
// TestParseSubmission_FavoritedState verifies parseSubmission reports the
// authenticated viewer's favorite state. FA renders exactly one of the
// "+Fav" (/fav/) or "−Fav" (/unfav/) anchors, matching the viewer's current
// state; an anonymous fetch shows neither.
func TestParseSubmission_FavoritedState(t *testing.T) {
const tmpl = `
%s
`
cases := []struct {
name string
link string
want bool
}{
{"favorited shows unfav link", `−Fav`, true},
{"not favorited shows fav link", `+Fav`, false},
{"anonymous shows neither", ``, false},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
doc, err := goquery.NewDocumentFromReader(strings.NewReader(fmt.Sprintf(tmpl, c.link)))
if err != nil {
t.Fatalf("setup: %v", err)
}
sub, err := parseSubmission(1234, doc)
if err != nil {
t.Fatalf("parseSubmission: %v", err)
}
if sub.Favorited != c.want {
t.Errorf("Favorited = %v; want %v", sub.Favorited, c.want)
}
})
}
}
func TestParseSubmission_MissingTitleErrors(t *testing.T) {
doc, err := goquery.NewDocumentFromReader(strings.NewReader(""))
if err != nil {
t.Fatalf("setup: %v", err)
}
if _, err := parseSubmission(1, doc); err == nil {
t.Fatal("expected parse error for missing title")
}
}
// TestParseSubmission_RealFixture runs the parser against a real FA HTML
// dump captured by the `fixtures` build tag. Skips cleanly if no fixture
// has been recorded.
func TestParseSubmission_RealFixture(t *testing.T) {
raw := loadFixture(t, "submission.html")
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(raw))
if err != nil {
t.Fatalf("read doc: %v", err)
}
sub, err := parseSubmission(0, doc)
if err != nil {
t.Fatalf("parseSubmission(real): %v", err)
}
// We can't assert exact values against a fixture whose contents we don't
// pin in this repo. Instead assert that the load-bearing fields populated.
if sub.Title == "" {
t.Error("real fixture: Title is empty")
}
if sub.Author.Name == "" {
t.Error("real fixture: Author.Name is empty")
}
if sub.FileURL == "" {
t.Error("real fixture: FileURL is empty")
}
}