package search import ( "encoding/json" "net/http" "net/http/httptest" "testing" "time" ) func TestSearchPodcasts_EmptyTerm(t *testing.T) { // 创建iTunes搜索提供者 provider := NewITunesProvider() // 使用空搜索词调用SearchPodcasts result, err := provider.SearchPodcasts("", nil) // 验证返回错误 if err == nil { t.Error("期望返回错误,但没有") } // 验证错误消息 expectedErrMsg := "搜索词不能为空" if err.Error() != expectedErrMsg { t.Errorf("期望错误消息为 %q,但得到 %q", expectedErrMsg, err.Error()) } // 验证结果为nil if result != nil { t.Errorf("期望结果为nil,但得到 %+v", result) } } func TestSearchPodcasts_SuccessfulSearch(t *testing.T) { // 创建测试服务器 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 验证请求方法 if r.Method != http.MethodGet { t.Errorf("期望请求方法为GET,但得到 %s", r.Method) } // 验证请求路径 if r.URL.Path != "/search" { t.Errorf("期望请求路径为/search,但得到 %s", r.URL.Path) } // 验证查询参数 query := r.URL.Query() if query.Get("term") != "golang" { t.Errorf("期望term参数为golang,但得到 %s", query.Get("term")) } if query.Get("entity") != "podcast" { t.Errorf("期望entity参数为podcast,但得到 %s", query.Get("entity")) } if query.Get("media") != "podcast" { t.Errorf("期望media参数为podcast,但得到 %s", query.Get("media")) } // 返回模拟响应 w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) // 创建模拟响应数据 response := map[string]interface{}{ "resultCount": 2, "results": []map[string]interface{}{ { "collectionId": 1234567890, "collectionName": "Go编程语言", "artistName": "Go开发者", "feedUrl": "https://example.com/feed1", "artworkUrl100": "https://example.com/artwork1_100.jpg", "artworkUrl600": "https://example.com/artwork1_600.jpg", "genres": []string{"Technology", "Education"}, }, { "collectionId": 9876543210, "collectionName": "Golang讨论", "artistName": "Golang爱好者", "feedUrl": "https://example.com/feed2", "artworkUrl100": "https://example.com/artwork2_100.jpg", "artworkUrl600": "https://example.com/artwork2_600.jpg", "genres": []string{"Technology"}, }, }, } // 将响应数据编码为JSON并写入响应 json.NewEncoder(w).Encode(response) })) defer server.Close() // 创建iTunes搜索提供者,并设置测试服务器URL provider := &ITunesProvider{ SearchAPIEndpoint: server.URL, FeedAPIEndpoint: "https://itunes.apple.com", GenresMap: initITunesGenres(), Client: &http.Client{Timeout: 20 * time.Second}, } // 调用SearchPodcasts result, err := provider.SearchPodcasts("golang", nil) // 验证没有错误 if err != nil { t.Fatalf("期望没有错误,但得到 %v", err) } // 验证结果 if result == nil { t.Fatal("期望结果不为nil,但得到nil") } // 验证结果计数 if result.ResultCount != 2 { t.Errorf("期望结果计数为2,但得到 %d", result.ResultCount) } // 验证结果成功标志 if !result.Successful { t.Error("期望结果成功标志为true,但得到false") } // 验证结果项目数量 if len(result.Items) != 2 { t.Errorf("期望结果项目数量为2,但得到 %d", len(result.Items)) } // 验证第一个结果项目 item1 := result.Items[0] if item1.CollectionID != 1234567890 { t.Errorf("期望第一个项目的CollectionID为1234567890,但得到 %d", item1.CollectionID) } if item1.Title != "Go编程语言" { t.Errorf("期望第一个项目的Title为'Go编程语言',但得到 %s", item1.Title) } if item1.Author != "Go开发者" { t.Errorf("期望第一个项目的Author为'Go开发者',但得到 %s", item1.Author) } if item1.FeedURL != "https://example.com/feed1" { t.Errorf("期望第一个项目的FeedURL为'https://example.com/feed1',但得到 %s", item1.FeedURL) } if item1.ArtworkURL != "https://example.com/artwork1_100.jpg" { t.Errorf("期望第一个项目的ArtworkURL为'https://example.com/artwork1_100.jpg',但得到 %s", item1.ArtworkURL) } if item1.ArtworkURL600 != "https://example.com/artwork1_600.jpg" { t.Errorf("期望第一个项目的ArtworkURL600为'https://example.com/artwork1_600.jpg',但得到 %s", item1.ArtworkURL600) } if len(item1.Genres) != 2 || item1.Genres[0] != "Technology" || item1.Genres[1] != "Education" { t.Errorf("期望第一个项目的Genres为[Technology, Education],但得到 %v", item1.Genres) } // 验证第二个结果项目 item2 := result.Items[1] if item2.CollectionID != 9876543210 { t.Errorf("期望第二个项目的CollectionID为9876543210,但得到 %d", item2.CollectionID) } if item2.Title != "Golang讨论" { t.Errorf("期望第二个项目的Title为'Golang讨论',但得到 %s", item2.Title) } } func TestSearchPodcasts_WithOptions(t *testing.T) { // 创建测试服务器 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 验证查询参数 query := r.URL.Query() // 验证基本参数 if query.Get("term") != "golang" { t.Errorf("期望term参数为golang,但得到 %s", query.Get("term")) } // 验证选项参数 if query.Get("country") != "cn" { t.Errorf("期望country参数为cn,但得到 %s", query.Get("country")) } if query.Get("genreId") != "1318" { // Technology的ID t.Errorf("期望genreId参数为1318,但得到 %s", query.Get("genreId")) } if query.Get("attribute") != "titleTerm" { t.Errorf("期望attribute参数为titleTerm,但得到 %s", query.Get("attribute")) } if query.Get("limit") != "10" { t.Errorf("期望limit参数为10,但得到 %s", query.Get("limit")) } if query.Get("language") != "zh-cn" { t.Errorf("期望language参数为zh-cn,但得到 %s", query.Get("language")) } if query.Get("explicit") != "No" { t.Errorf("期望explicit参数为No,但得到 %s", query.Get("explicit")) } // 返回模拟响应 w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) // 创建模拟响应数据 response := map[string]interface{}{ "resultCount": 1, "results": []map[string]interface{}{ { "collectionId": 1234567890, "collectionName": "Go编程语言", "artistName": "Go开发者", "feedUrl": "https://example.com/feed1", "artworkUrl100": "https://example.com/artwork1_100.jpg", "artworkUrl600": "https://example.com/artwork1_600.jpg", "genres": []string{"Technology"}, }, }, } // 将响应数据编码为JSON并写入响应 json.NewEncoder(w).Encode(response) })) defer server.Close() // 创建iTunes搜索提供者,并设置测试服务器URL provider := &ITunesProvider{ SearchAPIEndpoint: server.URL, FeedAPIEndpoint: "https://itunes.apple.com", GenresMap: initITunesGenres(), Client: &http.Client{Timeout: 20 * time.Second}, } // 创建搜索选项 options := &SearchOptions{ Country: "cn", Genre: "Technology", Attribute: "titleTerm", Limit: 10, Language: "zh-cn", Explicit: false, } // 调用SearchPodcasts result, err := provider.SearchPodcasts("golang", options) // 验证没有错误 if err != nil { t.Fatalf("期望没有错误,但得到 %v", err) } // 验证结果 if result == nil { t.Fatal("期望结果不为nil,但得到nil") } // 验证结果计数 if result.ResultCount != 1 { t.Errorf("期望结果计数为1,但得到 %d", result.ResultCount) } // 验证结果项目数量 if len(result.Items) != 1 { t.Errorf("期望结果项目数量为1,但得到 %d", len(result.Items)) } } func TestSearchPodcasts_APIError(t *testing.T) { // 创建测试服务器,返回错误状态码 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) })) defer server.Close() // 创建iTunes搜索提供者,并设置测试服务器URL provider := &ITunesProvider{ SearchAPIEndpoint: server.URL, FeedAPIEndpoint: "https://itunes.apple.com", GenresMap: initITunesGenres(), Client: &http.Client{Timeout: 20 * time.Second}, } // 调用SearchPodcasts result, err := provider.SearchPodcasts("golang", nil) // 验证返回错误 if err == nil { t.Error("期望返回错误,但没有") } // 验证错误消息包含状态码 expectedErrMsg := "API请求失败,状态码: 500" if err.Error() != expectedErrMsg { t.Errorf("期望错误消息包含 %q,但得到 %q", expectedErrMsg, err.Error()) } // 验证结果为nil if result != nil { t.Errorf("期望结果为nil,但得到 %+v", result) } } func TestSearchPodcasts_InvalidJSON(t *testing.T) { // 创建测试服务器,返回无效的JSON server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(`{"resultCount": 1, "results": [invalid json]`)) })) defer server.Close() // 创建iTunes搜索提供者,并设置测试服务器URL provider := &ITunesProvider{ SearchAPIEndpoint: server.URL, FeedAPIEndpoint: "https://itunes.apple.com", GenresMap: initITunesGenres(), Client: &http.Client{Timeout: 20 * time.Second}, } // 调用SearchPodcasts result, err := provider.SearchPodcasts("golang", nil) // 验证返回错误 if err == nil { t.Error("期望返回错误,但没有") } // 验证错误消息包含解析失败 if err != nil && err.Error() != "解析响应失败: invalid character 'i' looking for beginning of value" { t.Errorf("期望错误消息包含解析失败,但得到 %q", err.Error()) } // 验证结果为nil if result != nil { t.Errorf("期望结果为nil,但得到 %+v", result) } }