package moonblood import ( "bufio" "os" "testing" ) func TestParseMoonblood(t *testing.T) { file, err := os.Open("../../resources/moonblood.txt") if err != nil { t.Fatal(err) } defer file.Close() scanner := bufio.NewScanner(file) total, success := 0, 0 for scanner.Scan() { line := scanner.Text() if line == "" { continue } result := Parse(line) total++ if result.Group == "MOONBLOOD" && result.Year != 0 && result.Format == "FLAC" { success++ } if result.Confidence < 0.7 { t.Logf("Low confidence: %s (%.2f)", line, result.Confidence) } } t.Logf("Parsed %d releases, %d successful (%.1f%%)", total, success, float64(success)/float64(total)*100) } func TestParseFullTail(t *testing.T) { r := Parse("Fane-Dragged_Through_the_Unending-24BIT-WEB-FLAC-2025-MOONBLOOD") if r.Artist != "Fane" { t.Errorf("Artist = %q, want %q", r.Artist, "Fane") } if r.Title != "Dragged_Through_the_Unending" { t.Errorf("Title = %q, want %q", r.Title, "Dragged_Through_the_Unending") } if r.Year != 2025 || r.Format != "FLAC" || r.Source != "WEB" || r.BitDepth != "24BIT" { t.Errorf("tail fields wrong: %+v", r) } if len(r.UnknownTokens) != 0 { t.Errorf("UnknownTokens = %v, want empty", r.UnknownTokens) } if r.Confidence < 0.99 { t.Errorf("Confidence = %.2f, want ~1.0", r.Confidence) } } func TestParseEditionAndDemo(t *testing.T) { r := Parse("Satans_Grind-Rotten_Demo-DEMO-16BIT-WEB-FLAC-2016-MOONBLOOD") if r.Edition != "DEMO" { t.Errorf("Edition = %q, want %q", r.Edition, "DEMO") } if r.Artist != "Satans_Grind" || r.Title != "Rotten_Demo" { t.Errorf("Artist/Title = %q/%q", r.Artist, r.Title) } if len(r.UnknownTokens) != 0 { t.Errorf("UnknownTokens = %v, want empty", r.UnknownTokens) } } func TestParseArtistWithHyphen(t *testing.T) { // Artist itself contains a hyphen; no part of it may leak into UnknownTokens. r := Parse("Ultra-Violence-All_My_Life-EP-16BIT-WEB-FLAC-2026-MOONBLOOD") if r.Artist != "Ultra" || r.Title != "Violence-All_My_Life" { t.Errorf("Artist/Title = %q/%q, want Ultra / Violence-All_My_Life", r.Artist, r.Title) } if r.Edition != "EP" || r.BitDepth != "16BIT" || r.Source != "WEB" || r.Year != 2026 { t.Errorf("tail fields wrong: %+v", r) } if len(r.UnknownTokens) != 0 { t.Errorf("UnknownTokens = %v, want empty", r.UnknownTokens) } } func TestParseLanguageAndCDSource(t *testing.T) { r := Parse("Burn_Thou_in_Effigy-Memorias_Postumas_do_Imperador-BR-CD-FLAC-2026-MOONBLOOD") if r.Language != "BR" || r.Source != "CD" || r.Year != 2026 || r.Format != "FLAC" { t.Errorf("tail fields wrong: %+v", r) } if len(r.UnknownTokens) != 0 { t.Errorf("UnknownTokens = %v, want empty", r.UnknownTokens) } } func TestParseNoYear(t *testing.T) { // Without a year the tail still parses; group stays the last token. r := Parse("Fane-Gallant_Shroud-EP-16BIT-WEB-FLAC-MOONBLOOD") if r.Group != "MOONBLOOD" || r.Year != 0 { t.Errorf("Group/Year = %q/%d", r.Group, r.Year) } if r.Edition != "EP" || r.Source != "WEB" || r.Format != "FLAC" { t.Errorf("tail fields wrong: %+v", r) } if r.Artist != "Fane" || r.Title != "Gallant_Shroud" { t.Errorf("Artist/Title = %q/%q", r.Artist, r.Title) } } func TestParseResolutionSlot(t *testing.T) { r := Parse("Ice_Nine_Kills-Last_Chance_To_Make_Amends-REMASTERED-24BIT-44KHZ-WEB-FLAC-2024-FLACCiD") if r.Group != "FLACCiD" { t.Errorf("Group = %q, want FLACCiD", r.Group) } if r.Resolution != "44KHZ" { t.Errorf("Resolution = %q, want 44KHZ", r.Resolution) } if r.BitDepth != "24BIT" || r.Edition != "REMASTERED" || r.Source != "WEB" || r.Year != 2024 { t.Errorf("tail fields wrong: %+v", r) } if r.Artist != "Ice_Nine_Kills" || r.Title != "Last_Chance_To_Make_Amends" { t.Errorf("Artist/Title = %q/%q", r.Artist, r.Title) } if len(r.UnknownTokens) != 0 { t.Errorf("UnknownTokens = %v, want empty", r.UnknownTokens) } } func TestParseVinylIsSource(t *testing.T) { r := Parse("ROTH-Alles_Endet-DE-VINYL-FLAC-2026-TOTENKVLT") if r.Language != "DE" { t.Errorf("Language = %q, want DE", r.Language) } if r.Source != "VINYL" { t.Errorf("Source = %q, want VINYL", r.Source) } if r.Edition != "" { t.Errorf("Edition = %q, want empty (VINYL is source, not edition)", r.Edition) } if len(r.UnknownTokens) != 0 { t.Errorf("UnknownTokens = %v, want empty", r.UnknownTokens) } } func TestParseCaseInsensitiveDictionaries(t *testing.T) { r := Parse("Artist-Some_Title-16bit-web-flac-2020-moonblood") if r.BitDepth != "16BIT" || r.Source != "WEB" || r.Format != "FLAC" { t.Errorf("case-insensitive lookup failed: %+v", r) } if r.Artist != "Artist" || r.Title != "Some_Title" { t.Errorf("Artist/Title = %q/%q", r.Artist, r.Title) } } func TestParseUnknownTailTokenReported(t *testing.T) { // Невідомий токен між двома відомими маркерами → у UnknownTokens. r := Parse("Artist-Some_Title-SPLIT-FOO-16BIT-WEB-FLAC-2025-MOONBLOOD") if len(r.UnknownTokens) != 1 || r.UnknownTokens[0] != "FOO" { t.Errorf("UnknownTokens = %v, want [FOO]", r.UnknownTokens) } if r.Edition != "SPLIT" || r.BitDepth != "16BIT" || r.Source != "WEB" || r.Year != 2025 { t.Errorf("tail fields wrong: %+v", r) } if r.Artist != "Artist" || r.Title != "Some_Title" { t.Errorf("Artist/Title = %q/%q", r.Artist, r.Title) } } func TestParseCoreTokensNeverReported(t *testing.T) { // Артист/тайтл ніколи не потрапляють в UnknownTokens. r := Parse("Fane-Dragged_Through_the_Unending-24BIT-WEB-FLAC-2025-MOONBLOOD") if len(r.UnknownTokens) != 0 { t.Errorf("UnknownTokens = %v, want empty", r.UnknownTokens) } if r.Artist != "Fane" || r.Title != "Dragged_Through_the_Unending" { t.Errorf("Artist/Title = %q/%q", r.Artist, r.Title) } }