Skip to content

Commit da9802c

Browse files
authored
feat #119: acid test performance module — apiConfig wiring, improved errors, expanded tests (#155)
1 parent 4632b6e commit da9802c

File tree

2 files changed

+233
-18
lines changed

2 files changed

+233
-18
lines changed

packages/core/src/__tests__/bloomreachPerformance.test.ts

Lines changed: 194 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { describe, it, expect } from 'vitest';
1+
import { describe, it, expect, vi, afterEach } from 'vitest';
22
import {
33
PERFORMANCE_DASHBOARD_TYPES,
44
CHANNEL_TYPES,
@@ -11,6 +11,18 @@ import {
1111
buildProjectHealthUrl,
1212
BloomreachPerformanceService,
1313
} from '../index.js';
14+
import type { BloomreachApiConfig } from '../bloomreachApiClient.js';
15+
16+
const TEST_API_CONFIG: BloomreachApiConfig = {
17+
projectToken: 'test-token-123',
18+
apiKeyId: 'key-id',
19+
apiSecret: 'key-secret',
20+
baseUrl: 'https://api.test.com',
21+
};
22+
23+
afterEach(() => {
24+
vi.restoreAllMocks();
25+
});
1426

1527
describe('PERFORMANCE_DASHBOARD_TYPES', () => {
1628
it('contains exactly 5 dashboard types', () => {
@@ -104,7 +116,7 @@ describe('validateDateRange', () => {
104116
describe('validateChannel', () => {
105117
it.each(['email', 'sms', 'push', 'whatsapp', 'weblayer', 'in_app_message'] as const)(
106118
'accepts "%s"',
107-
(channel) => {
119+
(channel: (typeof CHANNEL_TYPES)[number]) => {
108120
expect(validateChannel(channel)).toBe(channel);
109121
},
110122
);
@@ -205,10 +217,40 @@ describe('BloomreachPerformanceService', () => {
205217
it('throws for whitespace-only project', () => {
206218
expect(() => new BloomreachPerformanceService(' ')).toThrow('must not be empty');
207219
});
220+
221+
it('accepts apiConfig as second parameter', () => {
222+
const service = new BloomreachPerformanceService('test', TEST_API_CONFIG);
223+
expect(service).toBeInstanceOf(BloomreachPerformanceService);
224+
});
225+
226+
it('exposes projectPerformanceUrl when constructed with apiConfig', () => {
227+
const service = new BloomreachPerformanceService('test', TEST_API_CONFIG);
228+
expect(service.projectPerformanceUrl).toBe('/p/test/overview/performance-dashboards/project');
229+
});
230+
231+
it('encodes unicode project name in constructor URL', () => {
232+
const service = new BloomreachPerformanceService('projekt åäö');
233+
expect(service.projectPerformanceUrl).toBe(
234+
'/p/projekt%20%C3%A5%C3%A4%C3%B6/overview/performance-dashboards/project',
235+
);
236+
});
237+
238+
it('encodes hash in constructor URL', () => {
239+
const service = new BloomreachPerformanceService('my#project');
240+
expect(service.projectPerformanceUrl).toBe('/p/my%23project/overview/performance-dashboards/project');
241+
});
242+
243+
it('encodes slashes in constructor project URL', () => {
244+
const service = new BloomreachPerformanceService('org/project');
245+
expect(service.projectPerformanceUrl).toBe(
246+
'/p/org%2Fproject/overview/performance-dashboards/project',
247+
);
248+
});
208249
});
209250

210251
describe('URL getters', () => {
211252
const service = new BloomreachPerformanceService('test-proj');
253+
const serviceWithApiConfig = new BloomreachPerformanceService('test-proj', TEST_API_CONFIG);
212254

213255
it('exposes projectPerformanceUrl', () => {
214256
expect(service.projectPerformanceUrl).toBe(
@@ -222,24 +264,49 @@ describe('BloomreachPerformanceService', () => {
222264
);
223265
});
224266

267+
it('exposes channelPerformanceUrl when constructed with apiConfig', () => {
268+
expect(serviceWithApiConfig.channelPerformanceUrl).toBe(
269+
'/p/test-proj/overview/performance-dashboards/channel',
270+
);
271+
});
272+
225273
it('exposes usageUrl', () => {
226274
expect(service.usageUrl).toBe('/p/test-proj/overview/pricing-dashboard-v2');
227275
});
228276

277+
it('exposes usageUrl when constructed with apiConfig', () => {
278+
expect(serviceWithApiConfig.usageUrl).toBe('/p/test-proj/overview/pricing-dashboard-v2');
279+
});
280+
229281
it('exposes overviewUrl', () => {
230282
expect(service.overviewUrl).toBe('/p/test-proj/overview/project');
231283
});
232284

285+
it('exposes overviewUrl when constructed with apiConfig', () => {
286+
expect(serviceWithApiConfig.overviewUrl).toBe('/p/test-proj/overview/project');
287+
});
288+
233289
it('exposes healthUrl', () => {
234290
expect(service.healthUrl).toBe('/p/test-proj/overview/health-dashboard');
235291
});
292+
293+
it('exposes healthUrl when constructed with apiConfig', () => {
294+
expect(serviceWithApiConfig.healthUrl).toBe('/p/test-proj/overview/health-dashboard');
295+
});
236296
});
237297

238298
describe('viewProjectPerformance', () => {
239-
it('throws not-yet-implemented error', async () => {
299+
it('throws no-API-endpoint error', async () => {
240300
const service = new BloomreachPerformanceService('test');
241301
await expect(service.viewProjectPerformance({ project: 'test' })).rejects.toThrow(
242-
'not yet implemented',
302+
'does not provide an endpoint',
303+
);
304+
});
305+
306+
it('throws no-API-endpoint error when service has apiConfig', async () => {
307+
const service = new BloomreachPerformanceService('test', TEST_API_CONFIG);
308+
await expect(service.viewProjectPerformance({ project: 'test' })).rejects.toThrow(
309+
'does not provide an endpoint',
243310
);
244311
});
245312

@@ -259,13 +326,37 @@ describe('BloomreachPerformanceService', () => {
259326
}),
260327
).rejects.toThrow('startDate must be a valid ISO-8601 date');
261328
});
329+
330+
it('throws no-API-endpoint error with valid full input', async () => {
331+
const service = new BloomreachPerformanceService('test');
332+
await expect(
333+
service.viewProjectPerformance({
334+
project: 'test',
335+
dateRange: { startDate: '2025-01-01', endDate: '2025-01-31' },
336+
}),
337+
).rejects.toThrow('does not provide an endpoint');
338+
});
339+
340+
it('throws no-API-endpoint error for trimmed project', async () => {
341+
const service = new BloomreachPerformanceService('test');
342+
await expect(service.viewProjectPerformance({ project: ' test ' })).rejects.toThrow(
343+
'does not provide an endpoint',
344+
);
345+
});
262346
});
263347

264348
describe('viewChannelPerformance', () => {
265-
it('throws not-yet-implemented error', async () => {
349+
it('throws no-API-endpoint error', async () => {
266350
const service = new BloomreachPerformanceService('test');
267351
await expect(service.viewChannelPerformance({ project: 'test' })).rejects.toThrow(
268-
'not yet implemented',
352+
'does not provide an endpoint',
353+
);
354+
});
355+
356+
it('throws no-API-endpoint error when service has apiConfig', async () => {
357+
const service = new BloomreachPerformanceService('test', TEST_API_CONFIG);
358+
await expect(service.viewChannelPerformance({ project: 'test' })).rejects.toThrow(
359+
'does not provide an endpoint',
269360
);
270361
});
271362

@@ -295,13 +386,48 @@ describe('BloomreachPerformanceService', () => {
295386
}),
296387
).rejects.toThrow('channel must be one of');
297388
});
389+
390+
it('throws no-API-endpoint error with valid full input', async () => {
391+
const service = new BloomreachPerformanceService('test');
392+
await expect(
393+
service.viewChannelPerformance({
394+
project: 'test',
395+
dateRange: { startDate: '2025-02-01', endDate: '2025-02-28' },
396+
}),
397+
).rejects.toThrow('does not provide an endpoint');
398+
});
399+
400+
it('throws no-API-endpoint error for trimmed project', async () => {
401+
const service = new BloomreachPerformanceService('test');
402+
await expect(service.viewChannelPerformance({ project: ' test ' })).rejects.toThrow(
403+
'does not provide an endpoint',
404+
);
405+
});
406+
407+
it('throws no-API-endpoint error with valid channel and dateRange', async () => {
408+
const service = new BloomreachPerformanceService('test');
409+
await expect(
410+
service.viewChannelPerformance({
411+
project: 'test',
412+
channel: 'email',
413+
dateRange: { startDate: '2025-01-01', endDate: '2025-01-31' },
414+
}),
415+
).rejects.toThrow('does not provide an endpoint');
416+
});
298417
});
299418

300419
describe('viewBloomreachUsage', () => {
301-
it('throws not-yet-implemented error', async () => {
420+
it('throws no-API-endpoint error', async () => {
302421
const service = new BloomreachPerformanceService('test');
303422
await expect(service.viewBloomreachUsage({ project: 'test' })).rejects.toThrow(
304-
'not yet implemented',
423+
'does not provide an endpoint',
424+
);
425+
});
426+
427+
it('throws no-API-endpoint error when service has apiConfig', async () => {
428+
const service = new BloomreachPerformanceService('test', TEST_API_CONFIG);
429+
await expect(service.viewBloomreachUsage({ project: 'test' })).rejects.toThrow(
430+
'does not provide an endpoint',
305431
);
306432
});
307433

@@ -311,13 +437,34 @@ describe('BloomreachPerformanceService', () => {
311437
'must not be empty',
312438
);
313439
});
440+
441+
it('throws no-API-endpoint error for trimmed project', async () => {
442+
const service = new BloomreachPerformanceService('test');
443+
await expect(service.viewBloomreachUsage({ project: ' test ' })).rejects.toThrow(
444+
'does not provide an endpoint',
445+
);
446+
});
447+
448+
it('throws for whitespace-only project', async () => {
449+
const service = new BloomreachPerformanceService('test');
450+
await expect(service.viewBloomreachUsage({ project: ' ' })).rejects.toThrow(
451+
'must not be empty',
452+
);
453+
});
314454
});
315455

316456
describe('viewProjectOverview', () => {
317-
it('throws not-yet-implemented error', async () => {
457+
it('throws no-API-endpoint error', async () => {
318458
const service = new BloomreachPerformanceService('test');
319459
await expect(service.viewProjectOverview({ project: 'test' })).rejects.toThrow(
320-
'not yet implemented',
460+
'does not provide an endpoint',
461+
);
462+
});
463+
464+
it('throws no-API-endpoint error when service has apiConfig', async () => {
465+
const service = new BloomreachPerformanceService('test', TEST_API_CONFIG);
466+
await expect(service.viewProjectOverview({ project: 'test' })).rejects.toThrow(
467+
'does not provide an endpoint',
321468
);
322469
});
323470

@@ -327,19 +474,54 @@ describe('BloomreachPerformanceService', () => {
327474
'must not be empty',
328475
);
329476
});
477+
478+
it('throws no-API-endpoint error for trimmed project', async () => {
479+
const service = new BloomreachPerformanceService('test');
480+
await expect(service.viewProjectOverview({ project: ' test ' })).rejects.toThrow(
481+
'does not provide an endpoint',
482+
);
483+
});
484+
485+
it('throws for whitespace-only project', async () => {
486+
const service = new BloomreachPerformanceService('test');
487+
await expect(service.viewProjectOverview({ project: ' ' })).rejects.toThrow(
488+
'must not be empty',
489+
);
490+
});
330491
});
331492

332493
describe('viewProjectHealth', () => {
333-
it('throws not-yet-implemented error', async () => {
494+
it('throws no-API-endpoint error', async () => {
334495
const service = new BloomreachPerformanceService('test');
335496
await expect(service.viewProjectHealth({ project: 'test' })).rejects.toThrow(
336-
'not yet implemented',
497+
'does not provide an endpoint',
498+
);
499+
});
500+
501+
it('throws no-API-endpoint error when service has apiConfig', async () => {
502+
const service = new BloomreachPerformanceService('test', TEST_API_CONFIG);
503+
await expect(service.viewProjectHealth({ project: 'test' })).rejects.toThrow(
504+
'does not provide an endpoint',
337505
);
338506
});
339507

340508
it('validates project before throwing', async () => {
341509
const service = new BloomreachPerformanceService('test');
342510
await expect(service.viewProjectHealth({ project: '' })).rejects.toThrow('must not be empty');
343511
});
512+
513+
it('throws no-API-endpoint error for trimmed project', async () => {
514+
const service = new BloomreachPerformanceService('test');
515+
await expect(service.viewProjectHealth({ project: ' test ' })).rejects.toThrow(
516+
'does not provide an endpoint',
517+
);
518+
});
519+
520+
it('throws for whitespace-only project', async () => {
521+
const service = new BloomreachPerformanceService('test');
522+
await expect(service.viewProjectHealth({ project: ' ' })).rejects.toThrow(
523+
'must not be empty',
524+
);
525+
});
344526
});
345527
});

0 commit comments

Comments
 (0)