test: enable Subsonic response snapshot tests on Windows (#5427)

* fix(test): enable Subsonic response snapshot tests on Windows

Replaced cupaloy with a simple custom snapshot matcher that normalizes
CRLF line endings before comparison. The tests were skipped on Windows
via a //go:build unix tag because Git for Windows checks out snapshot
files with CRLF, while Go's xml/json.MarshalIndent always produces LF,
causing direct string comparison to fail. The new matcher reads snapshot
files with os.ReadFile and normalizes \r\n to \n before comparing.
Also added a .gitattributes in the .snapshots directory to enforce LF
checkout, and removed the now-unused cupaloy dependency.

* fix(test): add UPDATE_SNAPSHOTS support to custom snapshot matcher

Restore the ability to update snapshots via `make snapshots`
(UPDATE_SNAPSHOTS=true), which was lost when replacing cupaloy
with the custom matcher.
This commit is contained in:
Deluan Quintão 2026-04-27 20:19:28 -04:00 • committed by GitHub
commit 3e25ca3868
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 54 additions and 23 deletions

View file

@ -0,0 +1 @@
* eol=lf

View file

@ -1,14 +1,17 @@
package responses
import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"github.com/bradleyjkemp/cupaloy/v2"
"github.com/navidrome/navidrome/log"
"github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega"
"github.com/onsi/gomega/types"
"github.com/pmezard/go-difflib/difflib"
)
func TestSubsonicApiResponses(t *testing.T) {
@ -18,25 +21,61 @@ func TestSubsonicApiResponses(t *testing.T) {
}
func MatchSnapshot() types.GomegaMatcher {
c := cupaloy.New(cupaloy.FailOnUpdate(false))
return &snapshotMatcher{c}
return &snapshotMatcher{}
}
type snapshotMatcher struct {
c *cupaloy.Config
diff string
}
func (matcher snapshotMatcher) Match(actual any) (success bool, err error) {
actualJson := strings.TrimSpace(string(actual.([]byte)))
err = matcher.c.SnapshotWithName(ginkgo.CurrentSpecReport().FullText(), actualJson)
success = err == nil
return
func (matcher *snapshotMatcher) Match(actual any) (success bool, err error) {
actualBytes, ok := actual.([]byte)
if !ok {
return false, fmt.Errorf("MatchSnapshot expects []byte, got %T", actual)
}
name := ginkgo.CurrentSpecReport().FullText()
actualStr := strings.TrimSpace(string(actualBytes))
snapshotPath := filepath.Join(".snapshots", name)
// Support UPDATE_SNAPSHOTS=true for `make snapshots`
if _, update := os.LookupEnv("UPDATE_SNAPSHOTS"); update {
err := os.MkdirAll(".snapshots", os.ModePerm)
if err != nil {
return false, err
}
return true, os.WriteFile(snapshotPath, []byte(actualStr+"\n"), 0600)
}
expected, err := os.ReadFile(snapshotPath)
if err != nil {
return false, err
}
// Normalize line endings so snapshots work on Windows (CRLF) and Unix (LF)
normalizedExpected := strings.ReplaceAll(string(expected), "\r\n", "\n")
normalizedExpected = strings.TrimSpace(normalizedExpected)
if actualStr == normalizedExpected {
return true, nil
}
diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(normalizedExpected),
B: difflib.SplitLines(actualStr),
FromFile: "snapshot",
ToFile: "actual",
Context: 3,
})
matcher.diff = diff
return false, nil
}
func (matcher snapshotMatcher) FailureMessage(_ any) (message string) {
return "Expected to match saved snapshot\n"
func (matcher *snapshotMatcher) FailureMessage(_ any) (message string) {
return "Expected to match saved snapshot\n" + matcher.diff
}
func (matcher snapshotMatcher) NegatedFailureMessage(_ any) (message string) {
func (matcher *snapshotMatcher) NegatedFailureMessage(_ any) (message string) {
return "Expected to not match saved snapshot\n"
}

View file

@ -1,8 +1,3 @@
//go:build unix
// TODO Fix snapshot tests in Windows
// Response Snapshot tests. Only run in Linux and macOS, as they fail in Windows
// Probably because of EOL char differences
package responses_test
import (