1 files changed,
161 insertions(+),
0 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-05-05 14:52:52 +0300
Authored at:
2026-05-05 14:35:50 +0300
Change ID:
rypslmqpwzyxxltrtlsnmmlttpqtpqxw
Parent:
63e22ef
A
sources/moviefeed/moviefeed_test.go
ยทยทยท 1 +package moviefeed 2 + 3 +import ( 4 + "encoding/xml" 5 + "fmt" 6 + "net/http" 7 + "net/http/httptest" 8 + "net/url" 9 + "strings" 10 + "testing" 11 + "time" 12 + 13 + "olexsmir.xyz/rss-tools/app" 14 + "olexsmir.xyz/x/is" 15 +) 16 + 17 +func TestHandleMoviesRendersFeedForRequestedID(t *testing.T) { 18 + server, client := newTMDBStub(t) 19 + defer server.Close() 20 + 21 + mf := &moviefeed{ 22 + api: NewTMDBAPI("test-key", client), 23 + shows: nil, 24 + } 25 + 26 + mux := http.NewServeMux() 27 + mux.HandleFunc("GET /movies/{rest...}", mf.handleMovies) 28 + 29 + req := httptest.NewRequest(http.MethodGet, "/movies/tt123", nil) 30 + rr := httptest.NewRecorder() 31 + mux.ServeHTTP(rr, req) 32 + 33 + is.Equal(t, rr.Code, http.StatusOK) 34 + if got := rr.Header().Get("Content-Type"); !strings.Contains(got, "application/atom+xml") { 35 + t.Fatalf("expected atom response content-type, got %q", got) 36 + } 37 + 38 + var feed app.AtomFeed 39 + is.Err(t, xml.NewDecoder(rr.Body).Decode(&feed), nil) 40 + is.Equal(t, feed.Title, "Episodes from tt123") 41 + is.Equal(t, feed.ID, "moviefeed-tt123") 42 + is.Equal(t, len(feed.Entries), 3) // two episodes + one image entry 43 + is.Equal(t, strings.Contains(feed.Entries[0].Title, "S1E2"), true) // newest first 44 + is.Equal(t, feed.Entries[2].ID, "tmdb-101-s1e1-img") 45 +} 46 + 47 +func TestHandleMoviesUsesConfiguredShowsForEmptyPath(t *testing.T) { 48 + server, client := newTMDBStub(t) 49 + defer server.Close() 50 + 51 + mf := &moviefeed{ 52 + api: NewTMDBAPI("test-key", client), 53 + shows: []string{"tt123"}, 54 + } 55 + 56 + mux := http.NewServeMux() 57 + mux.HandleFunc("GET /movies/{rest...}", mf.handleMovies) 58 + 59 + req := httptest.NewRequest(http.MethodGet, "/movies/", nil) 60 + rr := httptest.NewRecorder() 61 + mux.ServeHTTP(rr, req) 62 + 63 + is.Equal(t, rr.Code, http.StatusOK) 64 + 65 + var feed app.AtomFeed 66 + is.Err(t, xml.NewDecoder(rr.Body).Decode(&feed), nil) 67 + is.Equal(t, feed.ID, "moviefeed-tt123") 68 +} 69 + 70 +func TestHandleMoviesReturnsBadRequestWhenNoIDs(t *testing.T) { 71 + mf := &moviefeed{ 72 + api: NewTMDBAPI("test-key", http.DefaultClient), 73 + shows: nil, 74 + } 75 + 76 + mux := http.NewServeMux() 77 + mux.HandleFunc("GET /movies/{rest...}", mf.handleMovies) 78 + 79 + req := httptest.NewRequest(http.MethodGet, "/movies/", nil) 80 + rr := httptest.NewRecorder() 81 + mux.ServeHTTP(rr, req) 82 + 83 + is.Equal(t, rr.Code, http.StatusBadRequest) 84 + is.Equal(t, strings.Contains(rr.Body.String(), "no movie IDs provided"), true) 85 +} 86 + 87 +func TestFetchEpisodesForShowFiltersRecentAndMapsFields(t *testing.T) { 88 + server, client := newTMDBStub(t) 89 + defer server.Close() 90 + 91 + api := NewTMDBAPI("test-key", client) 92 + episodes, err := api.FetchEpisodesForShow("tt123") 93 + is.Err(t, err, nil) 94 + 95 + is.Equal(t, len(episodes), 2) // old episode is filtered out 96 + is.Equal(t, episodes[0].ShowID, "101") 97 + is.Equal(t, episodes[0].ShowName, "Test Show") 98 +} 99 + 100 +func newTMDBStub(t *testing.T) (*httptest.Server, *http.Client) { 101 + t.Helper() 102 + 103 + recentDay := time.Now().AddDate(0, 0, -7).Format(dateFormat) 104 + newestDay := time.Now().AddDate(0, 0, -1).Format(dateFormat) 105 + oldDay := time.Now().AddDate(0, 0, -45).Format(dateFormat) 106 + 107 + mux := http.NewServeMux() 108 + mux.HandleFunc("/3/find/tt123", func(w http.ResponseWriter, r *http.Request) { 109 + if got := r.URL.Query().Get("external_source"); got != "imdb_id" { 110 + t.Fatalf("unexpected external_source query: %q", got) 111 + } 112 + if got := r.URL.Query().Get("api_key"); got != "test-key" { 113 + t.Fatalf("unexpected api_key query: %q", got) 114 + } 115 + _, _ = w.Write([]byte(`{"tv_results":[{"id":101}]}`)) 116 + }) 117 + 118 + mux.HandleFunc("/3/tv/101", func(w http.ResponseWriter, r *http.Request) { 119 + if got := r.URL.Query().Get("api_key"); got != "test-key" { 120 + t.Fatalf("unexpected api_key query: %q", got) 121 + } 122 + _, _ = w.Write([]byte(`{"id":101,"name":"Test Show","number_of_seasons":1}`)) 123 + }) 124 + 125 + mux.HandleFunc("/3/tv/101/season/1", func(w http.ResponseWriter, r *http.Request) { 126 + if got := r.URL.Query().Get("api_key"); got != "test-key" { 127 + t.Fatalf("unexpected api_key query: %q", got) 128 + } 129 + body := fmt.Sprintf(`{ 130 + "episodes": [ 131 + {"id": 1001, "name": "Episode 1", "overview": "E1", "air_date": %q, "episode_number": 1, "season_number": 1, "still_path": "/e1.jpg"}, 132 + {"id": 1002, "name": "Episode 2", "overview": "E2", "air_date": %q, "episode_number": 2, "season_number": 1, "still_path": ""}, 133 + {"id": 1003, "name": "Episode old", "overview": "old", "air_date": %q, "episode_number": 3, "season_number": 1, "still_path": ""} 134 + ] 135 + }`, recentDay, newestDay, oldDay) 136 + _, _ = w.Write([]byte(body)) 137 + }) 138 + 139 + server := httptest.NewServer(mux) 140 + target, err := url.Parse(server.URL) 141 + is.Err(t, err, nil) 142 + 143 + client := &http.Client{ 144 + Transport: rewriteTransport{target: target}, 145 + } 146 + return server, client 147 +} 148 + 149 +type rewriteTransport struct { 150 + target *url.URL 151 +} 152 + 153 +func (t rewriteTransport) RoundTrip(req *http.Request) (*http.Response, error) { 154 + clone := req.Clone(req.Context()) 155 + copiedURL := *clone.URL 156 + copiedURL.Scheme = t.target.Scheme 157 + copiedURL.Host = t.target.Host 158 + clone.URL = &copiedURL 159 + clone.Host = t.target.Host 160 + return http.DefaultTransport.RoundTrip(clone) 161 +}