Merge pull request #1754 from code-yeongyu/fix/issue-1745-auto-update-pin
fix: respect user-pinned plugin version, skip auto-update when explicitly pinned
This commit is contained in:
@@ -0,0 +1,174 @@
|
|||||||
|
import { describe, it, expect, mock, beforeEach } from "bun:test"
|
||||||
|
|
||||||
|
// Mock modules before importing
|
||||||
|
const mockFindPluginEntry = mock(() => null as any)
|
||||||
|
const mockGetCachedVersion = mock(() => null as string | null)
|
||||||
|
const mockGetLatestVersion = mock(async () => null as string | null)
|
||||||
|
const mockUpdatePinnedVersion = mock(() => false)
|
||||||
|
const mockExtractChannel = mock(() => "latest")
|
||||||
|
const mockInvalidatePackage = mock(() => {})
|
||||||
|
const mockRunBunInstall = mock(async () => true)
|
||||||
|
const mockShowUpdateAvailableToast = mock(async () => {})
|
||||||
|
const mockShowAutoUpdatedToast = mock(async () => {})
|
||||||
|
|
||||||
|
mock.module("../checker", () => ({
|
||||||
|
findPluginEntry: mockFindPluginEntry,
|
||||||
|
getCachedVersion: mockGetCachedVersion,
|
||||||
|
getLatestVersion: mockGetLatestVersion,
|
||||||
|
updatePinnedVersion: mockUpdatePinnedVersion,
|
||||||
|
}))
|
||||||
|
|
||||||
|
mock.module("../version-channel", () => ({
|
||||||
|
extractChannel: mockExtractChannel,
|
||||||
|
}))
|
||||||
|
|
||||||
|
mock.module("../cache", () => ({
|
||||||
|
invalidatePackage: mockInvalidatePackage,
|
||||||
|
}))
|
||||||
|
|
||||||
|
mock.module("../../../cli/config-manager", () => ({
|
||||||
|
runBunInstall: mockRunBunInstall,
|
||||||
|
}))
|
||||||
|
|
||||||
|
mock.module("./update-toasts", () => ({
|
||||||
|
showUpdateAvailableToast: mockShowUpdateAvailableToast,
|
||||||
|
showAutoUpdatedToast: mockShowAutoUpdatedToast,
|
||||||
|
}))
|
||||||
|
|
||||||
|
mock.module("../../../shared/logger", () => ({
|
||||||
|
log: () => {},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { runBackgroundUpdateCheck } = await import("./background-update-check")
|
||||||
|
|
||||||
|
describe("runBackgroundUpdateCheck", () => {
|
||||||
|
const mockCtx = { directory: "/test" } as any
|
||||||
|
const mockGetToastMessage = (isUpdate: boolean, version?: string) =>
|
||||||
|
isUpdate ? `Update to ${version}` : "Up to date"
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockFindPluginEntry.mockReset()
|
||||||
|
mockGetCachedVersion.mockReset()
|
||||||
|
mockGetLatestVersion.mockReset()
|
||||||
|
mockUpdatePinnedVersion.mockReset()
|
||||||
|
mockExtractChannel.mockReset()
|
||||||
|
mockInvalidatePackage.mockReset()
|
||||||
|
mockRunBunInstall.mockReset()
|
||||||
|
mockShowUpdateAvailableToast.mockReset()
|
||||||
|
mockShowAutoUpdatedToast.mockReset()
|
||||||
|
|
||||||
|
mockExtractChannel.mockReturnValue("latest")
|
||||||
|
mockRunBunInstall.mockResolvedValue(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#given user has pinned a specific version", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mockFindPluginEntry.mockReturnValue({
|
||||||
|
entry: "oh-my-opencode@3.4.0",
|
||||||
|
isPinned: true,
|
||||||
|
pinnedVersion: "3.4.0",
|
||||||
|
configPath: "/test/opencode.json",
|
||||||
|
})
|
||||||
|
mockGetCachedVersion.mockReturnValue("3.4.0")
|
||||||
|
mockGetLatestVersion.mockResolvedValue("3.5.0")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("#then should NOT call updatePinnedVersion", async () => {
|
||||||
|
await runBackgroundUpdateCheck(mockCtx, true, mockGetToastMessage)
|
||||||
|
|
||||||
|
expect(mockUpdatePinnedVersion).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("#then should show update-available toast instead", async () => {
|
||||||
|
await runBackgroundUpdateCheck(mockCtx, true, mockGetToastMessage)
|
||||||
|
|
||||||
|
expect(mockShowUpdateAvailableToast).toHaveBeenCalledWith(
|
||||||
|
mockCtx,
|
||||||
|
"3.5.0",
|
||||||
|
mockGetToastMessage
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("#then should NOT run bun install", async () => {
|
||||||
|
await runBackgroundUpdateCheck(mockCtx, true, mockGetToastMessage)
|
||||||
|
|
||||||
|
expect(mockRunBunInstall).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("#then should NOT invalidate package cache", async () => {
|
||||||
|
await runBackgroundUpdateCheck(mockCtx, true, mockGetToastMessage)
|
||||||
|
|
||||||
|
expect(mockInvalidatePackage).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#given user has NOT pinned a version (unpinned)", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mockFindPluginEntry.mockReturnValue({
|
||||||
|
entry: "oh-my-opencode",
|
||||||
|
isPinned: false,
|
||||||
|
pinnedVersion: null,
|
||||||
|
configPath: "/test/opencode.json",
|
||||||
|
})
|
||||||
|
mockGetCachedVersion.mockReturnValue("3.4.0")
|
||||||
|
mockGetLatestVersion.mockResolvedValue("3.5.0")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("#then should proceed with auto-update", async () => {
|
||||||
|
await runBackgroundUpdateCheck(mockCtx, true, mockGetToastMessage)
|
||||||
|
|
||||||
|
expect(mockInvalidatePackage).toHaveBeenCalled()
|
||||||
|
expect(mockRunBunInstall).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("#then should show auto-updated toast on success", async () => {
|
||||||
|
mockRunBunInstall.mockResolvedValue(true)
|
||||||
|
|
||||||
|
await runBackgroundUpdateCheck(mockCtx, true, mockGetToastMessage)
|
||||||
|
|
||||||
|
expect(mockShowAutoUpdatedToast).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#given autoUpdate is false", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mockFindPluginEntry.mockReturnValue({
|
||||||
|
entry: "oh-my-opencode",
|
||||||
|
isPinned: false,
|
||||||
|
pinnedVersion: null,
|
||||||
|
configPath: "/test/opencode.json",
|
||||||
|
})
|
||||||
|
mockGetCachedVersion.mockReturnValue("3.4.0")
|
||||||
|
mockGetLatestVersion.mockResolvedValue("3.5.0")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("#then should only show notification toast", async () => {
|
||||||
|
await runBackgroundUpdateCheck(mockCtx, false, mockGetToastMessage)
|
||||||
|
|
||||||
|
expect(mockShowUpdateAvailableToast).toHaveBeenCalled()
|
||||||
|
expect(mockRunBunInstall).not.toHaveBeenCalled()
|
||||||
|
expect(mockUpdatePinnedVersion).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#given already on latest version", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mockFindPluginEntry.mockReturnValue({
|
||||||
|
entry: "oh-my-opencode@3.5.0",
|
||||||
|
isPinned: true,
|
||||||
|
pinnedVersion: "3.5.0",
|
||||||
|
configPath: "/test/opencode.json",
|
||||||
|
})
|
||||||
|
mockGetCachedVersion.mockReturnValue("3.5.0")
|
||||||
|
mockGetLatestVersion.mockResolvedValue("3.5.0")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("#then should not update or show toast", async () => {
|
||||||
|
await runBackgroundUpdateCheck(mockCtx, true, mockGetToastMessage)
|
||||||
|
|
||||||
|
expect(mockUpdatePinnedVersion).not.toHaveBeenCalled()
|
||||||
|
expect(mockShowUpdateAvailableToast).not.toHaveBeenCalled()
|
||||||
|
expect(mockShowAutoUpdatedToast).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -56,13 +56,9 @@ export async function runBackgroundUpdateCheck(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pluginInfo.isPinned) {
|
if (pluginInfo.isPinned) {
|
||||||
const updated = updatePinnedVersion(pluginInfo.configPath, pluginInfo.entry, latestVersion)
|
await showUpdateAvailableToast(ctx, latestVersion, getToastMessage)
|
||||||
if (!updated) {
|
log(`[auto-update-checker] User-pinned version detected (${pluginInfo.entry}), skipping auto-update. Notification only.`)
|
||||||
await showUpdateAvailableToast(ctx, latestVersion, getToastMessage)
|
return
|
||||||
log("[auto-update-checker] Failed to update pinned version in config")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log(`[auto-update-checker] Config updated: ${pluginInfo.entry} → ${PACKAGE_NAME}@${latestVersion}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidatePackage(PACKAGE_NAME)
|
invalidatePackage(PACKAGE_NAME)
|
||||||
|
|||||||
Reference in New Issue
Block a user