🏡 index : quine-global/quintodrome.git

package persistence

import (
	"time"

	"github.com/Masterminds/squirrel"
	"github.com/navidrome/navidrome/conf"
	"github.com/navidrome/navidrome/conf/configtest"
	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
)

var _ = Describe("Helpers", func() {
	Describe("toSnakeCase", func() {
		It("converts camelCase", func() {
			Expect(toSnakeCase("camelCase")).To(Equal("camel_case"))
		})
		It("converts PascalCase", func() {
			Expect(toSnakeCase("PascalCase")).To(Equal("pascal_case"))
		})
		It("converts ALLCAPS", func() {
			Expect(toSnakeCase("ALLCAPS")).To(Equal("allcaps"))
		})
		It("does not converts snake_case", func() {
			Expect(toSnakeCase("snake_case")).To(Equal("snake_case"))
		})
	})
	Describe("toCamelCase", func() {
		It("converts snake_case", func() {
			Expect(toCamelCase("snake_case")).To(Equal("snakeCase"))
		})
		It("converts PascalCase", func() {
			Expect(toCamelCase("PascalCase")).To(Equal("PascalCase"))
		})
		It("converts camelCase", func() {
			Expect(toCamelCase("camelCase")).To(Equal("camelCase"))
		})
		It("converts ALLCAPS", func() {
			Expect(toCamelCase("ALLCAPS")).To(Equal("ALLCAPS"))
		})
	})
	Describe("toSQLArgs", func() {
		type Embed struct{}
		type Model struct {
			Embed     `structs:"-"`
			ID        string     `structs:"id" json:"id"`
			AlbumId   string     `structs:"album_id" json:"albumId"`
			PlayCount int        `structs:"play_count" json:"playCount"`
			UpdatedAt *time.Time `structs:"updated_at"`
			CreatedAt time.Time  `structs:"created_at"`
		}

		It("returns a map with snake_case keys", func() {
			now := time.Now()
			m := &Model{ID: "123", AlbumId: "456", CreatedAt: now, UpdatedAt: &now, PlayCount: 2}
			args, err := toSQLArgs(m)
			Expect(err).To(BeNil())
			Expect(args).To(SatisfyAll(
				HaveKeyWithValue("id", "123"),
				HaveKeyWithValue("album_id", "456"),
				HaveKeyWithValue("play_count", 2),
				HaveKeyWithValue("updated_at", BeTemporally("~", now)),
				HaveKeyWithValue("created_at", BeTemporally("~", now)),
				Not(HaveKey("Embed")),
			))
		})
	})

	Describe("Exists", func() {
		It("constructs the correct EXISTS query", func() {
			e := Exists("album", squirrel.Eq{"id": 1})
			sql, args, err := e.ToSql()
			Expect(sql).To(Equal("exists (select 1 from album where id = ?)"))
			Expect(args).To(ConsistOf(1))
			Expect(err).To(BeNil())
		})
	})

	Describe("NotExists", func() {
		It("constructs the correct NOT EXISTS query", func() {
			e := NotExists("artist", squirrel.ConcatExpr("id = artist_id"))
			sql, args, err := e.ToSql()
			Expect(sql).To(Equal("not exists (select 1 from artist where id = artist_id)"))
			Expect(args).To(BeEmpty())
			Expect(err).To(BeNil())
		})
	})

	Describe("mapSortOrder", func() {
		BeforeEach(func() {
			DeferCleanup(configtest.SetupConfig())
		})

		It("does not change the sort string if there are no order columns", func() {
			Expect(mapSortOrder("album", "album_name asc")).To(Equal("album_name asc"))
		})

		DescribeTable("maps order columns to a collated expression",
			func(preferSortTags, naturalSorting bool, expected string) {
				conf.Server.PreferSortTags = preferSortTags
				conf.Server.EnableNaturalSorting = naturalSorting
				Expect(mapSortOrder("album", "ORDER_ALBUM_NAME asc")).To(Equal(expected))
			},
			Entry("qualified column", false, false,
				"(album.order_album_name collate nocase) asc"),
			Entry("natural collation", false, true,
				"(album.order_album_name collate NATSORT) asc"),
			Entry("sort tags preferred", true, false,
				`(coalesce(nullif(album.sort_album_name,''),album.order_album_name) collate nocase) asc`),
			Entry("sort tags preferred, natural collation", true, true,
				`(coalesce(nullif(album.sort_album_name,''),album.order_album_name) collate NATSORT) asc`),
		)

		It("changes multiple order columns to sort expressions", func() {
			conf.Server.PreferSortTags = true
			sort := "compilation, order_title asc, order_album_artist_name desc, year desc"
			Expect(mapSortOrder("album", sort)).To(Equal(
				`compilation, (coalesce(nullif(album.sort_title,''),album.order_title) collate nocase) asc,` +
					` (coalesce(nullif(album.sort_album_artist_name,''),album.order_album_artist_name) collate nocase) desc, year desc`))
		})
	})

	Describe("naturalSort", func() {
		BeforeEach(func() {
			DeferCleanup(configtest.SetupConfig())
		})

		It("leaves the column alone by default, keeping its declared collation", func() {
			Expect(naturalSort("media_file.title")).To(Equal("media_file.title"))
		})

		It("applies the natural collation when enabled", func() {
			conf.Server.EnableNaturalSorting = true
			Expect(naturalSort("media_file.title")).To(Equal("(media_file.title collate NATSORT)"))
		})
	})
})