package rtcclient_test import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/qy/hw-ws-service/internal/rtcclient" ) func successBody(title, audioURL string) []byte { b, _ := json.Marshal(map[string]any{ "code": 0, "message": "success", "data": map[string]string{ "title": title, "audio_url": audioURL, }, }) return b } func errorBody(code int, msg string) []byte { b, _ := json.Marshal(map[string]any{ "code": code, "message": msg, "data": nil, }) return b } func TestFetchStoryByMAC_Success(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/api/v1/devices/stories/" { t.Errorf("unexpected path: %s", r.URL.Path) } w.Header().Set("Content-Type", "application/json") w.Write(successBody("小红帽", "https://example.com/story.mp3")) })) defer srv.Close() client := rtcclient.New(srv.URL) story, err := client.FetchStoryByMAC(context.Background(), "aa:bb:cc:dd:ee:ff") if err != nil { t.Fatalf("unexpected error: %v", err) } if story == nil { t.Fatal("expected story, got nil") } if story.Title != "小红帽" { t.Errorf("title = %q, want %q", story.Title, "小红帽") } if story.AudioURL != "https://example.com/story.mp3" { t.Errorf("audio_url = %q", story.AudioURL) } } // TestFetchStoryByMAC_MACUppercase verifies the client always sends uppercase MAC. func TestFetchStoryByMAC_MACUppercase(t *testing.T) { var gotMAC string srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotMAC = r.URL.Query().Get("mac_address") w.Header().Set("Content-Type", "application/json") w.Write(successBody("test", "https://example.com/t.mp3")) })) defer srv.Close() client := rtcclient.New(srv.URL) client.FetchStoryByMAC(context.Background(), "aa:bb:cc:dd:ee:ff") //nolint:errcheck if gotMAC != "AA:BB:CC:DD:EE:FF" { t.Errorf("MAC not uppercased: got %q, want %q", gotMAC, "AA:BB:CC:DD:EE:FF") } } // TestFetchStoryByMAC_NotFound verifies that HTTP 404 returns (nil, nil). func TestFetchStoryByMAC_NotFound(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) })) defer srv.Close() client := rtcclient.New(srv.URL) story, err := client.FetchStoryByMAC(context.Background(), "AA:BB:CC:DD:EE:FF") if err != nil { t.Fatalf("unexpected error for 404: %v", err) } if story != nil { t.Errorf("expected nil story for 404, got %+v", story) } } // TestFetchStoryByMAC_BusinessError verifies that code != 0 returns (nil, nil). func TestFetchStoryByMAC_BusinessError(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write(errorBody(404, "暂无可播放的故事")) })) defer srv.Close() client := rtcclient.New(srv.URL) story, err := client.FetchStoryByMAC(context.Background(), "AA:BB:CC:DD:EE:FF") if err != nil { t.Fatalf("unexpected error for business error response: %v", err) } if story != nil { t.Errorf("expected nil story for business error, got %+v", story) } } // TestFetchStoryByMAC_ServerError verifies that HTTP 5xx returns an error. func TestFetchStoryByMAC_ServerError(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) })) defer srv.Close() client := rtcclient.New(srv.URL) _, err := client.FetchStoryByMAC(context.Background(), "AA:BB:CC:DD:EE:FF") if err == nil { t.Error("expected error for HTTP 500, got nil") } } // TestFetchStoryByMAC_ContextCanceled verifies that a canceled context returns an error. func TestFetchStoryByMAC_ContextCanceled(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Never respond — let the client time out <-r.Context().Done() })) defer srv.Close() ctx, cancel := context.WithCancel(context.Background()) cancel() // cancel immediately client := rtcclient.New(srv.URL) _, err := client.FetchStoryByMAC(ctx, "AA:BB:CC:DD:EE:FF") if err == nil { t.Error("expected error for canceled context, got nil") } }