Skip to content

Commit

Permalink
tests: more applicationManager.pullApplication tests
Browse files Browse the repository at this point in the history
  • Loading branch information
feloy committed Jan 23, 2024
1 parent be07dd4 commit ccb3533
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 69 deletions.
244 changes: 178 additions & 66 deletions packages/backend/src/managers/applicationManager.spec.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -8,17 +8,30 @@ 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 {
parseYamlMock: vi.fn(),
builImageMock: vi.fn(),
};
});

vi.mock('../models/AIConfig', () => ({
parseYaml: mocks.parseYamlMock,
}));

vi.mock('@podman-desktop/api', () => ({
containerEngine: {
buildImage: mocks.builImageMock,
},
}));

beforeEach(() => {
vi.resetAllMocks();
});

test('appUserDirectory should be under home directory', () => {
vi.spyOn(os, 'homedir').mockReturnValue('/home/user');
const manager = new ApplicationManager({} as GitManager, {} as RecipeStatusRegistry, {} as ExtensionContext);
Expand Down Expand Up @@ -71,76 +84,175 @@ 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<string>
>;

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;
} else if (path.endsWith('contextdir1')) {
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: [
{
name: 'container1',
contextdir: 'contextdir1',
containerfile: 'Containerfile',
},
],
},
});
mocks.builImageMock.mockResolvedValue(undefined);

manager = new ApplicationManager(
{
cloneRepository: cloneRepositoryMock,
} as unknown as GitManager,
{
setStatus: setStatusMock,
} as unknown as RecipeStatusRegistry,
{} as ExtensionContext,
);

getLocalModelsSpy = vi.spyOn(manager, 'getLocalModels');
downloadModelMainSpy = vi.spyOn(manager, 'downloadModelMain');
downloadModelMainSpy.mockResolvedValue('');
}

test('pullApplication should clone repository and call downloadModelMain and buildImage', async () => {
mockForPullApplication({
recipeFolderExists: false,
});
getLocalModelsSpy.mockReturnValue([]);

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();
expect(mocks.builImageMock).toHaveBeenCalledOnce();
});
vi.spyOn(fs, 'readFileSync').mockImplementation((_path: string) => {
return '';

test('pullApplication should not clone repository if folder already exists locally', async () => {
mockForPullApplication({
recipeFolderExists: true,
});
getLocalModelsSpy.mockReturnValue([]);

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();
});
mocks.parseYamlMock.mockReturnValue({
application: {
containers: [],
},

test('pullApplication should not download model if already on disk', async () => {
mockForPullApplication({
recipeFolderExists: true,
});
getLocalModelsSpy.mockReturnValue([
{
id: 'model1',
file: 'model1.file',
},
]);

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();
expect(downloadModelMainSpy).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();
});
10 changes: 7 additions & 3 deletions packages/backend/vitest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ const PACKAGE_ROOT = __dirname;

const config = {
test: {
include: ['**/*.{test,spec}.?(c|m)[jt]s?(x)', '../shared/**/*.{test,spec}.?(c|m)[jt]s?(x)']
},
resolve: {
include: ['**/*.{test,spec}.?(c|m)[jt]s?(x)', '../shared/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
coverage: {
provider: 'v8',
reporter: ['lcov', 'text'],
},
},
resolve: {
alias: {
'@podman-desktop/api': path.resolve(__dirname, '__mocks__/@podman-desktop/api.js'),
'/@/': join(PACKAGE_ROOT, 'src') + '/',
Expand Down

0 comments on commit ccb3533

Please sign in to comment.