Skip to content

Commit 9dbc566

Browse files
committed
Add unit tests for Asset Service
1 parent 06d7b69 commit 9dbc566

File tree

6 files changed

+392
-0
lines changed

6 files changed

+392
-0
lines changed
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
// ===== IMPORTS =====
2+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
3+
import { AssetService } from '../../../../src/services/orchestrator/assets';
4+
import { ApiClient } from '../../../../src/core/http/api-client';
5+
import { PaginationHelpers } from '../../../../src/utils/pagination/helpers';
6+
import {
7+
createMockRawAsset,
8+
createMockTransformedAssetCollection
9+
} from '../../../utils/mocks/assets';
10+
import { createServiceTestDependencies, createMockApiClient } from '../../../utils/setup';
11+
import { createMockError } from '../../../utils/mocks/core';
12+
import {
13+
AssetGetAllOptions,
14+
AssetGetByIdOptions,
15+
AssetValueType,
16+
AssetValueScope,
17+
AssetGetResponse
18+
} from '../../../../src/models/orchestrator/assets.types';
19+
import { PaginatedResponse } from '../../../../src/utils/pagination';
20+
import { ASSET_TEST_CONSTANTS } from '../../../utils/constants/assets';
21+
import { TEST_CONSTANTS } from '../../../utils/constants/common';
22+
import { ASSET_ENDPOINTS } from '../../../../src/utils/constants/endpoints';
23+
import { FOLDER_ID } from '../../../../src/utils/constants/headers';
24+
25+
// ===== MOCKING =====
26+
// Mock the dependencies
27+
vi.mock('../../../../src/core/http/api-client');
28+
29+
// Import mock objects using vi.hoisted() - this ensures they're available before vi.mock() calls
30+
const mocks = vi.hoisted(() => {
31+
// Import/re-export the mock utilities from core
32+
return import('../../../utils/mocks/core');
33+
});
34+
35+
// Setup mocks at module level
36+
// NOTE: We do NOT mock transformData
37+
vi.mock('../../../../src/utils/pagination/helpers', async () => (await mocks).mockPaginationHelpers);
38+
39+
// ===== TEST SUITE =====
40+
describe('AssetService Unit Tests', () => {
41+
let assetService: AssetService;
42+
let mockApiClient: any;
43+
44+
beforeEach(() => {
45+
// Create mock instances using centralized setup
46+
const { config, executionContext, tokenManager } = createServiceTestDependencies();
47+
mockApiClient = createMockApiClient();
48+
49+
// Mock the ApiClient constructor
50+
vi.mocked(ApiClient).mockImplementation(() => mockApiClient);
51+
52+
// Reset pagination helpers mock before each test
53+
vi.mocked(PaginationHelpers.getAll).mockReset();
54+
55+
assetService = new AssetService(config, executionContext, tokenManager);
56+
});
57+
58+
afterEach(() => {
59+
vi.clearAllMocks();
60+
});
61+
62+
describe('getById', () => {
63+
it('should get asset by ID successfully with all fields mapped correctly', async () => {
64+
const mockAsset = createMockRawAsset();
65+
66+
mockApiClient.get.mockResolvedValue(mockAsset);
67+
68+
const result = await assetService.getById(
69+
ASSET_TEST_CONSTANTS.ASSET_ID,
70+
TEST_CONSTANTS.FOLDER_ID
71+
);
72+
73+
// Verify the result
74+
expect(result).toBeDefined();
75+
expect(result.id).toBe(ASSET_TEST_CONSTANTS.ASSET_ID);
76+
expect(result.name).toBe(ASSET_TEST_CONSTANTS.ASSET_NAME);
77+
expect(result.key).toBe(ASSET_TEST_CONSTANTS.ASSET_KEY);
78+
expect(result.valueType).toBe(AssetValueType.DBConnectionString,);
79+
expect(result.valueScope).toBe(AssetValueScope.Global,);
80+
81+
// Verify the API call has correct endpoint and headers
82+
expect(mockApiClient.get).toHaveBeenCalledWith(
83+
ASSET_ENDPOINTS.GET_BY_ID(ASSET_TEST_CONSTANTS.ASSET_ID),
84+
expect.objectContaining({
85+
headers: expect.objectContaining({
86+
[FOLDER_ID]: TEST_CONSTANTS.FOLDER_ID.toString()
87+
})
88+
})
89+
);
90+
91+
// Verify field transformations
92+
// CreationTime -> createdTime
93+
expect(result.createdTime).toBe(ASSET_TEST_CONSTANTS.CREATED_TIME);
94+
expect((result as any).CreationTime).toBeUndefined(); // Original field should be removed
95+
96+
// LastModificationTime -> lastModifiedTime
97+
expect(result.lastModifiedTime).toBe(ASSET_TEST_CONSTANTS.LAST_MODIFIED_TIME);
98+
expect((result as any).LastModificationTime).toBeUndefined(); // Original field should be removed
99+
});
100+
101+
it('should get asset with options successfully', async () => {
102+
const mockAsset = createMockRawAsset();
103+
mockApiClient.get.mockResolvedValue(mockAsset);
104+
105+
const options: AssetGetByIdOptions = {
106+
expand: ASSET_TEST_CONSTANTS.ODATA_EXPAND_KEY_VALUE_LIST,
107+
select: ASSET_TEST_CONSTANTS.ODATA_SELECT_FIELDS
108+
};
109+
110+
const result = await assetService.getById(
111+
ASSET_TEST_CONSTANTS.ASSET_ID,
112+
TEST_CONSTANTS.FOLDER_ID,
113+
options
114+
);
115+
116+
// Verify the result
117+
expect(result).toBeDefined();
118+
expect(result.id).toBe(ASSET_TEST_CONSTANTS.ASSET_ID);
119+
expect(result.name).toBe(ASSET_TEST_CONSTANTS.ASSET_NAME);
120+
expect(result.key).toBe(ASSET_TEST_CONSTANTS.ASSET_KEY);
121+
122+
// Verify API call has options with OData prefix
123+
expect(mockApiClient.get).toHaveBeenCalledWith(
124+
ASSET_ENDPOINTS.GET_BY_ID(ASSET_TEST_CONSTANTS.ASSET_ID),
125+
expect.objectContaining({
126+
params: expect.objectContaining({
127+
'$expand': ASSET_TEST_CONSTANTS.ODATA_EXPAND_KEY_VALUE_LIST,
128+
'$select': ASSET_TEST_CONSTANTS.ODATA_SELECT_FIELDS
129+
})
130+
})
131+
);
132+
});
133+
134+
it('should handle API errors', async () => {
135+
const error = createMockError(ASSET_TEST_CONSTANTS.ERROR_ASSET_NOT_FOUND);
136+
mockApiClient.get.mockRejectedValue(error);
137+
138+
await expect(assetService.getById(
139+
ASSET_TEST_CONSTANTS.ASSET_ID,
140+
TEST_CONSTANTS.FOLDER_ID
141+
)).rejects.toThrow(ASSET_TEST_CONSTANTS.ERROR_ASSET_NOT_FOUND);
142+
});
143+
});
144+
145+
describe('getAll', () => {
146+
it('should return all assets without pagination options', async () => {
147+
const mockResponse = createMockTransformedAssetCollection();
148+
149+
vi.mocked(PaginationHelpers.getAll).mockResolvedValue(mockResponse);
150+
151+
const result = await assetService.getAll();
152+
153+
// Verify PaginationHelpers.getAll was called
154+
expect(PaginationHelpers.getAll).toHaveBeenCalledWith(
155+
expect.objectContaining({
156+
serviceAccess: expect.any(Object),
157+
getEndpoint: expect.toSatisfy((fn: Function) => fn() === ASSET_ENDPOINTS.GET_ALL),
158+
transformFn: expect.any(Function),
159+
pagination: expect.any(Object)
160+
}),
161+
undefined
162+
);
163+
164+
expect(result).toEqual(mockResponse);
165+
});
166+
167+
it('should return assets filtered by folder ID', async () => {
168+
const mockResponse = createMockTransformedAssetCollection();
169+
170+
vi.mocked(PaginationHelpers.getAll).mockResolvedValue(mockResponse);
171+
172+
const options: AssetGetAllOptions = {
173+
folderId: TEST_CONSTANTS.FOLDER_ID
174+
};
175+
176+
const result = await assetService.getAll(options);
177+
178+
// Verify PaginationHelpers.getAll was called with folder options
179+
expect(PaginationHelpers.getAll).toHaveBeenCalledWith(
180+
expect.objectContaining({
181+
serviceAccess: expect.any(Object),
182+
getEndpoint: expect.toSatisfy((fn: Function) => fn(TEST_CONSTANTS.FOLDER_ID) === ASSET_ENDPOINTS.GET_BY_FOLDER),
183+
transformFn: expect.any(Function),
184+
pagination: expect.any(Object)
185+
}),
186+
expect.objectContaining({
187+
folderId: TEST_CONSTANTS.FOLDER_ID
188+
})
189+
);
190+
191+
expect(result).toEqual(mockResponse);
192+
});
193+
194+
it('should return paginated assets when pagination options provided', async () => {
195+
const mockResponse = createMockTransformedAssetCollection(100, {
196+
totalCount: 100,
197+
hasNextPage: true,
198+
nextCursor: TEST_CONSTANTS.NEXT_CURSOR,
199+
previousCursor: null,
200+
currentPage: 1,
201+
totalPages: 10
202+
});
203+
204+
vi.mocked(PaginationHelpers.getAll).mockResolvedValue(mockResponse);
205+
206+
const options: AssetGetAllOptions = {
207+
pageSize: TEST_CONSTANTS.PAGE_SIZE
208+
};
209+
210+
const result = await assetService.getAll(options) as PaginatedResponse<AssetGetResponse>;
211+
212+
// Verify PaginationHelpers.getAll was called with pagination options
213+
expect(PaginationHelpers.getAll).toHaveBeenCalledWith(
214+
expect.any(Object),
215+
expect.objectContaining({
216+
pageSize: TEST_CONSTANTS.PAGE_SIZE
217+
})
218+
);
219+
220+
expect(result).toEqual(mockResponse);
221+
expect(result.hasNextPage).toBe(true);
222+
expect(result.nextCursor).toBe(TEST_CONSTANTS.NEXT_CURSOR);
223+
});
224+
225+
it('should handle API errors', async () => {
226+
const error = createMockError(TEST_CONSTANTS.ERROR_MESSAGE);
227+
vi.mocked(PaginationHelpers.getAll).mockRejectedValue(error);
228+
229+
await expect(assetService.getAll()).rejects.toThrow(TEST_CONSTANTS.ERROR_MESSAGE);
230+
});
231+
});
232+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './assets.test';

tests/utils/constants/assets.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Asset service test constants
3+
* Asset-specific constants only
4+
*/
5+
6+
export const ASSET_TEST_CONSTANTS = {
7+
// Asset IDs
8+
ASSET_ID: 123,
9+
10+
// Asset Metadata
11+
ASSET_NAME: 'DatabaseConnection',
12+
ASSET_KEY: '12345678-1234-1234-1234-123456789abc',
13+
ASSET_DESCRIPTION: 'Database connection string for production',
14+
15+
// Asset Values
16+
ASSET_VALUE: 'Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;',
17+
18+
// Timestamps
19+
CREATED_TIME: '2023-10-15T10:00:00Z',
20+
LAST_MODIFIED_TIME: '2023-10-20T15:30:00Z',
21+
22+
// User IDs
23+
LAST_MODIFIER_USER_ID: 102,
24+
25+
// Key-Value List Items
26+
KEY_VALUE_ITEM_1_KEY: 'environment',
27+
KEY_VALUE_ITEM_1_VALUE: 'production',
28+
KEY_VALUE_ITEM_2_KEY: 'region',
29+
KEY_VALUE_ITEM_2_VALUE: 'us-east-1',
30+
31+
// Error Messages
32+
ERROR_ASSET_NOT_FOUND: 'Asset not found',
33+
34+
// OData Parameters
35+
ODATA_EXPAND_KEY_VALUE_LIST: 'keyValueList',
36+
ODATA_SELECT_FIELDS: 'id,name,value',
37+
} as const;

tests/utils/constants/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export * from './common';
66
export * from './maestro';
77
export * from './tasks';
88
export * from './entities';
9+
export * from './assets';
910

0 commit comments

Comments
 (0)