Skip to content

Commit

Permalink
UIE-204 Narrow Ajax Usage pt23
Browse files Browse the repository at this point in the history
- narrow Ajax() usage within some of src/libs/ajax area to call Ajax().SubAreaX directly.
- improve mock types where possible
  • Loading branch information
msilva-broad committed Jan 28, 2025
1 parent 6606b54 commit 1c71bbe
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 319 deletions.
165 changes: 42 additions & 123 deletions src/libs/ajax/analysis-providers/AnalysisProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,23 +59,12 @@ describe('AnalysisProvider - listAnalyses', () => {
describe('AnalysisProvider - copyAnalysis', () => {
it('handles GCP workspace', async () => {
// Arrange
const mockBuckets: Partial<AjaxBucketsContract> = {
analysis: jest.fn(),
};
const watchCopy = jest.fn();
asMockedFn((mockBuckets as AjaxBucketsContract).analysis).mockImplementation(() => {
const mockAnalysisContract: Partial<AjaxBucketsAnalysisContract> = {
copy: watchCopy,
};
const analysisContract = mockAnalysisContract as AjaxBucketsAnalysisContract;
asMockedFn(analysisContract.copy).mockResolvedValue(undefined);
return analysisContract;
});
const analysis: MockedFn<GoogleStorageContract['analysis']> = jest.fn();
const watchCopy: MockedFn<AjaxBucketsAnalysisContract['copy']> = jest.fn();
watchCopy.mockResolvedValue(undefined);

const mockAjax: Partial<AjaxContract> = {
Buckets: mockBuckets as AjaxBucketsContract,
};
asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract);
analysis.mockReturnValue(partial<AjaxBucketsAnalysisContract>({ copy: watchCopy }));
asMockedFn(GoogleStorage).mockReturnValue(partial<GoogleStorageContract>({ analysis }));

const workspaceInfo: Partial<WorkspaceInfo> = {
googleProject: 'GoogleProject123',
Expand All @@ -97,36 +86,20 @@ describe('AnalysisProvider - copyAnalysis', () => {

// Assert
expect(result).toEqual(undefined);
expect(mockBuckets.analysis).toBeCalledTimes(1);
expect(mockBuckets.analysis).toBeCalledWith(
'GoogleProject123',
'Bucket123',
'PrintName123.jpt',
runtimeToolLabels.Jupyter
);
expect(analysis).toBeCalledTimes(1);
expect(analysis).toBeCalledWith('GoogleProject123', 'Bucket123', 'PrintName123.jpt', runtimeToolLabels.Jupyter);
expect(watchCopy).toBeCalledTimes(1);
expect(watchCopy).toBeCalledWith('NewName123.jpt', 'TargetBucket456', false);
});

it('handles Azure workspace', async () => {
// Arrange
const mockAzureStorage: Partial<AjaxAzureStorageContract> = {
blob: jest.fn(),
};
const watchCopy = jest.fn();
asMockedFn((mockAzureStorage as AjaxAzureStorageContract).blob).mockImplementation(() => {
const mockBlobContract: Partial<AjaxAzureStorageBlobContract> = {
copy: watchCopy,
};
const blobContract = mockBlobContract as AjaxAzureStorageBlobContract;
asMockedFn(blobContract.copy).mockResolvedValue(undefined);
return blobContract;
});
const blob: MockedFn<AzureStorageContract['blob']> = jest.fn();
const watchCopy: MockedFn<AjaxAzureStorageBlobContract['copy']> = jest.fn();
watchCopy.mockResolvedValue(undefined);

const mockAjax: Partial<AjaxContract> = {
AzureStorage: mockAzureStorage as AjaxAzureStorageContract,
};
asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract);
blob.mockReturnValue(partial<AjaxAzureStorageBlobContract>({ copy: watchCopy }));
asMockedFn(AzureStorage).mockReturnValue(partial<AzureStorageContract>({ blob }));

const workspaceInfo: Partial<WorkspaceInfo> = {
workspaceId: 'Workspace123',
Expand All @@ -147,8 +120,8 @@ describe('AnalysisProvider - copyAnalysis', () => {

// Assert
expect(result).toEqual(undefined);
expect(mockAzureStorage.blob).toBeCalledTimes(1);
expect(mockAzureStorage.blob).toBeCalledWith('Workspace123', 'PrintName123.jpt');
expect(blob).toBeCalledTimes(1);
expect(blob).toBeCalledWith('Workspace123', 'PrintName123.jpt');
expect(watchCopy).toBeCalledTimes(1);
expect(watchCopy).toBeCalledWith('NewName123', 'Workspace456');
});
Expand All @@ -157,23 +130,12 @@ describe('AnalysisProvider - copyAnalysis', () => {
describe('AnalysisProvider - createAnalysis', () => {
it('handles GCP workspace', async () => {
// Arrange
const mockBuckets: Partial<AjaxBucketsContract> = {
analysis: jest.fn(),
};
const watchCreate = jest.fn();
asMockedFn((mockBuckets as AjaxBucketsContract).analysis).mockImplementation(() => {
const mockAnalysisContract: Partial<AjaxBucketsAnalysisContract> = {
create: watchCreate,
};
const analysisContract = mockAnalysisContract as AjaxBucketsAnalysisContract;
asMockedFn(analysisContract.create).mockResolvedValue(undefined);
return analysisContract;
});
const analysis: MockedFn<GoogleStorageContract['analysis']> = jest.fn();
const watchCreate: MockedFn<AjaxBucketsAnalysisContract['create']> = jest.fn();
watchCreate.mockResolvedValue(undefined);

const mockAjax: Partial<AjaxContract> = {
Buckets: mockBuckets as AjaxBucketsContract,
};
asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract);
analysis.mockReturnValue(partial<AjaxBucketsAnalysisContract>({ create: watchCreate }));
asMockedFn(GoogleStorage).mockReturnValue(partial<GoogleStorageContract>({ analysis }));

const workspaceInfo: Partial<WorkspaceInfo> = {
googleProject: 'GoogleProject123',
Expand All @@ -194,36 +156,20 @@ describe('AnalysisProvider - createAnalysis', () => {
// contents: any, signal?: AbortSignal) => Promise<void>
// Assert
expect(result).toEqual(undefined);
expect(mockBuckets.analysis).toBeCalledTimes(1);
expect(mockBuckets.analysis).toBeCalledWith(
'GoogleProject123',
'Bucket123',
'PrintName123.ipynb',
runtimeToolLabels.Jupyter
);
expect(analysis).toBeCalledTimes(1);
expect(analysis).toBeCalledWith('GoogleProject123', 'Bucket123', 'PrintName123.ipynb', runtimeToolLabels.Jupyter);
expect(watchCreate).toBeCalledTimes(1);
expect(watchCreate).toBeCalledWith('MyIpynbContents');
});

it('handles Azure workspace', async () => {
// Arrange
const mockAzureStorage: Partial<AjaxAzureStorageContract> = {
blob: jest.fn(),
};
const watchCreate = jest.fn();
asMockedFn((mockAzureStorage as AjaxAzureStorageContract).blob).mockImplementation(() => {
const mockBlobContract: Partial<AjaxAzureStorageBlobContract> = {
create: watchCreate,
};
const blobContract = mockBlobContract as AjaxAzureStorageBlobContract;
asMockedFn(blobContract.create).mockResolvedValue(undefined);
return blobContract;
});
const blob: MockedFn<AzureStorageContract['blob']> = jest.fn();
const watchCreate: MockedFn<AjaxAzureStorageBlobContract['create']> = jest.fn();
watchCreate.mockResolvedValue(undefined);

const mockAjax: Partial<AjaxContract> = {
AzureStorage: mockAzureStorage as AjaxAzureStorageContract,
};
asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract);
blob.mockReturnValue(partial<AjaxAzureStorageBlobContract>({ create: watchCreate }));
asMockedFn(AzureStorage).mockReturnValue(partial<AzureStorageContract>({ blob }));

const workspaceInfo: Partial<WorkspaceInfo> = {
workspaceId: 'Workspace123',
Expand All @@ -240,8 +186,8 @@ describe('AnalysisProvider - createAnalysis', () => {

// Assert
expect(result).toEqual(undefined);
expect(mockAzureStorage.blob).toBeCalledTimes(1);
expect(mockAzureStorage.blob).toBeCalledWith('Workspace123', 'PrintName123.ipynb');
expect(blob).toBeCalledTimes(1);
expect(blob).toBeCalledWith('Workspace123', 'PrintName123.ipynb');
expect(watchCreate).toBeCalledTimes(1);
expect(watchCreate).toBeCalledWith('MyIpynbContents');
});
Expand All @@ -250,23 +196,12 @@ describe('AnalysisProvider - createAnalysis', () => {
describe('AnalysisProvider - deleteAnalysis', () => {
it('handles GCP workspace', async () => {
// Arrange
const mockBuckets: Partial<AjaxBucketsContract> = {
analysis: jest.fn(),
};
const watchDelete = jest.fn();
asMockedFn((mockBuckets as AjaxBucketsContract).analysis).mockImplementation(() => {
const mockAnalysisContract: Partial<AjaxBucketsAnalysisContract> = {
delete: watchDelete,
};
const analysisContract = mockAnalysisContract as AjaxBucketsAnalysisContract;
asMockedFn(analysisContract.delete).mockResolvedValue(undefined);
return analysisContract;
});
const analysis: MockedFn<GoogleStorageContract['analysis']> = jest.fn();
const watchDelete: MockedFn<AjaxBucketsAnalysisContract['delete']> = jest.fn();
watchDelete.mockResolvedValue(undefined);

const mockAjax: Partial<AjaxContract> = {
Buckets: mockBuckets as AjaxBucketsContract,
};
asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract);
analysis.mockReturnValue(partial<AjaxBucketsAnalysisContract>({ delete: watchDelete }));
asMockedFn(GoogleStorage).mockReturnValue(partial<GoogleStorageContract>({ analysis }));

const workspaceInfo: Partial<WorkspaceInfo> = {
googleProject: 'GoogleProject123',
Expand All @@ -285,35 +220,19 @@ describe('AnalysisProvider - deleteAnalysis', () => {
// contents: any, signal?: AbortSignal) => Promise<void>
// Assert
expect(result).toEqual(undefined);
expect(mockBuckets.analysis).toBeCalledTimes(1);
expect(mockBuckets.analysis).toBeCalledWith(
'GoogleProject123',
'Bucket123',
'PrintName123.ipynb',
runtimeToolLabels.Jupyter
);
expect(analysis).toBeCalledTimes(1);
expect(analysis).toBeCalledWith('GoogleProject123', 'Bucket123', 'PrintName123.ipynb', runtimeToolLabels.Jupyter);
expect(watchDelete).toBeCalledTimes(1);
});

it('handles Azure workspace', async () => {
// Arrange
const mockAzureStorage: Partial<AjaxAzureStorageContract> = {
blob: jest.fn(),
};
const watchDelete = jest.fn();
asMockedFn((mockAzureStorage as AjaxAzureStorageContract).blob).mockImplementation(() => {
const mockBlobContract: Partial<AjaxAzureStorageBlobContract> = {
delete: watchDelete,
};
const blobContract = mockBlobContract as AjaxAzureStorageBlobContract;
asMockedFn(blobContract.delete).mockResolvedValue(undefined);
return blobContract;
});
const blob: MockedFn<AzureStorageContract['blob']> = jest.fn();
const watchDelete: MockedFn<AjaxAzureStorageBlobContract['delete']> = jest.fn();
watchDelete.mockResolvedValue(undefined);

const mockAjax: Partial<AjaxContract> = {
AzureStorage: mockAzureStorage as AjaxAzureStorageContract,
};
asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract);
blob.mockReturnValue(partial<AjaxAzureStorageBlobContract>({ delete: watchDelete }));
asMockedFn(AzureStorage).mockReturnValue(partial<AzureStorageContract>({ blob }));

const workspaceInfo: Partial<WorkspaceInfo> = {
workspaceId: 'Workspace123',
Expand All @@ -328,8 +247,8 @@ describe('AnalysisProvider - deleteAnalysis', () => {

// Assert
expect(result).toEqual(undefined);
expect(mockAzureStorage.blob).toBeCalledTimes(1);
expect(mockAzureStorage.blob).toBeCalledWith('Workspace123', 'PrintName123.ipynb');
expect(blob).toBeCalledTimes(1);
expect(blob).toBeCalledWith('Workspace123', 'PrintName123.ipynb');
expect(watchDelete).toBeCalledTimes(1);
});
});
50 changes: 22 additions & 28 deletions src/libs/ajax/leonardo/providers/LeoAppProvider.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { Ajax } from 'src/libs/ajax';
import { AppAjaxContract, AppsAjaxContract } from 'src/libs/ajax/leonardo/Apps';
import { asMockedFn } from 'src/testing/test-utils';
import { AppAjaxContract, Apps, AppsAjaxContract } from 'src/libs/ajax/leonardo/Apps';
import { asMockedFn, partial } from 'src/testing/test-utils';
import { defaultAzureWorkspace } from 'src/testing/workspace-fixtures';

import { AppBasics, leoAppProvider } from './LeoAppProvider';

jest.mock('src/libs/ajax');
jest.mock('src/libs/ajax/leonardo/Apps');

type AjaxContract = ReturnType<typeof Ajax>;
type AppNeeds = Pick<AppAjaxContract, 'delete' | 'pause' | 'details'>;
type AppsNeeds = Pick<AppsAjaxContract, 'app' | 'listWithoutProject' | 'deleteAppV2' | 'getAppV2'>;

Expand All @@ -16,10 +14,9 @@ interface AjaxMockNeeds {
app: AppNeeds;
}
/**
* local test utility - mocks the Ajax super-object and the subset of needed multi-contracts it
* returns with as much type-safety as possible.
* local test utility - sets up mocks for needed ajax data-calls with as much type-saftely as possible.
*
* @return collection of key contract sub-objects for easy
* @return collection of key data-call fns for easy
* mock overrides and/or method spying/assertions
*/
const mockAjaxNeeds = (): AjaxMockNeeds => {
Expand All @@ -28,18 +25,15 @@ const mockAjaxNeeds = (): AjaxMockNeeds => {
pause: jest.fn(),
details: jest.fn(),
};
const mockApp = partialApp as AppAjaxContract;

const partialApps: AppsNeeds = {
app: jest.fn(),
app: jest.fn(() => partial<AppAjaxContract>(partialApp)),
listWithoutProject: jest.fn(),
deleteAppV2: jest.fn(),
getAppV2: jest.fn(),
};
const mockApps = partialApps as AppsAjaxContract;

asMockedFn(mockApps.app).mockReturnValue(mockApp);
asMockedFn(Ajax).mockReturnValue({ Apps: mockApps } as AjaxContract);
asMockedFn(Apps).mockReturnValue(partial<AppsAjaxContract>(partialApps));

return {
Apps: partialApps,
Expand All @@ -58,8 +52,8 @@ describe('leoAppProvider', () => {
const result = await leoAppProvider.listWithoutProject({ arg: '1' }, { signal });

// Assert;
expect(Ajax).toBeCalledTimes(1);
expect(Ajax).toBeCalledWith(signal);
expect(Apps).toBeCalledTimes(1);
expect(Apps).toBeCalledWith(signal);
expect(ajaxMock.Apps.listWithoutProject).toBeCalledTimes(1);
expect(ajaxMock.Apps.listWithoutProject).toBeCalledWith({ arg: '1' });
expect(result).toEqual([]);
Expand All @@ -83,8 +77,8 @@ describe('leoAppProvider', () => {
void (await leoAppProvider.pause(app, { signal: abort.signal }));

// Assert;
expect(Ajax).toBeCalledTimes(1);
expect(Ajax).toBeCalledWith(abort.signal);
expect(Apps).toBeCalledTimes(1);
expect(Apps).toBeCalledWith(abort.signal);
expect(ajaxMock.Apps.app).toBeCalledTimes(1);
expect(ajaxMock.Apps.app).toBeCalledWith('myGoogleProject', 'myAppName');
expect(ajaxMock.app.pause).toBeCalledTimes(1);
Expand All @@ -108,8 +102,8 @@ describe('leoAppProvider', () => {
void (await leoAppProvider.delete(app, { signal: abort.signal }));

// Assert;
expect(Ajax).toBeCalledTimes(1);
expect(Ajax).toBeCalledWith(abort.signal);
expect(Apps).toBeCalledTimes(1);
expect(Apps).toBeCalledWith(abort.signal);
expect(ajaxMock.Apps.app).toBeCalledTimes(1);
expect(ajaxMock.Apps.app).toBeCalledWith('myGoogleProject', 'myAppName');
expect(ajaxMock.app.delete).toBeCalledTimes(1);
Expand All @@ -134,8 +128,8 @@ describe('leoAppProvider', () => {
void (await leoAppProvider.get(app, { signal: abort.signal }));

// Assert;
expect(Ajax).toBeCalledTimes(1);
expect(Ajax).toBeCalledWith(abort.signal);
expect(Apps).toBeCalledTimes(1);
expect(Apps).toBeCalledWith(abort.signal);
expect(ajaxMock.Apps.app).toBeCalledTimes(1);
expect(ajaxMock.Apps.app).toBeCalledWith('myGoogleProject', 'myAppName');
expect(ajaxMock.app.details).toBeCalledTimes(1);
Expand All @@ -162,7 +156,7 @@ describe('leoAppProvider', () => {

// Assert
await expect(shouldThrow()).rejects.toEqual(new Error('Pausing apps is not supported for azure'));
expect(Ajax).toBeCalledTimes(0);
expect(Apps).toBeCalledTimes(0);
});

it('handles delete app call', async () => {
Expand All @@ -182,8 +176,8 @@ describe('leoAppProvider', () => {
void (await leoAppProvider.delete(app, { signal: abort.signal }));

// Assert;
expect(Ajax).toBeCalledTimes(1);
expect(Ajax).toBeCalledWith(abort.signal);
expect(Apps).toBeCalledTimes(1);
expect(Apps).toBeCalledWith(abort.signal);
expect(ajaxMock.Apps.deleteAppV2).toBeCalledTimes(1);
expect(ajaxMock.Apps.deleteAppV2).toBeCalledWith(app.appName, app.workspaceId);
});
Expand All @@ -210,7 +204,7 @@ describe('leoAppProvider', () => {
`Deleting apps is currently only supported for azure or google apps. Azure apps must have a workspace id. App: ${app.appName} workspaceId: null`
)
);
expect(Ajax).toBeCalledTimes(0);
expect(Apps).toBeCalledTimes(0);
});

it('handles get app call', async () => {
Expand All @@ -230,8 +224,8 @@ describe('leoAppProvider', () => {
void (await leoAppProvider.get(app, { signal: abort.signal }));

// Assert;
expect(Ajax).toBeCalledTimes(1);
expect(Ajax).toBeCalledWith(abort.signal);
expect(Apps).toBeCalledTimes(1);
expect(Apps).toBeCalledWith(abort.signal);
expect(ajaxMock.Apps.getAppV2).toBeCalledTimes(1);
expect(ajaxMock.Apps.getAppV2).toBeCalledWith(app.appName, app.workspaceId);
});
Expand All @@ -258,7 +252,7 @@ describe('leoAppProvider', () => {
`Getting apps is currently only supported for azure or google apps. Azure apps must have a workspace id. App: ${app.appName} workspaceId: null`
)
);
expect(Ajax).toBeCalledTimes(0);
expect(Apps).toBeCalledTimes(0);
});
});
});
Loading

0 comments on commit 1c71bbe

Please sign in to comment.