🏡 index : quine-global/quintodrome.git

package plugins

import (
	"context"
	"os"
	"path/filepath"

	"github.com/navidrome/navidrome/conf"
	"github.com/navidrome/navidrome/conf/configtest"
	"github.com/navidrome/navidrome/model"
	"github.com/navidrome/navidrome/tests"
	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
)

var _ = Describe("Manager.LoadPlugins", func() {
	var (
		ctx    context.Context
		mgr    *Manager
		repo   *tests.MockPluginRepo
		tmpDir string
	)

	BeforeEach(func() {
		ctx = GinkgoT().Context()
	})

	// newManager builds a manager over rows the caller can corrupt, with no Subsonic router: a CLI
	// has none, and Start would log.Fatal on that.
	newManager := func(rows model.Plugins) *Manager {
		DeferCleanup(configtest.SetupConfig())
		var err error
		tmpDir, err = os.MkdirTemp("", "plugins-readonly-*")
		Expect(err).ToNot(HaveOccurred())
		DeferCleanup(func() { _ = os.RemoveAll(tmpDir) })

		conf.Server.Plugins.Enabled = true
		conf.Server.Plugins.Folder = conf.NewDir(tmpDir)
		conf.Server.Plugins.AutoReload = false
		conf.Server.CacheFolder = conf.NewDir(tmpDir)

		if rows == nil {
			rows = installTestPlugins(tmpDir, "test-metadata-agent"+PackageExtension)
			for i := range rows {
				rows[i].AllUsers = true
			}
		}
		repo = tests.CreateMockPluginRepo()
		repo.Permitted = true
		repo.SetData(rows)
		m := &Manager{
			plugins: make(map[string]*plugin),
			ds:      &tests.MockDataStore{MockedPlugin: repo},
			metrics: noopMetricsRecorder{},
		}
		DeferCleanup(func() { _ = m.Stop() })
		return m
	}

	It("detects capabilities without a Subsonic router configured", func() {
		mgr = newManager(nil)

		Expect(mgr.LoadPlugins(ctx, []string{"test-metadata-agent", "broken"}, false)).To(Succeed())

		Expect(mgr.PluginNames(string(CapabilityMetadataAgent))).To(ContainElement("test-metadata-agent"))
	})

	Context("when a plugin cannot be loaded", func() {
		brokenRows := func() model.Plugins {
			return model.Plugins{{
				ID: "broken", Path: filepath.Join(GinkgoT().TempDir(), "does-not-exist.ndp"),
				Enabled: true, AllUsers: true,
			}}
		}

		It("leaves the stored row untouched", func() {
			mgr = newManager(brokenRows())

			Expect(mgr.LoadPlugins(ctx, []string{"test-metadata-agent", "broken"}, false)).To(Succeed())

			stored, err := repo.Get(ctx, "broken")
			Expect(err).ToNot(HaveOccurred())
			Expect(stored.Enabled).To(BeTrue(), "inspecting a plugin must never disable it")
			Expect(stored.LastError).To(BeEmpty())
		})

		// Without this the test above would pass for the wrong reason. Start cannot be used: it
		// syncs the folder first, dropping a row whose file is missing before any load.
		It("still disables it when not read-only", func() {
			mgr = newManager(brokenRows())

			Expect(mgr.loadEnabledPlugins(ctx)).To(Succeed())

			stored, err := repo.Get(ctx, "broken")
			Expect(err).ToNot(HaveOccurred())
			Expect(stored.Enabled).To(BeFalse())
			Expect(stored.LastError).ToNot(BeEmpty())
		})
	})

	// Loading a plugin creates its host services — a KVStore or task queue database on disk — so a
	// plugin that could never supply an image must not be instantiated just to be ignored.
	It("does not load a plugin that is not in the agent list", func() {
		mgr = newManager(nil)

		Expect(mgr.LoadPlugins(ctx, []string{"some-other-agent"}, false)).To(Succeed())

		Expect(mgr.PluginNames(string(CapabilityMetadataAgent))).To(BeEmpty())
	})

	It("does nothing when no agents are configured", func() {
		mgr = newManager(nil)

		Expect(mgr.LoadPlugins(ctx, nil, false)).To(Succeed())

		Expect(mgr.PluginNames(string(CapabilityMetadataAgent))).To(BeEmpty())
		// Not even the wazero cache: with nothing to load there is nothing to compile.
		Expect(filepath.Join(tmpDir, "plugins")).ToNot(BeADirectory())
	})

	It("does nothing when the plugin system is disabled", func() {
		mgr = newManager(nil)
		conf.Server.Plugins.Enabled = false

		Expect(mgr.LoadPlugins(ctx, []string{"test-metadata-agent", "broken"}, false)).To(Succeed())

		Expect(mgr.PluginNames(string(CapabilityMetadataAgent))).To(BeEmpty())
	})
})