package main import ( "bytes" "reflect" "strings" "testing" ) func TestProcessPlaylist_AppendsURLTvg(t *testing.T) { in := "#EXTM3U\n" + `#EXTINF:-1 tvg-id="Rai 1" tvg-name="Rai 1",Rai 1` + "\n" + "http://example.com/stream\n" var out bytes.Buffer ids, orig, err := processPlaylist(strings.NewReader(in), &out, "https://relay/epg") if err != nil { t.Fatalf("unexpected error: %v", err) } if orig != "" { t.Errorf("originalEPG = %q, want empty", orig) } if got, want := ids, []string{"Rai 1"}; !reflect.DeepEqual(got, want) { t.Errorf("tvgIDs = %v, want %v", got, want) } want := `#EXTM3U url-tvg="https://relay/epg"` + "\n" + `#EXTINF:-1 tvg-id="Rai 1" tvg-name="Rai 1",Rai 1` + "\n" + "http://example.com/stream\n" if out.String() != want { t.Errorf("output mismatch.\n got: %q\nwant: %q", out.String(), want) } } func TestProcessPlaylist_ReplacesExistingURLTvg(t *testing.T) { in := `#EXTM3U url-tvg="http://upstream.example/epg.xml"` + "\n" + `#EXTINF:-1 tvg-id="Italia 1",Italia 1` + "\n" + "http://example.com/stream\n" var out bytes.Buffer ids, orig, err := processPlaylist(strings.NewReader(in), &out, "https://relay/epg") if err != nil { t.Fatalf("unexpected error: %v", err) } if orig != "http://upstream.example/epg.xml" { t.Errorf("originalEPG = %q, want upstream URL captured", orig) } if got, want := ids, []string{"Italia 1"}; !reflect.DeepEqual(got, want) { t.Errorf("tvgIDs = %v, want %v", got, want) } if !strings.Contains(out.String(), `url-tvg="https://relay/epg"`) { t.Errorf("expected rewritten url-tvg in output, got: %q", out.String()) } if strings.Contains(out.String(), "upstream.example") { t.Errorf("upstream URL leaked into output: %q", out.String()) } } func TestProcessPlaylist_EmptyEPGURLLeavesHeaderAlone(t *testing.T) { in := `#EXTM3U url-tvg="http://upstream.example/epg.xml"` + "\n" + "http://example.com/stream\n" var out bytes.Buffer _, orig, err := processPlaylist(strings.NewReader(in), &out, "") if err != nil { t.Fatalf("unexpected error: %v", err) } if orig != "http://upstream.example/epg.xml" { t.Errorf("originalEPG = %q, want captured", orig) } if out.String() != in { t.Errorf("output should match input verbatim when epgURL is empty.\n got: %q\nwant: %q", out.String(), in) } } func TestProcessPlaylist_PreservesCRLF(t *testing.T) { in := "#EXTM3U\r\n" + `#EXTINF:-1 tvg-id="Rai 1",Rai 1` + "\r\n" + "http://example.com/stream\r\n" var out bytes.Buffer if _, _, err := processPlaylist(strings.NewReader(in), &out, "https://relay/epg"); err != nil { t.Fatalf("unexpected error: %v", err) } if !strings.HasPrefix(out.String(), `#EXTM3U url-tvg="https://relay/epg"`+"\r\n") { t.Errorf("CRLF not preserved in header: %q", out.String()) } if !strings.Contains(out.String(), "http://example.com/stream\r\n") { t.Errorf("CRLF not preserved in body: %q", out.String()) } } func TestProcessPlaylist_StreamLinesPassThrough(t *testing.T) { // All non-header lines must come through byte-for-byte; only the first // non-empty line (the EXTM3U header) is rewritten. in := "#EXTM3U\n" + `#EXTINF:-1 tvg-id="A",A` + "\n" + "http://example.com/a\n" + `#EXTINF:-1 tvg-id="",B` + "\n" + "http://example.com/b\n" var out bytes.Buffer ids, _, err := processPlaylist(strings.NewReader(in), &out, "https://relay/epg") if err != nil { t.Fatalf("unexpected error: %v", err) } if got, want := ids, []string{"A"}; !reflect.DeepEqual(got, want) { t.Errorf("tvgIDs = %v, want %v (empty tvg-id must be skipped)", got, want) } for _, must := range []string{ `#EXTINF:-1 tvg-id="A",A` + "\n", "http://example.com/a\n", `#EXTINF:-1 tvg-id="",B` + "\n", "http://example.com/b\n", } { if !strings.Contains(out.String(), must) { t.Errorf("output missing line %q", must) } } } func TestProcessPlaylist_SkipsLeadingBlankLines(t *testing.T) { in := "\n\n#EXTM3U\n#EXTINF:-1 tvg-id=\"X\",X\nhttp://example.com/x\n" var out bytes.Buffer ids, _, err := processPlaylist(strings.NewReader(in), &out, "https://relay/epg") if err != nil { t.Fatalf("unexpected error: %v", err) } if !reflect.DeepEqual(ids, []string{"X"}) { t.Errorf("tvgIDs = %v", ids) } if !strings.HasPrefix(out.String(), "\n\n#EXTM3U "+`url-tvg="https://relay/epg"`+"\n") { t.Errorf("leading blanks should be preserved before rewritten header: %q", out.String()) } } func TestProcessPlaylist_RejectsMissingHeader(t *testing.T) { in := "garbage\n#EXTINF:-1 tvg-id=\"X\",X\n" var out bytes.Buffer _, _, err := processPlaylist(strings.NewReader(in), &out, "https://relay/epg") if err == nil { t.Fatal("expected error for missing #EXTM3U header") } } func TestProcessPlaylist_RejectsEmptyInput(t *testing.T) { var out bytes.Buffer _, _, err := processPlaylist(strings.NewReader(""), &out, "https://relay/epg") if err == nil { t.Fatal("expected error for empty input") } }