1- import { describe , it , expect } from 'vitest' ;
1+ import { describe , it , expect , vi , afterEach } from 'vitest' ;
22import {
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
1527describe ( 'PERFORMANCE_DASHBOARD_TYPES' , ( ) => {
1628 it ( 'contains exactly 5 dashboard types' , ( ) => {
@@ -104,7 +116,7 @@ describe('validateDateRange', () => {
104116describe ( '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