diff --git a/tests/unit/handlers/finance.handlers.test.js b/tests/unit/handlers/finance.handlers.test.js index 0127a15..134ccd9 100644 --- a/tests/unit/handlers/finance.handlers.test.js +++ b/tests/unit/handlers/finance.handlers.test.js @@ -355,6 +355,34 @@ test('processTailLogData - processes power and hashrate', (t) => { t.pass() }) +test('processTailLogData - drills into .val (production shape)', (t) => { + const results = [ + [ + { + type: 'powermeter', + data: [ + { ts: 1700006400000, val: { site_power_w: 5000 } }, + { ts: 1700092800000, val: { site_power_w: 6000 } } + ] + }, + { + type: 'miner', + data: [ + { ts: 1700006400000, val: { hashrate_mhs_5m_sum_aggr: 100000 } }, + { ts: 1700092800000, val: { hashrate_mhs_5m_sum_aggr: 120000 } } + ] + } + ] + ] + + const daily = processTailLogData(results) + t.is(daily[1700006400000].powerW, 5000, 'extracts powerW from .val on day 1') + t.is(daily[1700006400000].hashrateMhs, 100000, 'extracts hashrateMhs from .val on day 1') + t.is(daily[1700092800000].powerW, 6000, 'extracts powerW from .val on day 2') + t.is(daily[1700092800000].hashrateMhs, 120000, 'extracts hashrateMhs from .val on day 2') + t.pass() +}) + test('processTailLogData - handles error results', (t) => { const results = [{ error: 'timeout' }] const daily = processTailLogData(results) @@ -371,6 +399,19 @@ test('processEbitdaPrices - processes valid data', (t) => { t.pass() }) +test('processEbitdaPrices - flat per-ork items with priceUSD (production shape)', (t) => { + const results = [ + [ + { ts: 1700006400000, priceUSD: 40000 }, + { ts: 1700092800000, priceUSD: 41500 } + ] + ] + const daily = processEbitdaPrices(results) + t.is(daily[1700006400000], 40000, 'should extract priceUSD for first day') + t.is(daily[1700092800000], 41500, 'should extract priceUSD for second day') + t.pass() +}) + test('calculateEbitdaSummary - calculates from log entries', (t) => { const log = [ { revenueBTC: 0.5, revenueUSD: 20000, totalCostsUSD: 5000, ebitdaSelling: 15000, ebitdaHodl: 15000 }, @@ -569,16 +610,18 @@ test('getSubsidyFees - empty ork results', async (t) => { test('calculateSubsidyFeesSummary - calculates from log entries', (t) => { const log = [ - { blockReward: 6.25, blockTotalFees: 0.5 }, - { blockReward: 6.25, blockTotalFees: 0.3 } + { blockReward: 6.25, blockTotalFees: 0.5, blockSize: 1500000 }, + { blockReward: 6.25, blockTotalFees: 0.3, blockSize: 1300000 } ] const summary = calculateSubsidyFeesSummary(log) t.is(summary.totalBlockReward, 12.5, 'should sum block rewards') t.is(summary.totalBlockTotalFees, 0.8, 'should sum block fees') + t.is(summary.totalBlockSize, 2800000, 'should sum block sizes') t.ok(summary.avgBlockReward !== null, 'should calculate avg block reward') t.is(summary.avgBlockReward, 6.25, 'should calculate correct avg block reward') t.ok(summary.avgBlockTotalFees !== null, 'should calculate avg block fees') + t.is(summary.avgBlockSize, 1400000, 'should calculate correct avg block size') t.pass() }) @@ -990,6 +1033,24 @@ test('processHashrateData - processes array data', (t) => { t.pass() }) +test('processHashrateData - drills into .val (production shape)', (t) => { + const results = [ + [ + { + type: 'miner', + data: [ + { ts: 1700006400000, val: { hashrate_mhs_5m_sum_aggr: 500000 } }, + { ts: 1700092800000, val: { hashrate_mhs_5m_sum_aggr: 600000 } } + ] + } + ] + ] + const daily = processHashrateData(results) + t.is(daily[1700006400000], 500000, 'extracts hashrate from .val on day 1') + t.is(daily[1700092800000], 600000, 'extracts hashrate from .val on day 2') + t.pass() +}) + test('processHashrateData - handles error results', (t) => { const results = [{ error: 'timeout' }] const daily = processHashrateData(results) @@ -1010,6 +1071,19 @@ test('processNetworkHashrateData - processes array data', (t) => { t.pass() }) +test('processNetworkHashrateData - flat per-ork items (production shape)', (t) => { + const results = [ + [ + { ts: 1700006400000, avgHashrateMHs: 1019725948656278 }, + { ts: 1700092800000, avgHashrateMHs: 1029591824888537 } + ] + ] + const daily = processNetworkHashrateData(results) + t.is(daily[1700006400000], 1019725948656278, 'extracts avgHashrateMHs day 1') + t.is(daily[1700092800000], 1029591824888537, 'extracts avgHashrateMHs day 2') + t.pass() +}) + test('processNetworkHashrateData - processes object-keyed data', (t) => { const results = [ [{ data: { 1700006400000: { avgHashrateMHs: 500000000000000 } } }] diff --git a/tests/unit/handlers/finance.utils.test.js b/tests/unit/handlers/finance.utils.test.js index b82f793..65a0adb 100644 --- a/tests/unit/handlers/finance.utils.test.js +++ b/tests/unit/handlers/finance.utils.test.js @@ -205,7 +205,8 @@ test('processBlockData - array items', (t) => { blocks: [{ ts: 1700006400000, blockReward: 6.25, - blockTotalFees: 0.5 + blockTotalFees: 0.5, + blockSize: 1500000 }] }] ] @@ -213,6 +214,22 @@ test('processBlockData - array items', (t) => { const key = Object.keys(daily)[0] t.is(daily[key].blockReward, 6.25, 'should extract blockReward') t.is(daily[key].blockTotalFees, 0.5, 'should extract blockTotalFees') + t.is(daily[key].blockSize, 1500000, 'should extract blockSize') + t.pass() +}) + +test('processBlockData - flat per-ork items (production shape)', (t) => { + const results = [ + [ + { ts: 1700006400000, blockSize: 1500000, blockHash: 'abc', blockReward: 6.25, blockTotalFees: 0.5 }, + { ts: 1700006400000, blockSize: 1200000, blockHash: 'def', blockReward: 6.25, blockTotalFees: 0.3 } + ] + ] + const daily = processBlockData(results) + const key = Object.keys(daily)[0] + t.is(daily[key].blockReward, 12.5, 'should sum blockReward across same-day items') + t.is(daily[key].blockTotalFees, 0.8, 'should sum blockTotalFees across same-day items') + t.is(daily[key].blockSize, 2700000, 'should sum blockSize across same-day items') t.pass() }) diff --git a/workers/lib/server/handlers/finance.handlers.js b/workers/lib/server/handlers/finance.handlers.js index 444e90d..5767a91 100644 --- a/workers/lib/server/handlers/finance.handlers.js +++ b/workers/lib/server/handlers/finance.handlers.js @@ -432,8 +432,9 @@ function processTailLogData (results) { for (const item of items) { const ts = getStartOfDay(item.ts || item.timestamp) if (!daily[ts]) daily[ts] = { powerW: 0, hashrateMhs: 0 } - daily[ts].powerW += (item[AGGR_FIELDS.SITE_POWER] || 0) - daily[ts].hashrateMhs += (item[AGGR_FIELDS.HASHRATE_SUM] || 0) + const val = item.val || item + daily[ts].powerW += (val[AGGR_FIELDS.SITE_POWER] || 0) + daily[ts].hashrateMhs += (val[AGGR_FIELDS.HASHRATE_SUM] || 0) } } } @@ -449,19 +450,21 @@ function processEbitdaPrices (results) { if (!Array.isArray(data)) continue for (const entry of data) { if (!entry) continue - const items = entry.data || entry.prices || entry + const rawTs = entry.ts || entry.timestamp || entry.time + const items = rawTs ? [entry] : (entry.data || entry.prices || entry) if (Array.isArray(items)) { for (const item of items) { const ts = getStartOfDay(item.ts || item.timestamp || item.time) - if (ts && item.price) { - daily[ts] = item.price + const price = item.priceUSD || item.price + if (ts && price) { + daily[ts] = price } } - } else if (typeof items === 'object' && !Array.isArray(items)) { + } else if (typeof items === 'object') { for (const [key, val] of Object.entries(items)) { const ts = getStartOfDay(Number(key)) if (ts) { - daily[ts] = typeof val === 'object' ? (val.USD || val.price || 0) : Number(val) || 0 + daily[ts] = typeof val === 'object' ? (val.USD || val.priceUSD || val.price || 0) : Number(val) || 0 } } } @@ -518,7 +521,7 @@ async function getCostSummary (ctx, req) { (cb) => ctx.dataProxy.requestData(RPC_METHODS.GET_WRK_EXT_DATA, { type: WORKER_TYPES.MEMPOOL, - query: { key: 'prices', start, end } + query: { key: 'HISTORICAL_PRICES', start, end } }).then(r => cb(null, r)).catch(cb), (cb) => ctx.dataProxy.requestData(RPC_METHODS.TAIL_LOG_RANGE_AGGR, { @@ -628,7 +631,8 @@ async function getSubsidyFees (ctx, req) { log.push({ ts, blockReward: block.blockReward, - blockTotalFees: block.blockTotalFees + blockTotalFees: block.blockTotalFees, + blockSize: block.blockSize }) } @@ -643,22 +647,27 @@ function calculateSubsidyFeesSummary (log) { return { totalBlockReward: 0, totalBlockTotalFees: 0, + totalBlockSize: 0, avgBlockReward: null, - avgBlockTotalFees: null + avgBlockTotalFees: null, + avgBlockSize: null } } const totals = log.reduce((acc, entry) => { acc.blockReward += entry.blockReward || 0 acc.blockTotalFees += entry.blockTotalFees || 0 + acc.blockSize += entry.blockSize || 0 return acc - }, { blockReward: 0, blockTotalFees: 0 }) + }, { blockReward: 0, blockTotalFees: 0, blockSize: 0 }) return { totalBlockReward: totals.blockReward, totalBlockTotalFees: totals.blockTotalFees, + totalBlockSize: totals.blockSize, avgBlockReward: safeDiv(totals.blockReward, log.length), - avgBlockTotalFees: safeDiv(totals.blockTotalFees, log.length) + avgBlockTotalFees: safeDiv(totals.blockTotalFees, log.length), + avgBlockSize: safeDiv(totals.blockSize, log.length) } } @@ -879,6 +888,7 @@ async function getRevenueSummary (ctx, req) { hashRevenueUSDPerPHsPerDay: safeDiv(revenueUSD, hashratePhs), blockReward: block.blockReward || 0, blockTotalFees: block.blockTotalFees || 0, + blockSize: block.blockSize || 0, curtailmentMWh, curtailmentRate, operationalIssuesRate, @@ -1087,7 +1097,8 @@ function processHashrateData (results) { const ts = getStartOfDay(item.ts || item.timestamp) if (!ts) continue if (!daily[ts]) daily[ts] = 0 - daily[ts] += (item[AGGR_FIELDS.HASHRATE_SUM] || 0) + const val = item.val || item + daily[ts] += (val[AGGR_FIELDS.HASHRATE_SUM] || 0) } } } @@ -1103,18 +1114,19 @@ function processNetworkHashrateData (results) { if (!Array.isArray(data)) continue for (const entry of data) { if (!entry) continue - const items = entry.data || entry + const rawTs = entry.ts || entry.timestamp || entry.time + const items = rawTs ? [entry] : (entry.data || entry) if (Array.isArray(items)) { for (const item of items) { if (!item) continue - const rawTs = item.ts || item.timestamp || item.time - const ts = getStartOfDay(normalizeTimestampMs(rawTs)) + const itemTs = item.ts || item.timestamp || item.time + const ts = getStartOfDay(normalizeTimestampMs(itemTs)) if (!ts) continue if (item.avgHashrateMHs) { daily[ts] = item.avgHashrateMHs } } - } else if (typeof items === 'object' && !Array.isArray(items)) { + } else if (typeof items === 'object') { for (const [key, val] of Object.entries(items)) { const ts = getStartOfDay(Number(key)) if (!ts) continue diff --git a/workers/lib/server/handlers/finance.utils.js b/workers/lib/server/handlers/finance.utils.js index 8b6746c..853c8e5 100644 --- a/workers/lib/server/handlers/finance.utils.js +++ b/workers/lib/server/handlers/finance.utils.js @@ -94,25 +94,28 @@ function processBlockData (results) { if (!Array.isArray(data)) continue for (const entry of data) { if (!entry) continue - const items = entry.data || entry.blocks || entry + const rawTs = entry.ts || entry.timestamp || entry.time + const items = rawTs ? [entry] : (entry.data || entry.blocks || entry) if (Array.isArray(items)) { for (const item of items) { if (!item) continue - const rawTs = item.ts || item.timestamp || item.time - const ts = getStartOfDay(normalizeTimestampMs(rawTs)) + const itemTs = item.ts || item.timestamp || item.time + const ts = getStartOfDay(normalizeTimestampMs(itemTs)) if (!ts) continue - if (!daily[ts]) daily[ts] = { blockReward: 0, blockTotalFees: 0 } + if (!daily[ts]) daily[ts] = { blockReward: 0, blockTotalFees: 0, blockSize: 0 } daily[ts].blockReward += (item.blockReward || item.block_reward || item.subsidy || 0) daily[ts].blockTotalFees += (item.blockTotalFees || item.block_total_fees || item.totalFees || item.total_fees || 0) + daily[ts].blockSize += (item.blockSize || item.block_size || item.size || 0) } - } else if (typeof items === 'object' && !Array.isArray(items)) { + } else if (typeof items === 'object') { for (const [key, val] of Object.entries(items)) { const ts = getStartOfDay(Number(key)) if (!ts) continue - if (!daily[ts]) daily[ts] = { blockReward: 0, blockTotalFees: 0 } + if (!daily[ts]) daily[ts] = { blockReward: 0, blockTotalFees: 0, blockSize: 0 } if (typeof val === 'object') { daily[ts].blockReward += (val.blockReward || val.block_reward || val.subsidy || 0) daily[ts].blockTotalFees += (val.blockTotalFees || val.block_total_fees || val.totalFees || val.total_fees || 0) + daily[ts].blockSize += (val.blockSize || val.block_size || val.size || 0) } } }