Files
mangaBookmark/backend/internal/latest/browser_test.go
T
sulthan 84cfd1b2c1 Make browser sidecar on-demand (#44) (#50)
Closes #44. Chrome now starts on first CDP connection, tracks concurrent helpers, reaps after 300 seconds idle, preserves the named profile, and classifies reap interruptions. Shutdown stops Chrome's process group so cookie batches flush. ADR-0005 records the measured constraints and decisions. Verification: docker build, live CDP wake, graceful stop cleanup, sh -n, and go test ./... (7 packages, 3 no tests).

Reviewed-on: #50
Co-authored-by: Sulthan Zaki <sultankiki05@gmail.com>
Co-committed-by: Sulthan Zaki <sultankiki05@gmail.com>
2026-08-09 07:39:07 +07:00

78 lines
2.4 KiB
Go

package latest
import (
"context"
"errors"
"testing"
)
func TestKaganeAPIURL(t *testing.T) {
const uuid = "019f84bc-9ba0-7ed9-86f5-8b905ec7c28b"
tests := []struct {
name string
seriesURL string
want string
wantOK bool
}{
{
name: "series page maps to its API endpoint",
seriesURL: "https://kagane.to/series/" + uuid,
want: "https://kagane.to/api/v2/series/" + uuid,
wantOK: true,
},
{
name: "trailing slash is tolerated",
seriesURL: "https://kagane.to/series/" + uuid + "/",
want: "https://kagane.to/api/v2/series/" + uuid,
wantOK: true,
},
{"not a series path", "https://kagane.to/search", "", false},
{"foreign host", "https://evil.example/series/" + uuid, "", false},
{"garbage", "://", "", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, ok := kaganeAPIURL(tt.seriesURL)
if ok != tt.wantOK || got != tt.want {
t.Errorf("kaganeAPIURL(%q) = %q, %v; want %q, %v",
tt.seriesURL, got, ok, tt.want, tt.wantOK)
}
})
}
}
func TestNovelfullSeriesURL(t *testing.T) {
cases := []struct {
name string
url string
want bool
}{
{"series page", "https://novelfull.com/reverend-insanity.html", true},
{"foreign host", "https://evil.example/reverend-insanity.html", false},
{"not https", "http://novelfull.com/reverend-insanity.html", false},
{"not a series page", "https://novelfull.com/genre/Fantasy", false},
{"garbage", "://nope", false},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := novelfullSeriesURL(tc.url); got != tc.want {
t.Fatalf("novelfullSeriesURL(%q) = %v, want %v", tc.url, got, tc.want)
}
})
}
}
func TestClassifyBrowserInterruption(t *testing.T) {
if err := classifyBrowserError(context.Background(), true, context.Canceled); !errors.Is(err, errBrowserInterrupted) {
t.Fatalf("classifyBrowserError(context.Canceled) = %v, want browser interruption", err)
}
if err := classifyBrowserError(context.Background(), false, context.Canceled); errors.Is(err, errBrowserInterrupted) {
t.Fatalf("ordinary cancellation misclassified as browser interruption: %v", err)
}
caller, cancel := context.WithCancel(context.Background())
cancel()
if err := classifyBrowserError(caller, true, context.Canceled); errors.Is(err, errBrowserInterrupted) {
t.Fatalf("caller cancellation misclassified as browser interruption: %v", err)
}
}