package web import ( "testing" "axenov/iptv-checker/app/playlist" ) func TestHasTokensString(t *testing.T) { cases := []struct { in string want bool }{ {"http://example.com/stream.m3u8?token=abc", true}, {"http://example.com/stream?user=test&pwd=secret", true}, {"http://example.com/stream?drmreq=xyz", true}, {"http://example.com/stream?u=foo", true}, {"http://example.com/stream?password=123", true}, {"http://example.com/stream.m3u8", false}, {"", false}, {"plain text without tokens", false}, } for _, c := range cases { got := hasTokensString(c.in) if got != c.want { t.Errorf("hasTokensString(%q) = %v, want %v", c.in, got, c.want) } } } func TestStatusClass(t *testing.T) { cases := []struct { view *PlaylistView want string }{ {&PlaylistView{IsKnown: false}, "secondary"}, {&PlaylistView{IsKnown: true, Playlist: playlist.Playlist{IsOnline: true}}, "success"}, {&PlaylistView{IsKnown: true, Playlist: playlist.Playlist{IsOnline: false}}, "danger"}, } for _, c := range cases { got := statusClass(c.view) if got != c.want { t.Errorf("statusClass() = %s, want %s", got, c.want) } } } func TestStatusBadge(t *testing.T) { cases := []struct { view *PlaylistView want string }{ {&PlaylistView{IsKnown: false}, "unknown"}, {&PlaylistView{IsKnown: true, Playlist: playlist.Playlist{IsOnline: true}}, "online"}, {&PlaylistView{IsKnown: true, Playlist: playlist.Playlist{IsOnline: false}}, "offline"}, } for _, c := range cases { got := statusBadge(c.view) if got != c.want { t.Errorf("statusBadge() = %s, want %s", got, c.want) } } } func TestFormatDate(t *testing.T) { if formatDate(0) != "" { t.Error("expected empty string for ts=0") } result := formatDate(1700000000) if result == "" { t.Error("expected non-empty date string") } } func TestIsAlphanumeric(t *testing.T) { cases := []struct { in string want bool }{ {"ABC123", true}, {"abc", true}, {"123", true}, {"", false}, {"ABC-123", false}, {"ABC_123", false}, {"ABC 123", false}, {"Привет", false}, } for _, c := range cases { got := isAlphanumeric(c.in) if got != c.want { t.Errorf("isAlphanumeric(%q) = %v, want %v", c.in, got, c.want) } } } func TestParseRedisVersion(t *testing.T) { info := "# Server\nredis_version:6.2.6\nredis_mode:standalone\n" got := parseRedisVersion(info) if got != "6.2.6" { t.Errorf("expected '6.2.6', got '%s'", got) } } func TestParseRedisVersion_NotFound(t *testing.T) { info := "# Server\nsome_other_field:foo\n" got := parseRedisVersion(info) if got != "unknown" { t.Errorf("expected 'unknown', got '%s'", got) } } func TestContainsTag(t *testing.T) { tags := []string{"hd", "adult", "russian"} if !containsTag(tags, "adult") { t.Error("expected true for 'adult'") } if containsTag(tags, "movies") { t.Error("expected false for 'movies'") } if containsTag([]string{}, "x") { t.Error("expected false for empty list") } } func TestMakeUnknownView(t *testing.T) { iniPls := playlist.Playlist{ Name: "Test", Description: "Desc", Url: "http://example.com", Source: "public", } view := makeUnknownView("CODE", iniPls) if view.Code != "CODE" { t.Errorf("expected Code='CODE', got '%s'", view.Code) } if view.IsKnown { t.Error("expected IsKnown=false") } if view.Name != "Test" { t.Errorf("expected Name='Test', got '%s'", view.Name) } if len(view.Tags) != 0 { t.Errorf("expected 0 tags, got %d", len(view.Tags)) } } func TestEnrichPlaylist_OnlinePercent(t *testing.T) { view := &PlaylistView{ Playlist: playlist.Playlist{ IsOnline: true, OnlineCount: 8, OfflineCount: 2, Channels: map[string]playlist.Channel{ "1": {}, "2": {}, "3": {}, "4": {}, "5": {}, "6": {}, "7": {}, "8": {}, "9": {}, "10": {}, }, Attributes: map[string]string{}, Groups: map[string]playlist.Group{}, Content: "#EXTM3U", }, IsKnown: true, } enrichPlaylist(view) if view.OnlinePercent != 80 { t.Errorf("expected 80%%, got %d%%", view.OnlinePercent) } if view.OfflinePercent != 20 { t.Errorf("expected 20%%, got %d%%", view.OfflinePercent) } } func TestEnrichPlaylist_Unknown(t *testing.T) { view := &PlaylistView{ Playlist: playlist.Playlist{ Channels: map[string]playlist.Channel{"1": {}}, Attributes: map[string]string{}, Groups: map[string]playlist.Group{}, Content: "", }, IsKnown: false, } enrichPlaylist(view) if view.OnlinePercent != 0 || view.OfflinePercent != 0 { t.Errorf("expected 0%% for unknown, got online=%d%% offline=%d%%", view.OnlinePercent, view.OfflinePercent) } } func TestEnrichPlaylist_HasCatchup(t *testing.T) { view := &PlaylistView{ Playlist: playlist.Playlist{ Content: "#EXTM3U catchup=default", Channels: map[string]playlist.Channel{}, Attributes: map[string]string{}, Groups: map[string]playlist.Group{}, }, IsKnown: true, } enrichPlaylist(view) if !view.HasCatchup { t.Error("expected HasCatchup=true") } } func TestEnrichPlaylist_HasTvg(t *testing.T) { view := &PlaylistView{ Playlist: playlist.Playlist{ Attributes: map[string]string{"url-tvg": "http://example.com/epg.xml"}, Channels: map[string]playlist.Channel{}, Groups: map[string]playlist.Group{}, Content: "", }, IsKnown: true, } enrichPlaylist(view) if !view.HasTvg { t.Error("expected HasTvg=true") } } func TestEnrichPlaylist_HasTokens(t *testing.T) { view := &PlaylistView{ Playlist: playlist.Playlist{ Url: "http://example.com/list.m3u8?token=abc", Content: "#EXTM3U", Channels: map[string]playlist.Channel{}, Attributes: map[string]string{}, Groups: map[string]playlist.Group{}, }, IsKnown: true, } enrichPlaylist(view) if !view.HasTokens { t.Error("expected HasTokens=true") } } func TestEnrichPlaylist_TagsCollection(t *testing.T) { view := &PlaylistView{ Playlist: playlist.Playlist{ Channels: map[string]playlist.Channel{ "1": {Tags: []string{"hd", "russian"}}, "2": {Tags: []string{"adult", "hd"}}, }, Attributes: map[string]string{}, Groups: map[string]playlist.Group{}, Content: "", }, IsKnown: true, } enrichPlaylist(view) if len(view.Tags) != 3 { t.Errorf("expected 3 unique tags, got %d: %v", len(view.Tags), view.Tags) } } func TestEnrichPlaylist_ChannelHasToken(t *testing.T) { view := &PlaylistView{ Playlist: playlist.Playlist{ Channels: map[string]playlist.Channel{ "1": {URL: "http://example.com/stream?token=abc"}, }, Attributes: map[string]string{}, Groups: map[string]playlist.Group{}, Content: "", }, IsKnown: true, } enrichPlaylist(view) for _, cv := range view.ViewChannels { if !cv.HasToken { t.Error("expected channel HasToken=true") } } } func TestComputeStats(t *testing.T) { playlists := []PlaylistView{ { Playlist: playlist.Playlist{ Code: "RU", IsOnline: true, CheckedAt: 1700000000, Channels: map[string]playlist.Channel{"1": {IsOnline: true}, "2": {IsOnline: false}}, Groups: map[string]playlist.Group{"g1": {}}, Attributes: map[string]string{"url-tvg": "http://epg.xml"}, Content: "#EXTM3U catchup=default", }, IsKnown: true, HasCatchup: true, HasTvg: true, Tags: []string{"adult"}, }, { Playlist: playlist.Playlist{ Code: "EN", IsOnline: false, CheckedAt: 1700000100, Channels: map[string]playlist.Channel{"1": {IsOnline: false}}, }, IsKnown: false, }, } stats := computeStats(playlists) plsStats := stats["playlists"].(map[string]interface{}) if plsStats["all"].(int) != 2 { t.Errorf("expected 2 playlists, got %d", plsStats["all"]) } if plsStats["online"].(int) != 1 { t.Errorf("expected 1 online, got %d", plsStats["online"]) } if plsStats["unknown"].(int) != 1 { t.Errorf("expected 1 unknown, got %d", plsStats["unknown"]) } if plsStats["adult"].(int) != 1 { t.Errorf("expected 1 adult, got %d", plsStats["adult"]) } if plsStats["hasCatchup"].(int) != 1 { t.Errorf("expected 1 catchup, got %d", plsStats["hasCatchup"]) } if plsStats["hasTvg"].(int) != 1 { t.Errorf("expected 1 tvg, got %d", plsStats["hasTvg"]) } if plsStats["groupped"].(int) != 1 { t.Errorf("expected 1 groupped, got %d", plsStats["groupped"]) } chStats := stats["channels"].(map[string]interface{}) if chStats["all"].(int) != 3 { t.Errorf("expected 3 channels, got %d", chStats["all"]) } if chStats["online"].(int) != 1 { t.Errorf("expected 1 online channel, got %d", chStats["online"]) } if chStats["offline"].(int) != 2 { t.Errorf("expected 2 offline channels, got %d", chStats["offline"]) } latest := plsStats["latest"].(map[string]interface{}) if latest["code"] != "EN" { t.Errorf("expected latest code 'EN', got '%v'", latest["code"]) } } func TestComputeStats_Empty(t *testing.T) { stats := computeStats([]PlaylistView{}) plsStats := stats["playlists"].(map[string]interface{}) if plsStats["all"].(int) != 0 { t.Errorf("expected 0, got %d", plsStats["all"]) } }