/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { updateCommand, handleUpdate } from './update.js';
import yargs from 'yargs';
import { ExtensionUpdateState } from '../../ui/state/extensions.js';
const mockGetLoadedExtensions = vi.hoisted(() => vi.fn());
const mockUpdateExtension = vi.hoisted(() => vi.fn());
const mockCheckForAllExtensionUpdates = vi.hoisted(() => vi.fn());
const mockUpdateAllUpdatableExtensions = vi.hoisted(() => vi.fn());
const mockCheckForExtensionUpdate = vi.hoisted(() => vi.fn());
const mockWriteStdoutLine = vi.hoisted(() => vi.fn());
const mockWriteStderrLine = vi.hoisted(() => vi.fn());
vi.mock('./utils.js', () => ({
    getExtensionManager: vi.fn().mockResolvedValue({
        getLoadedExtensions: mockGetLoadedExtensions,
        updateExtension: mockUpdateExtension,
        checkForAllExtensionUpdates: mockCheckForAllExtensionUpdates,
        updateAllUpdatableExtensions: mockUpdateAllUpdatableExtensions,
    }),
}));
vi.mock('@hoptrendy/hopcode-core', () => ({
    checkForExtensionUpdate: mockCheckForExtensionUpdate,
}));
vi.mock('../../utils/errors.js', () => ({
    getErrorMessage: vi.fn((error) => error.message),
}));
vi.mock('../../ui/state/extensions.js', () => ({
    ExtensionUpdateState: {
        UPDATE_AVAILABLE: 'update available',
        UP_TO_DATE: 'up to date',
        ERROR: 'error',
    },
}));
vi.mock('../../utils/stdioHelpers.js', () => ({
    writeStdoutLine: mockWriteStdoutLine,
    writeStderrLine: mockWriteStderrLine,
    clearScreen: vi.fn(),
}));
describe('extensions update command', () => {
    it('should fail if neither name nor --all is provided', () => {
        const validationParser = yargs([])
            .command(updateCommand)
            .fail(false)
            .locale('en');
        expect(() => validationParser.parse('update')).toThrow('Either an extension name or --all must be provided');
    });
    it('should fail if both name and --all are provided', () => {
        const validationParser = yargs([])
            .command(updateCommand)
            .fail(false)
            .locale('en');
        expect(() => validationParser.parse('update test-extension --all')).toThrow(/Arguments .* are mutually exclusive/);
    });
    it('should accept --all flag', () => {
        const parser = yargs([]).command(updateCommand).fail(false).locale('en');
        expect(() => parser.parse('update --all')).not.toThrow();
    });
    it('should accept an extension name', () => {
        const parser = yargs([]).command(updateCommand).fail(false).locale('en');
        expect(() => parser.parse('update test-extension')).not.toThrow();
    });
});
describe('handleUpdate', () => {
    beforeEach(() => {
        vi.clearAllMocks();
    });
    describe('update by name', () => {
        it('should show message when extension is not found', async () => {
            mockGetLoadedExtensions.mockReturnValueOnce([]);
            await handleUpdate({ name: 'non-existent-extension' });
            expect(mockWriteStdoutLine).toHaveBeenCalledWith('Extension "non-existent-extension" not found.');
        });
        it('should show message when extension has no install metadata', async () => {
            mockGetLoadedExtensions.mockReturnValueOnce([
                { name: 'test-extension', installMetadata: undefined },
            ]);
            await handleUpdate({ name: 'test-extension' });
            expect(mockWriteStdoutLine).toHaveBeenCalledWith('Unable to install extension "test-extension" due to missing install metadata');
        });
        it('should show message when extension is already up to date', async () => {
            const mockExtension = {
                name: 'test-extension',
                installMetadata: { source: 'test' },
            };
            mockGetLoadedExtensions.mockReturnValueOnce([mockExtension]);
            mockCheckForExtensionUpdate.mockResolvedValueOnce(ExtensionUpdateState.UP_TO_DATE);
            await handleUpdate({ name: 'test-extension' });
            expect(mockWriteStdoutLine).toHaveBeenCalledWith('Extension "test-extension" is already up to date.');
        });
        it('should update extension when update is available', async () => {
            const mockExtension = {
                name: 'test-extension',
                installMetadata: { source: 'test' },
            };
            mockGetLoadedExtensions.mockReturnValueOnce([mockExtension]);
            mockCheckForExtensionUpdate.mockResolvedValueOnce(ExtensionUpdateState.UPDATE_AVAILABLE);
            mockUpdateExtension.mockResolvedValueOnce({
                name: 'test-extension',
                originalVersion: '1.0.0',
                updatedVersion: '2.0.0',
            });
            await handleUpdate({ name: 'test-extension' });
            expect(mockUpdateExtension).toHaveBeenCalledWith(mockExtension, ExtensionUpdateState.UPDATE_AVAILABLE, expect.any(Function));
            expect(mockWriteStdoutLine).toHaveBeenCalledWith('Extension "test-extension" successfully updated: 1.0.0 → 2.0.0.');
        });
        it('should show up to date message when versions are the same after update', async () => {
            const mockExtension = {
                name: 'test-extension',
                installMetadata: { source: 'test' },
            };
            mockGetLoadedExtensions.mockReturnValueOnce([mockExtension]);
            mockCheckForExtensionUpdate.mockResolvedValueOnce(ExtensionUpdateState.UPDATE_AVAILABLE);
            mockUpdateExtension.mockResolvedValueOnce({
                name: 'test-extension',
                originalVersion: '1.0.0',
                updatedVersion: '1.0.0',
            });
            await handleUpdate({ name: 'test-extension' });
            expect(mockWriteStdoutLine).toHaveBeenCalledWith('Extension "test-extension" is already up to date.');
        });
        it('should handle errors during update', async () => {
            const mockExtension = {
                name: 'test-extension',
                installMetadata: { source: 'test' },
            };
            mockGetLoadedExtensions.mockReturnValueOnce([mockExtension]);
            mockCheckForExtensionUpdate.mockRejectedValueOnce(new Error('Update check failed'));
            await handleUpdate({ name: 'test-extension' });
            expect(mockWriteStderrLine).toHaveBeenCalledWith('Update check failed');
        });
    });
    describe('update all', () => {
        it('should show message when no extensions to update', async () => {
            mockCheckForAllExtensionUpdates.mockResolvedValueOnce(undefined);
            mockUpdateAllUpdatableExtensions.mockResolvedValueOnce([]);
            await handleUpdate({ all: true });
            expect(mockWriteStdoutLine).toHaveBeenCalledWith('No extensions to update.');
        });
        it('should update all extensions with updates available', async () => {
            mockCheckForAllExtensionUpdates.mockResolvedValueOnce(undefined);
            mockUpdateAllUpdatableExtensions.mockResolvedValueOnce([
                {
                    name: 'extension-1',
                    originalVersion: '1.0.0',
                    updatedVersion: '2.0.0',
                },
                {
                    name: 'extension-2',
                    originalVersion: '1.0.0',
                    updatedVersion: '1.5.0',
                },
            ]);
            await handleUpdate({ all: true });
            expect(mockWriteStdoutLine).toHaveBeenCalledWith('Extension "extension-1" successfully updated: 1.0.0 → 2.0.0.\n' +
                'Extension "extension-2" successfully updated: 1.0.0 → 1.5.0.');
        });
        it('should filter out extensions with same version after update', async () => {
            mockCheckForAllExtensionUpdates.mockResolvedValueOnce(undefined);
            mockUpdateAllUpdatableExtensions.mockResolvedValueOnce([
                {
                    name: 'extension-1',
                    originalVersion: '1.0.0',
                    updatedVersion: '2.0.0',
                },
                {
                    name: 'extension-2',
                    originalVersion: '1.0.0',
                    updatedVersion: '1.0.0',
                },
            ]);
            await handleUpdate({ all: true });
            expect(mockWriteStdoutLine).toHaveBeenCalledWith('Extension "extension-1" successfully updated: 1.0.0 → 2.0.0.');
        });
        it('should handle errors during update all', async () => {
            mockCheckForAllExtensionUpdates.mockRejectedValueOnce(new Error('Update all failed'));
            await handleUpdate({ all: true });
            expect(mockWriteStderrLine).toHaveBeenCalledWith('Update all failed');
        });
    });
});
//# sourceMappingURL=update.test.js.map