package novelfire import ( "context" "testing" ) func TestSlugFromURL(t *testing.T) { cases := []struct { url string want string }{ {"https://novelfire.net/book/shadow-slave", "shadow-slave"}, {"https://novelfire.net/book/a-dragon-against-the-whole-world", "a-dragon-against-the-whole-world"}, {"https://novelfire.net/book/foo/chapter-1", "foo"}, {"https://novelfire.net/", ""}, {"not-a-url", "not-a-url"}, } for _, c := range cases { got := slugFromURL(c.url) if got != c.want { t.Errorf("slugFromURL(%q) = %q, want %q", c.url, got, c.want) } } } func TestChapterNumberFromURL(t *testing.T) { cases := []struct { url string want int }{ {"https://novelfire.net/book/shadow-slave/chapter-42", 42}, {"https://novelfire.net/book/shadow-slave/chapter-1000", 1000}, {"https://novelfire.net/book/shadow-slave/chap-7", 7}, {"https://novelfire.net/book/shadow-slave/ch-3", 3}, {"https://novelfire.net/book/shadow-slave/42", 42}, {"https://novelfire.net/book/shadow-slave/no-number-here", 0}, {"not-a-url", 0}, } for _, c := range cases { got := chapterNumberFromURL(c.url) if got != c.want { t.Errorf("chapterNumberFromURL(%q) = %d, want %d", c.url, got, c.want) } } } func TestParseChapterCount(t *testing.T) { cases := []struct { in string want int }{ {"123 Chapters", 123}, {"1,234 Chapters", 1234}, {"0", 0}, {"", 0}, {"500", 500}, } for _, c := range cases { got := parseChapterCount(c.in) if got != c.want { t.Errorf("parseChapterCount(%q) = %d, want %d", c.in, got, c.want) } } } func TestRetryGet_ContextCancellation(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() // cancel immediately stub := newStubClient() stub.setError("https://example.com/page", context.Canceled) _, err := retryGet(ctx, nil, stub, "https://example.com/page", 3, 0) if err == nil { t.Fatal("expected error on cancelled context") } } func TestRetryGet_EventualSuccess(t *testing.T) { stub := newStubClient() calls := 0 stub.setFn("https://example.com/page", func() (string, error) { calls++ if calls < 3 { return "", context.DeadlineExceeded } return "ok", nil }) got, err := retryGet(context.Background(), nil, stub, "https://example.com/page", 5, 0) if err != nil { t.Fatalf("unexpected error: %v", err) } if got != "ok" { t.Errorf("got %q, want html", got) } if calls != 3 { t.Errorf("expected 3 calls, got %d", calls) } } // ── minimal stub client for tests ───────────────────────────────────────────── type stubClient struct { errors map[string]error fns map[string]func() (string, error) } func newStubClient() *stubClient { return &stubClient{ errors: make(map[string]error), fns: make(map[string]func() (string, error)), } } func (s *stubClient) setError(u string, err error) { s.errors[u] = err } func (s *stubClient) setFn(u string, fn func() (string, error)) { s.fns[u] = fn } func (s *stubClient) GetContent(_ context.Context, pageURL string) (string, error) { if fn, ok := s.fns[pageURL]; ok { return fn() } if err, ok := s.errors[pageURL]; ok { return "", err } return "", context.DeadlineExceeded }