package unfurl import ( "context" "net/http" "net/http/httptest" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestFetchKagiKite_Success(t *testing.T) { // Mock Kagi HTML response mockHTML := ` FAA orders 10% flight cuts at 40 airports - Kagi News Airport runway ` server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(mockHTML)) })) defer server.Close() ctx := context.Background() result, err := fetchKagiKite(ctx, server.URL, 5*time.Second, "TestBot/1.0") require.NoError(t, err) assert.Equal(t, "article", result.Type) assert.Equal(t, "FAA orders 10% flight cuts", result.Title) assert.Equal(t, "Flight restrictions announced", result.Description) assert.Contains(t, result.ThumbnailURL, "kagiproxy.com") assert.Equal(t, "kagi", result.Provider) assert.Equal(t, "kite.kagi.com", result.Domain) } func TestFetchKagiKite_NoImage(t *testing.T) { mockHTML := ` Test Story

No images here

` server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(mockHTML)) })) defer server.Close() ctx := context.Background() result, err := fetchKagiKite(ctx, server.URL, 5*time.Second, "TestBot/1.0") assert.Error(t, err) assert.Nil(t, result) assert.Contains(t, err.Error(), "no image found") } func TestFetchKagiKite_FallbackToTitle(t *testing.T) { mockHTML := ` Fallback Title ` server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(mockHTML)) })) defer server.Close() ctx := context.Background() result, err := fetchKagiKite(ctx, server.URL, 5*time.Second, "TestBot/1.0") require.NoError(t, err) assert.Equal(t, "Fallback Title", result.Title) assert.Contains(t, result.ThumbnailURL, "kagiproxy.com") } func TestFetchKagiKite_ImageWithAltText(t *testing.T) { mockHTML := ` News Story This is the alt text description ` server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(mockHTML)) })) defer server.Close() ctx := context.Background() result, err := fetchKagiKite(ctx, server.URL, 5*time.Second, "TestBot/1.0") require.NoError(t, err) assert.Equal(t, "News Story", result.Title) assert.Equal(t, "This is the alt text description", result.Description) assert.Contains(t, result.ThumbnailURL, "kagiproxy.com") } func TestFetchKagiKite_HTTPError(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) })) defer server.Close() ctx := context.Background() result, err := fetchKagiKite(ctx, server.URL, 5*time.Second, "TestBot/1.0") assert.Error(t, err) assert.Nil(t, result) assert.Contains(t, err.Error(), "HTTP 404") } func TestFetchKagiKite_Timeout(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { time.Sleep(2 * time.Second) w.WriteHeader(http.StatusOK) })) defer server.Close() ctx := context.Background() result, err := fetchKagiKite(ctx, server.URL, 100*time.Millisecond, "TestBot/1.0") assert.Error(t, err) assert.Nil(t, result) } func TestFetchKagiKite_MultipleImages_PicksSecond(t *testing.T) { mockHTML := ` Story with multiple images First image (header/logo) Second image ` server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(mockHTML)) })) defer server.Close() ctx := context.Background() result, err := fetchKagiKite(ctx, server.URL, 5*time.Second, "TestBot/1.0") require.NoError(t, err) // We skip the first image (often a header/logo) and use the second assert.Contains(t, result.ThumbnailURL, "second456") assert.Equal(t, "Second image", result.Description) } func TestFetchKagiKite_OnlyNonKagiImages_NoMatch(t *testing.T) { mockHTML := ` Story with non-Kagi images External image ` server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(mockHTML)) })) defer server.Close() ctx := context.Background() result, err := fetchKagiKite(ctx, server.URL, 5*time.Second, "TestBot/1.0") assert.Error(t, err) assert.Nil(t, result) assert.Contains(t, err.Error(), "no image found") }