Release v0.2.34
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
package opensubtitles
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSearchParamsPrefersItsOwnIDOverTheParents(t *testing.T) {
|
||||
params := searchParams(Query{
|
||||
IMDBID: "tt0903747", ParentIMDBID: "tt0999999", Season: 1, Episode: 2,
|
||||
Languages: []string{"it", "en", "en"}, Type: "episode",
|
||||
})
|
||||
if got := params.Get("imdb_id"); got != "0903747" {
|
||||
t.Fatalf("imdb_id = %q, want the tt stripped", got)
|
||||
}
|
||||
if params.Has("parent_imdb_id") {
|
||||
t.Fatal("parent_imdb_id was sent alongside the episode's own id")
|
||||
}
|
||||
if got := params.Get("season_number"); got != "1" {
|
||||
t.Fatalf("season_number = %q", got)
|
||||
}
|
||||
// Sorted and deduplicated: the API rejects the list in any other shape.
|
||||
if got := params.Get("languages"); got != "en,it" {
|
||||
t.Fatalf("languages = %q, want %q", got, "en,it")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchParamsFallsBackToTheSeriesForAnEpisode(t *testing.T) {
|
||||
params := searchParams(Query{ParentIMDBID: "tt0903747", Season: 5, Episode: 14})
|
||||
if got := params.Get("parent_imdb_id"); got != "0903747" {
|
||||
t.Fatalf("parent_imdb_id = %q", got)
|
||||
}
|
||||
if got := params.Get("episode_number"); got != "14" {
|
||||
t.Fatalf("episode_number = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// A film must never be sent a season number: an id plus a season is how a search comes
|
||||
// back empty for a title that plainly exists.
|
||||
func TestSearchParamsSendsNoSeasonForAFilm(t *testing.T) {
|
||||
params := searchParams(Query{TMDBID: "550", Type: "movie"})
|
||||
if params.Has("season_number") || params.Has("episode_number") {
|
||||
t.Fatal("a film was searched for with episode numbers")
|
||||
}
|
||||
if got := params.Get("tmdb_id"); got != "550" {
|
||||
t.Fatalf("tmdb_id = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchParamsRefusesAQueryItCannotIdentify(t *testing.T) {
|
||||
if params := searchParams(Query{Languages: []string{"en"}}); len(params) != 0 {
|
||||
t.Fatalf("searchParams answered %v for a query with no identity", params)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchDropsRowsWithNoFileToFetch(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("Api-Key") != "key" {
|
||||
t.Errorf("api key header = %q", r.Header.Get("Api-Key"))
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"data": []any{
|
||||
map[string]any{"attributes": map[string]any{
|
||||
"language": "en", "ratings": 8.5, "download_count": 120,
|
||||
"foreign_parts_only": true, "release": "BluRay",
|
||||
"files": []any{map[string]any{"file_id": 42, "file_name": "x.srt"}},
|
||||
}},
|
||||
map[string]any{"attributes": map[string]any{"language": "it", "files": []any{}}},
|
||||
}})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := New("key", "Memby/test", "", "", 5*time.Second)
|
||||
client.SetBaseURL(server.URL)
|
||||
found, err := client.Search(context.Background(), Query{IMDBID: "tt1", Languages: []string{"en"}})
|
||||
if err != nil {
|
||||
t.Fatalf("Search: %v", err)
|
||||
}
|
||||
if len(found) != 1 {
|
||||
t.Fatalf("got %d candidates, want the one with a file", len(found))
|
||||
}
|
||||
if found[0].FileID != 42 || !found[0].Forced || found[0].Downloads != 120 {
|
||||
t.Fatalf("candidate decoded as %+v", found[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadFollowsTheLinkWithoutTheCredentials(t *testing.T) {
|
||||
var files *httptest.Server
|
||||
api := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/login":
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{"token": "jwt"})
|
||||
case "/download":
|
||||
if r.Header.Get("Authorization") != "Bearer jwt" {
|
||||
t.Errorf("download authorization = %q", r.Header.Get("Authorization"))
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"link": files.URL + "/f.srt", "file_name": "f.srt", "remaining": 19,
|
||||
})
|
||||
default:
|
||||
t.Errorf("unexpected path %s", r.URL.Path)
|
||||
}
|
||||
}))
|
||||
defer api.Close()
|
||||
files = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// The CDN is not the API: neither the key nor the token belongs on this request.
|
||||
if r.Header.Get("Api-Key") != "" || r.Header.Get("Authorization") != "" {
|
||||
t.Error("credentials were sent to the download host")
|
||||
}
|
||||
_, _ = w.Write([]byte("1\n00:00:01,000 --> 00:00:02,000\nhello\n"))
|
||||
}))
|
||||
defer files.Close()
|
||||
|
||||
client := New("key", "Memby/test", "someone", "secret", 5*time.Second)
|
||||
client.SetBaseURL(api.URL)
|
||||
name, content, err := client.Download(context.Background(), 42)
|
||||
if err != nil {
|
||||
t.Fatalf("Download: %v", err)
|
||||
}
|
||||
if name != "f.srt" || len(content) == 0 {
|
||||
t.Fatalf("Download returned %q / %d bytes", name, len(content))
|
||||
}
|
||||
}
|
||||
|
||||
// The allowance running out is the one failure a viewer can act on, so it must not be
|
||||
// flattened into "the provider did not answer".
|
||||
func TestDownloadReportsAnExhaustedQuota(t *testing.T) {
|
||||
api := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusNotAcceptable)
|
||||
_, _ = w.Write([]byte(`{"message":"quota exceeded"}`))
|
||||
}))
|
||||
defer api.Close()
|
||||
|
||||
client := New("key", "Memby/test", "", "", 5*time.Second)
|
||||
client.SetBaseURL(api.URL)
|
||||
_, _, err := client.Download(context.Background(), 7)
|
||||
if _, ok := err.(*QuotaError); !ok {
|
||||
t.Fatalf("Download error = %v, want a QuotaError", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginTokenIsFetchedOnce(t *testing.T) {
|
||||
logins := 0
|
||||
api := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/login" {
|
||||
logins++
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{"token": "jwt"})
|
||||
return
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"data": []any{}})
|
||||
}))
|
||||
defer api.Close()
|
||||
|
||||
client := New("key", "Memby/test", "someone", "secret", 5*time.Second)
|
||||
client.SetBaseURL(api.URL)
|
||||
for range 3 {
|
||||
if _, err := client.Search(context.Background(), Query{IMDBID: "tt1"}); err != nil {
|
||||
t.Fatalf("Search: %v", err)
|
||||
}
|
||||
}
|
||||
if logins != 1 {
|
||||
t.Fatalf("logged in %d times, want once", logins)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatFromNameFallsBackToSubRip(t *testing.T) {
|
||||
for name, want := range map[string]string{
|
||||
"a.srt": "srt", "b.VTT": "vtt", "c.ass": "ass", "d": "srt", "e.zip": "srt",
|
||||
} {
|
||||
if got := formatFromName(name); got != want {
|
||||
t.Errorf("formatFromName(%q) = %q, want %q", name, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user