@@ -25,26 +25,25 @@ vi.mock("../config.js", () => ({
2525 isWalletConfigured : vi . fn ( ( ) => true ) ,
2626} ) ) ;
2727
28- // Mock order-selector
29- vi . mock ( "../services/order-selector.js" , ( ) => ( {
30- selectBestOrders : vi . fn ( async ( _creditType : unknown , quantity : number , _denom : unknown , _abbrevs : unknown ) => ( {
31- orders : [
32- {
33- sellOrderId : "1" ,
34- batchDenom : "C01-001" ,
35- quantity : String ( Math . min ( quantity , 10 ) ) ,
36- askAmount : "1000000" ,
37- askDenom : "uregen" ,
38- costMicro : BigInt ( Math . ceil ( Math . min ( quantity , 10 ) * 1000000 ) ) ,
39- } ,
40- ] ,
41- totalQuantity : String ( Math . min ( quantity , 10 ) . toFixed ( 6 ) ) ,
42- totalCostMicro : BigInt ( Math . ceil ( Math . min ( quantity , 10 ) * 1000000 ) ) ,
43- paymentDenom : "uregen" ,
44- displayDenom : "REGEN" ,
45- exponent : 6 ,
46- insufficientSupply : quantity > 10 ,
47- } ) ) ,
28+ // Mock ledger — sell orders, credit classes, allowed denoms
29+ vi . mock ( "../services/ledger.js" , ( ) => ( {
30+ listSellOrders : vi . fn ( async ( ) => [
31+ { id : "1" , seller : "regen1seller" , batch_denom : "C02-001-20210101-20211231-001" , quantity : "100" , ask_denom : "uregen" , ask_amount : "1000000" , disable_auto_retire : false , expiration : null } ,
32+ { id : "2" , seller : "regen1seller" , batch_denom : "C02-002-20220101-20221231-001" , quantity : "100" , ask_denom : "uregen" , ask_amount : "1000000" , disable_auto_retire : false , expiration : null } ,
33+ { id : "3" , seller : "regen1seller" , batch_denom : "BT01-001-20210101-20211231-001" , quantity : "100" , ask_denom : "uregen" , ask_amount : "1000000" , disable_auto_retire : false , expiration : null } ,
34+ { id : "4" , seller : "regen1seller" , batch_denom : "BT01-002-20220101-20221231-001" , quantity : "100" , ask_denom : "uregen" , ask_amount : "1000000" , disable_auto_retire : false , expiration : null } ,
35+ { id : "5" , seller : "regen1seller" , batch_denom : "USS01-001-20210101-20211231-001" , quantity : "100" , ask_denom : "uregen" , ask_amount : "1000000" , disable_auto_retire : false , expiration : null } ,
36+ { id : "6" , seller : "regen1seller" , batch_denom : "KSH01-001-20210101-20211231-001" , quantity : "100" , ask_denom : "uregen" , ask_amount : "1000000" , disable_auto_retire : false , expiration : null } ,
37+ ] ) ,
38+ listCreditClasses : vi . fn ( async ( ) => [
39+ { id : "C02" , credit_type_abbrev : "C" } ,
40+ { id : "BT01" , credit_type_abbrev : "BT" } ,
41+ { id : "USS01" , credit_type_abbrev : "USS" } ,
42+ { id : "KSH01" , credit_type_abbrev : "KSH" } ,
43+ ] ) ,
44+ getAllowedDenoms : vi . fn ( async ( ) => [
45+ { bank_denom : "uregen" , display_denom : "REGEN" , exponent : 6 } ,
46+ ] ) ,
4847} ) ) ;
4948
5049// Mock email service
@@ -64,8 +63,8 @@ vi.mock("../server/db.js", async () => {
6463
6564import { executePoolRun , type PoolRunResult } from "../services/pool.js" ;
6665import { signAndBroadcast } from "../services/wallet.js" ;
67- import { selectBestOrders } from "../services/order-selector.js" ;
6866import { loadConfig } from "../config.js" ;
67+ import { listSellOrders } from "../services/ledger.js" ;
6968
7069let db : Database . Database ;
7170
@@ -239,17 +238,19 @@ describe("Pool Service", () => {
239238 . toBe ( result . totalRevenueCents ) ;
240239 } ) ;
241240
242- it ( "calculates correct 50/30/20 credit type allocation within credits budget " , async ( ) => {
241+ it ( "allocates credit budget equally across all eligible batches " , async ( ) => {
243242 addTestSubscribers ( 10 , 1000 ) ; // $100 total, 75% = $75 credits budget (monthly)
244243
245244 const result = await executePoolRun ( { dryRun : true } ) ;
246245
247- // 50% of 7500 = 3750
248- expect ( result . carbon . budgetCents ) . toBe ( 3750 ) ;
249- // 30% of 7500 = 2250
250- expect ( result . biodiversity . budgetCents ) . toBe ( 2250 ) ;
251- // Remainder = 7500 - 3750 - 2250 = 1500
252- expect ( result . uss . budgetCents ) . toBe ( 1500 ) ;
246+ // 6 eligible batches, equal allocation: floor(7500 / 6) = 1250 per batch
247+ // Carbon: 2 batches (C02-001, C02-002) = 2 * 1250 = 2500 (+ 0 remainder to first batch overall)
248+ // Biodiversity: 2 batches (BT01-001, BT01-002) = 2 * 1250 = 2500
249+ // USS: 2 batches (USS01-001, KSH01-001) = 2 * 1250 = 2500
250+ expect ( result . batches . length ) . toBe ( 6 ) ;
251+ expect ( result . carbon . budgetCents ) . toBe ( 2500 ) ;
252+ expect ( result . biodiversity . budgetCents ) . toBe ( 2500 ) ;
253+ expect ( result . uss . budgetCents ) . toBe ( 2500 ) ;
253254
254255 // Credit type budgets sum to credits budget
255256 expect ( result . carbon . budgetCents + result . biodiversity . budgetCents + result . uss . budgetCents )
@@ -276,9 +277,11 @@ describe("Pool Service", () => {
276277 const result = await executePoolRun ( { dryRun : false } ) ;
277278
278279 expect ( result . dryRun ) . toBe ( false ) ;
279- // signAndBroadcast should have been called for each credit type
280- expect ( signAndBroadcast ) . toHaveBeenCalled ( ) ;
281- expect ( result . carbon . txHash ) . toBe ( "AABB1122" ) ;
280+ // signAndBroadcast should have been called for each eligible batch (6 batches)
281+ expect ( signAndBroadcast ) . toHaveBeenCalledTimes ( 6 ) ;
282+ // Each category aggregates txHashes from its batches (comma-separated)
283+ // Carbon has 2 batches, each returning "AABB1122"
284+ expect ( result . carbon . txHash ) . toBe ( "AABB1122,AABB1122" ) ;
282285 } ) ;
283286
284287 it ( "records per-subscriber fractional attributions" , async ( ) => {
@@ -315,10 +318,11 @@ describe("Pool Service", () => {
315318 }
316319 } ) ;
317320
318- it ( "handles partial fill when one credit type fails" , async ( ) => {
321+ it ( "handles partial fill when one batch fails" , async ( ) => {
319322 addTestSubscribers ( 1 , 1000 ) ;
320323
321- // Make signAndBroadcast fail on second call
324+ // Make signAndBroadcast fail on second call (BT01-002, the second sorted batch)
325+ // Sorted batches: BT01-001, BT01-002, C02-001, C02-002, KSH01-001, USS01-001
322326 let callCount = 0 ;
323327 vi . mocked ( signAndBroadcast ) . mockImplementation ( async ( ) => {
324328 callCount ++ ;
@@ -340,7 +344,9 @@ describe("Pool Service", () => {
340344 const result = await executePoolRun ( { dryRun : false } ) ;
341345
342346 expect ( result . status ) . toBe ( "partial" ) ;
343- expect ( result . carbon . txHash ) . toBe ( "TX1" ) ;
347+ // Carbon: 2 batches (calls 3 & 4) both succeed
348+ expect ( result . carbon . txHash ) . toBe ( "TX3,TX4" ) ;
349+ // Biodiversity: call 1 succeeds (TX1), call 2 fails
344350 expect ( result . biodiversity . error ) . toContain ( "broadcast failed" ) ;
345351 expect ( result . errors . length ) . toBeGreaterThan ( 0 ) ;
346352 } ) ;
0 commit comments