Skip to content

Commit 39b0436

Browse files
authored
fix(search): use raw HTTP request in multi to avoid broken union-type unmarshal (#75)
1 parent e01aecf commit 39b0436

2 files changed

Lines changed: 102 additions & 5 deletions

File tree

cmd/search/multi.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package search
22

33
import (
4+
"net/url"
5+
"strconv"
6+
47
"seerr-cli/cmd/apiutil"
58

69
"github.com/spf13/cobra"
@@ -21,16 +24,28 @@ var multiCmd = &cobra.Command{
2124
page, _ := cmd.Flags().GetFloat32("page")
2225
language, _ := cmd.Flags().GetString("language")
2326

24-
req := apiClient.SearchAPI.SearchGet(ctx).Query(query)
27+
params := url.Values{}
28+
params.Set("query", query)
2529
if cmd.Flags().Changed("page") {
26-
req = req.Page(page)
30+
params.Set("page", strconv.Itoa(int(page)))
2731
}
2832
if cmd.Flags().Changed("language") {
29-
req = req.Language(language)
33+
params.Set("language", language)
34+
}
35+
36+
// Use a raw HTTP request to avoid the broken union-type unmarshal in the
37+
// generated client (TV results are incorrectly parsed as PersonResult) and
38+
// to ensure spaces are encoded as %20 rather than + in the query string.
39+
b, err := apiutil.RawGet(ctx, apiClient, "/search", params)
40+
if err != nil {
41+
return err
3042
}
3143

32-
res, r, err := req.Execute()
33-
return apiutil.HandleResponse(cmd, r, err, res, isVerbose, "SearchGet")
44+
if isVerbose {
45+
cmd.Printf("GET /api/v1/search\n")
46+
}
47+
cmd.Println(string(b))
48+
return nil
3449
},
3550
}
3651

tests/search_multi_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package tests
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"net/http"
7+
"net/http/httptest"
8+
"strings"
9+
"testing"
10+
11+
"seerr-cli/cmd"
12+
"seerr-cli/cmd/apiutil"
13+
14+
"github.com/spf13/viper"
15+
"github.com/stretchr/testify/assert"
16+
)
17+
18+
// TestSearchMultiMixedResults verifies that movie, TV, and person results are
19+
// all preserved correctly in the response — the generated client's broken
20+
// union-type unmarshal would lose TV results by misidentifying them as persons.
21+
func TestSearchMultiMixedResults(t *testing.T) {
22+
mixedResponse := `{"page":1,"totalPages":1,"totalResults":3,"results":[` +
23+
`{"id":1,"mediaType":"movie","title":"The Matrix"},` +
24+
`{"id":2,"mediaType":"tv","name":"Matrix Reloaded"},` +
25+
`{"id":3,"mediaType":"person","name":"Keanu Reeves"}` +
26+
`]}`
27+
28+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
29+
assert.Equal(t, "/api/v1/search", r.URL.Path)
30+
w.Header().Set("Content-Type", "application/json")
31+
w.WriteHeader(http.StatusOK)
32+
fmt.Fprint(w, mixedResponse)
33+
}))
34+
defer server.Close()
35+
36+
viper.Set("seerr.server", server.URL)
37+
viper.Set("seerr.api_key", "test-key")
38+
apiutil.OverrideServerURL = server.URL + "/api/v1"
39+
defer func() { apiutil.OverrideServerURL = "" }()
40+
41+
buf := new(bytes.Buffer)
42+
cmd.RootCmd.SetOut(buf)
43+
cmd.RootCmd.SetArgs([]string{"search", "multi", "-q", "Matrix"})
44+
45+
err := cmd.RootCmd.Execute()
46+
assert.NoError(t, err)
47+
48+
out := buf.String()
49+
assert.Contains(t, out, `"mediaType":"movie"`)
50+
assert.Contains(t, out, `"mediaType":"tv"`)
51+
assert.Contains(t, out, `"mediaType":"person"`)
52+
}
53+
54+
// TestSearchMultiQueryEncoding verifies that spaces in the query are encoded
55+
// as %20 and not + in the URL sent to the server.
56+
func TestSearchMultiQueryEncoding(t *testing.T) {
57+
var capturedQuery string
58+
59+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
60+
capturedQuery = r.URL.RawQuery
61+
w.Header().Set("Content-Type", "application/json")
62+
w.WriteHeader(http.StatusOK)
63+
fmt.Fprint(w, `{"page":1,"totalResults":0,"results":[]}`)
64+
}))
65+
defer server.Close()
66+
67+
viper.Set("seerr.server", server.URL)
68+
viper.Set("seerr.api_key", "test-key")
69+
apiutil.OverrideServerURL = server.URL + "/api/v1"
70+
defer func() { apiutil.OverrideServerURL = "" }()
71+
72+
buf := new(bytes.Buffer)
73+
cmd.RootCmd.SetOut(buf)
74+
cmd.RootCmd.SetArgs([]string{"search", "multi", "-q", "the matrix"})
75+
76+
err := cmd.RootCmd.Execute()
77+
assert.NoError(t, err)
78+
79+
// Spaces must be encoded as %20, not as +.
80+
assert.True(t, strings.Contains(capturedQuery, "%20"), "expected %%20 encoding in query %q", capturedQuery)
81+
assert.False(t, strings.Contains(capturedQuery, "+"), "unexpected + encoding in query %q", capturedQuery)
82+
}

0 commit comments

Comments
 (0)