@@ -22,6 +22,7 @@ let getPledges: CampaignStoreModule['getPledges'];
2222let getDb : DbModule [ 'getDb' ] ;
2323let getCampaignHistory : EventHistoryModule [ 'getCampaignHistory' ] ;
2424let addPledge : CampaignStoreModule [ 'addPledge' ] ;
25+ let getCampaignAnalytics : CampaignStoreModule [ 'getCampaignAnalytics' ] ;
2526
2627const CREATOR = `G${ 'A' . repeat ( 55 ) } ` ;
2728const CONTRIBUTOR = `G${ 'B' . repeat ( 55 ) } ` ;
@@ -41,6 +42,7 @@ beforeAll(async () => {
4142 getCampaign,
4243 getPledges,
4344 addPledge,
45+ getCampaignAnalytics,
4446 } = await import ( './campaignStore' ) ) ;
4547 ( { getDb } = await import ( './db' ) ) ;
4648 ( { getCampaignHistory } = await import ( './eventHistory' ) ) ;
@@ -208,3 +210,62 @@ describe('campaign pledge pagination', () => {
208210 expect ( page2 . pledges [ 0 ] . amount ) . toBe ( 50 ) ;
209211 } ) ;
210212} ) ;
213+
214+ describe ( 'campaign analytics' , ( ) => {
215+ it ( 'returns correct funding_gap for campaign with pledges' , ( ) => {
216+ const futureDeadline = Math . floor ( Date . now ( ) / 1000 ) + 86400 ;
217+ const campaign = createCampaign ( {
218+ creator : CREATOR ,
219+ title : 'Analytics Test Campaign' ,
220+ description : 'Campaign to test analytics metrics' ,
221+ assetCode : 'USDC' ,
222+ targetAmount : 1000 ,
223+ deadline : futureDeadline ,
224+ } ) ;
225+
226+ addPledge ( campaign . id , { contributor : CONTRIBUTOR , amount : 250 } ) ;
227+
228+ const analytics = getCampaignAnalytics ( campaign . id ) ;
229+ expect ( analytics ) . toBeDefined ( ) ;
230+ expect ( analytics ?. fundingGap ) . toBe ( 750 ) ; // 1000 - 250 = 750
231+ } ) ;
232+
233+ it ( 'returns zero funding_gap when campaign is fully funded' , ( ) => {
234+ const futureDeadline = Math . floor ( Date . now ( ) / 1000 ) + 86400 ;
235+ const campaign = createCampaign ( {
236+ creator : CREATOR ,
237+ title : 'Fully Funded Campaign' ,
238+ description : 'Campaign to test funding_gap when fully funded' ,
239+ assetCode : 'XLM' ,
240+ targetAmount : 500 ,
241+ deadline : futureDeadline ,
242+ } ) ;
243+
244+ addPledge ( campaign . id , { contributor : CONTRIBUTOR , amount : 500 } ) ;
245+
246+ const analytics = getCampaignAnalytics ( campaign . id ) ;
247+ expect ( analytics ) . toBeDefined ( ) ;
248+ expect ( analytics ?. fundingGap ) . toBe ( 0 ) ; // 500 - 500 = 0
249+ } ) ;
250+
251+ it ( 'returns funding_gap equal to target for campaign with no pledges' , ( ) => {
252+ const futureDeadline = Math . floor ( Date . now ( ) / 1000 ) + 86400 ;
253+ const campaign = createCampaign ( {
254+ creator : CREATOR ,
255+ title : 'Empty Campaign' ,
256+ description : 'Campaign with no pledges to test analytics' ,
257+ assetCode : 'USDC' ,
258+ targetAmount : 2000 ,
259+ deadline : futureDeadline ,
260+ } ) ;
261+
262+ const analytics = getCampaignAnalytics ( campaign . id ) ;
263+ expect ( analytics ) . toBeDefined ( ) ;
264+ expect ( analytics ?. fundingGap ) . toBe ( 2000 ) ; // 2000 - 0 = 2000
265+ } ) ;
266+
267+ it ( 'returns undefined for non-existent campaign' , ( ) => {
268+ const analytics = getCampaignAnalytics ( '99999' ) ;
269+ expect ( analytics ) . toBeUndefined ( ) ;
270+ } ) ;
271+ } ) ;
0 commit comments