diff --git a/packages/backend/src/managers/applicationManager.spec.ts b/packages/backend/src/managers/applicationManager.spec.ts index 04321edd4..5ed456963 100644 --- a/packages/backend/src/managers/applicationManager.spec.ts +++ b/packages/backend/src/managers/applicationManager.spec.ts @@ -1,4 +1,4 @@ -import { expect, test, vi } from 'vitest'; +import { type MockInstance, describe, expect, test, vi, beforeEach } from 'vitest'; import { ApplicationManager } from './applicationManager'; import type { RecipeStatusRegistry } from '../registries/RecipeStatusRegistry'; import type { ExtensionContext } from '@podman-desktop/api'; @@ -8,6 +8,8 @@ import fs, { Stats, type Dirent } from 'fs'; import path from 'path'; import type { Recipe } from '@shared/src/models/IRecipe'; import type { ModelInfo } from '@shared/src/models/IModelInfo'; +import type { LocalModelInfo } from '@shared/src/models/ILocalModelInfo'; +import type { RecipeStatusUtils } from '../utils/recipeStatusUtils'; const mocks = vi.hoisted(() => { return { @@ -71,76 +73,133 @@ test('getLocalModels should return models in local directory', () => { ]); }); -test('pullApplication should clone repository and call downloadModelMain', async () => { +describe('pullApplication', () => { + interface mockForPullApplicationOptions { + recipeFolderExists: boolean; + } + const setStatusMock = vi.fn(); const cloneRepositoryMock = vi.fn(); - vi.spyOn(os, 'homedir').mockReturnValue('/home/user'); - vi.spyOn(fs, 'mkdirSync').mockReturnValue(undefined); - vi.spyOn(fs, 'existsSync').mockImplementation((path: string) => { - if (path.endsWith('recipe1')) { + let manager: ApplicationManager; + let getLocalModelsSpy: MockInstance<[], LocalModelInfo[]>; + let downloadModelMainSpy: MockInstance< + [modelId: string, url: string, taskUtil: RecipeStatusUtils, destFileName?: string], + Promise + >; + + beforeEach(() => { + vi.resetAllMocks(); + }) + + function mockForPullApplication(options: mockForPullApplicationOptions) { + vi.spyOn(os, 'homedir').mockReturnValue('/home/user'); + vi.spyOn(fs, 'mkdirSync').mockReturnValue(undefined); + vi.spyOn(fs, 'existsSync').mockImplementation((path: string) => { + if (path.endsWith('recipe1')) { + return options.recipeFolderExists; + } else if (path.endsWith('ai-studio.yaml')) { + return true; + } return false; - } else if (path.endsWith('ai-studio.yaml')) { - return true; - } - return false; - }); - vi.spyOn(fs, 'statSync').mockImplementation((path: string) => { - if (path.endsWith('recipe1')) { - const stat = new Stats(); - stat.isDirectory = () => true; - return stat; - } else if (path.endsWith('ai-studio.yaml')) { - const stat = new Stats(); - stat.isDirectory = () => false; - return stat; + }); + vi.spyOn(fs, 'statSync').mockImplementation((path: string) => { + if (path.endsWith('recipe1')) { + const stat = new Stats(); + stat.isDirectory = () => true; + return stat; + } else if (path.endsWith('ai-studio.yaml')) { + const stat = new Stats(); + stat.isDirectory = () => false; + return stat; + } + }); + vi.spyOn(fs, 'readFileSync').mockImplementation((_path: string) => { + return ''; + }); + mocks.parseYamlMock.mockReturnValue({ + application: { + containers: [], + }, + }); + + manager = new ApplicationManager( + { + cloneRepository: cloneRepositoryMock, + } as unknown as GitManager, + { + setStatus: setStatusMock, + } as unknown as RecipeStatusRegistry, + {} as ExtensionContext, + ); + + getLocalModelsSpy = vi.spyOn(manager, 'getLocalModels'); + getLocalModelsSpy.mockReturnValue([]); + downloadModelMainSpy = vi.spyOn(manager, 'downloadModelMain'); + downloadModelMainSpy.mockResolvedValue(''); + } + + test('pullApplication should clone repository and call downloadModelMain', async () => { + mockForPullApplication({ + recipeFolderExists: false, + }); + + const recipe: Recipe = { + id: 'recipe1', + name: 'Recipe 1', + categories: [], + description: '', + readme: '', + repository: 'repo', + }; + const model: ModelInfo = { + id: 'model1', + description: '', + hw: '', + license: '', + name: 'Model 1', + popularity: 1, + registry: '', + url: '', + }; + + await manager.pullApplication(recipe, model); + if (process.platform === 'win32') { + expect(cloneRepositoryMock).toHaveBeenNthCalledWith( + 1, + 'repo', + '\\home\\user\\podman-desktop\\ai-studio\\recipe1', + ); + } else { + expect(cloneRepositoryMock).toHaveBeenNthCalledWith(1, 'repo', '/home/user/podman-desktop/ai-studio/recipe1'); } + expect(downloadModelMainSpy).toHaveBeenCalledOnce(); }); - vi.spyOn(fs, 'readFileSync').mockImplementation((_path: string) => { - return ''; - }); - mocks.parseYamlMock.mockReturnValue({ - application: { - containers: [], - }, + + test('pullApplication should not clone repository if folder already exists locally', async () => { + mockForPullApplication({ + recipeFolderExists: true, + }); + + const recipe: Recipe = { + id: 'recipe1', + name: 'Recipe 1', + categories: [], + description: '', + readme: '', + repository: 'repo', + }; + const model: ModelInfo = { + id: 'model1', + description: '', + hw: '', + license: '', + name: 'Model 1', + popularity: 1, + registry: '', + url: '', + }; + + await manager.pullApplication(recipe, model); + expect(cloneRepositoryMock).not.toHaveBeenCalled(); }); - const manager = new ApplicationManager( - { - cloneRepository: cloneRepositoryMock, - } as unknown as GitManager, - { - setStatus: setStatusMock, - } as unknown as RecipeStatusRegistry, - {} as ExtensionContext, - ); - - const getLocalModelsSpy = vi.spyOn(manager, 'getLocalModels'); - getLocalModelsSpy.mockReturnValue([]); - const downloadModelMainSpy = vi.spyOn(manager, 'downloadModelMain'); - downloadModelMainSpy.mockResolvedValue(''); - - const recipe: Recipe = { - id: 'recipe1', - name: 'Recipe 1', - categories: [], - description: '', - readme: '', - repository: 'repo', - }; - const model: ModelInfo = { - id: 'model1', - description: '', - hw: '', - license: '', - name: 'Model 1', - popularity: 1, - registry: '', - url: '', - }; - await manager.pullApplication(recipe, model); - if (process.platform === 'win32') { - expect(cloneRepositoryMock).toHaveBeenNthCalledWith(1, 'repo', '\\home\\user\\podman-desktop\\ai-studio\\recipe1'); - } else { - expect(cloneRepositoryMock).toHaveBeenNthCalledWith(1, 'repo', '/home/user/podman-desktop/ai-studio/recipe1'); - } - expect(downloadModelMainSpy).toHaveBeenCalledOnce(); });