package wallpaper import ( "context" "os" "testing" "time" "service.xpcool.com/internal/model/dto" ) // 这一组测试会**真实访问各平台接口**,用于验证 JSON 路径解析与尺寸字段映射。 // // 平台接口的字段名一旦变化,单元测试(打桩)是发现不了的,只有真连一次才知道。 // 因此保留它们,但默认跳过,只在需要时手动开启: // // WALLPAPER_LIVE=1 go test ./internal/service/wallpaper/ -run TestLive -v // // 需要 Key 的平台会自动跳过(除非另外提供了环境变量)。 func liveEnabled(t *testing.T) { t.Helper() if os.Getenv("WALLPAPER_LIVE") != "1" { t.Skip("跳过联网测试:设置 WALLPAPER_LIVE=1 后运行") } } // runLive 拉一页并检查基本约束。 func runLive(t *testing.T, p Provider, cfg dto.WallpaperSourceConfig, q dto.WallpaperQuery) []dto.WallpaperItem { t.Helper() ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() items, _, err := p.List(ctx, cfg, q) if err != nil { t.Fatalf("%s 拉取失败: %v", p.Code(), err) } if len(items) == 0 { t.Fatalf("%s 未返回任何条目", p.Code()) } for i, it := range items { if it.ThumbUrl == "" || it.FullUrl == "" { t.Errorf("%s 第 %d 条缺少图片地址: %+v", p.Code(), i, it) } if it.Width <= 0 || it.Height <= 0 { t.Errorf("%s 第 %d 条尺寸异常: %dx%d", p.Code(), i, it.Width, it.Height) } if it.Orientation != orientationOf(it.Width, it.Height) { t.Errorf("%s 第 %d 条朝向与尺寸不一致", p.Code(), i) } } t.Logf("%s 取到 %d 条,示例:%s | 缩略 %s", p.Code(), len(items), items[0].Title, items[0].ThumbUrl) return items } func TestLiveBing(t *testing.T) { liveEnabled(t) runLive(t, &BingProvider{}, dto.WallpaperSourceConfig{}, dto.WallpaperQuery{Page: 1, Size: 8}) } func TestLivePicsum(t *testing.T) { liveEnabled(t) items := runLive(t, &PicsumProvider{}, dto.WallpaperSourceConfig{}, dto.WallpaperQuery{Page: 1, Size: 6}) // 顺带验证朝向过滤确实生效(Picsum 没有服务端筛选,全靠本地过滤) portrait := runLive(t, &PicsumProvider{}, dto.WallpaperSourceConfig{}, dto.WallpaperQuery{Page: 1, Size: 3, Orientation: 2}) for i, it := range portrait { if it.Width >= it.Height { t.Errorf("竖版筛选失效:第 %d 条是 %dx%d", i, it.Width, it.Height) } } _ = items } func TestLiveUnsplash(t *testing.T) { liveEnabled(t) key := os.Getenv("UNSPLASH_ACCESS_KEY") if key == "" { t.Skip("未提供 UNSPLASH_ACCESS_KEY,跳过") } runLive(t, &UnsplashProvider{}, dto.WallpaperSourceConfig{ApiKey: key}, dto.WallpaperQuery{Page: 1, Size: 5, Query: "mountain"}) } func TestLivePexels(t *testing.T) { liveEnabled(t) key := os.Getenv("PEXELS_API_KEY") if key == "" { t.Skip("未提供 PEXELS_API_KEY,跳过") } runLive(t, &PexelsProvider{}, dto.WallpaperSourceConfig{ApiKey: key}, dto.WallpaperQuery{Page: 1, Size: 5, Query: "mountain"}) } func TestLiveWallhaven(t *testing.T) { liveEnabled(t) // Wallhaven 的 SFW 检索无需 Key,故这里不带凭据也应能跑通 items := runLive(t, &WallhavenProvider{}, dto.WallpaperSourceConfig{}, dto.WallpaperQuery{Page: 1, Size: 5}) for i := range items { if items[i].FullUrl == "" { t.Errorf("第 %d 条缺少原图地址", i) } } } // TestLiveBingCopyrightSplit 校验版权串的拆分(纯函数,不联网也跑)。 func TestLiveBingCopyrightSplit(t *testing.T) { cases := []struct { in string wantTitle string wantAuthor string }{ {"冰岛某瀑布 (© 张三/Getty Images)", "冰岛某瀑布", "张三/Getty Images"}, {"没有括号的版权串", "没有括号的版权串", ""}, {"", "", ""}, } for _, c := range cases { gotTitle, gotAuthor := splitBingCopyright(c.in) if gotTitle != c.wantTitle || gotAuthor != c.wantAuthor { t.Errorf("splitBingCopyright(%q) = (%q, %q),期望 (%q, %q)", c.in, gotTitle, gotAuthor, c.wantTitle, c.wantAuthor) } } }