diff --git a/src/routes/feeEstimate.js b/src/routes/feeEstimate.js index 3273cc6..a06aa8f 100644 --- a/src/routes/feeEstimate.js +++ b/src/routes/feeEstimate.js @@ -34,8 +34,9 @@ router.get("/", async (req, res, next) => { // Cache miss or fresh=true - fetch from Horizon const feeStats = await server.feeStats(); + const ledgerHistory = await server.ledgers().order("desc").limit(5).call(); + const ledgerHistoryRecords = ledgerHistory.records || []; - const base = parseInt(feeStats.fee_charged.p10); const recommended = parseInt(feeStats.fee_charged.p50); const priority = parseInt(feeStats.fee_charged.p95); @@ -82,6 +83,13 @@ router.get("/", async (req, res, next) => { p95: feeStats.fee_charged.p95, p99: feeStats.fee_charged.p99, }, + history: ledgerHistoryRecords.map((ledger) => ({ + ledger: parseInt(ledger.sequence, 10), + baseFee: parseInt(ledger.base_fee_in_stroops || ledger.base_fee, 10) || 0, + capacityUsage: parseFloat( + Math.min((ledger.successful_transaction_count || 0) / 1000, 1.0).toFixed(4) + ), + })), // New fields context: "Stroops are the smallest unit of XLM; 1 XLM = 10,000,000 stroops.", networkCongestion: (function () { diff --git a/tests/feeEstimate.surgeStatus.test.js b/tests/feeEstimate.surgeStatus.test.js index 260e03c..2fe9980 100644 --- a/tests/feeEstimate.surgeStatus.test.js +++ b/tests/feeEstimate.surgeStatus.test.js @@ -1,5 +1,4 @@ const request = require("supertest"); -const app = require("../src/index"); jest.mock("../src/config/stellar", () => { const originalModule = jest.requireActual("../src/config/stellar"); @@ -12,14 +11,15 @@ jest.mock("../src/config/stellar", () => { }; }); +const app = require("../src/index"); const { server } = require("../src/config/stellar"); -const { feeEstimateCache } = require("../src/utils/cache"); +const cache = require("../src/services/cache"); describe("Fee Surge Status API", () => { beforeEach(() => { jest.clearAllMocks(); - // Clear the cache - feeEstimateCache.clear(); + // Clear the actual route cache + cache.flush(); }); it("returns isSurging: false when average capacity usage is low", async () => { diff --git a/tests/feeEstimate.test.js b/tests/feeEstimate.test.js index e1ba508..96e4df1 100644 --- a/tests/feeEstimate.test.js +++ b/tests/feeEstimate.test.js @@ -1,21 +1,81 @@ const request = require('supertest'); -const app = require('../src/index'); +const { feeEstimateCache } = require('../src/utils/cache'); + +let app; +let server; describe('GET /fee-estimate', () => { + beforeEach(() => { + jest.resetModules(); + jest.doMock('../src/config/stellar', () => { + const originalModule = jest.requireActual('../src/config/stellar'); + return { + ...originalModule, + server: { + ledgers: jest.fn(), + feeStats: jest.fn(), + }, + }; + }); + + ({ server } = require('../src/config/stellar')); + app = require('../src/index'); + feeEstimateCache.clear(); + }); + it('includes new fields in the response', async () => { + jest.spyOn(server, 'feeStats').mockResolvedValue({ + fee_charged: { + min: '100', + p10: '110', + p50: '120', + p95: '140', + p99: '150', + max: '160', + }, + last_ledger_base_fee: '120', + ledger_capacity_usage: '0.12', + }); + + jest.spyOn(server, 'ledgers').mockReturnValue({ + order: jest.fn().mockReturnThis(), + limit: jest.fn().mockReturnThis(), + call: jest.fn().mockResolvedValue({ + records: [ + { sequence: '500', base_fee_in_stroops: '100', successful_transaction_count: 100 }, + { sequence: '499', base_fee_in_stroops: '110', successful_transaction_count: 200 }, + { sequence: '498', base_fee_in_stroops: '120', successful_transaction_count: 300 }, + { sequence: '497', base_fee_in_stroops: '130', successful_transaction_count: 400 }, + { sequence: '496', base_fee_in_stroops: '140', successful_transaction_count: 500 }, + ], + }), + }); + const res = await request(app).get('/fee-estimate?operations=2'); expect(res.statusCode).toBe(200); expect(res.body.success).toBe(true); const data = res.body.data; - // Existing fields + expect(data).toHaveProperty('operationCount'); expect(data).toHaveProperty('perOperation'); - // New fields expect(data).toHaveProperty('context'); expect(typeof data.context).toBe('string'); expect(data).toHaveProperty('networkCongestion'); expect(['low', 'medium', 'high']).toContain(data.networkCongestion); expect(data).toHaveProperty('recommendation'); expect(typeof data.recommendation).toBe('string'); + expect(data).toHaveProperty('history'); + expect(Array.isArray(data.history)).toBe(true); + expect(data.history).toHaveLength(5); + expect(data.history[0]).toEqual({ + ledger: 500, + baseFee: 100, + capacityUsage: 0.1, + }); + expect(data.history[4]).toEqual({ + ledger: 496, + baseFee: 140, + capacityUsage: 0.5, + }); }); }); diff --git a/types/index.d.ts b/types/index.d.ts index 30b2fb0..433c72c 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -364,6 +364,11 @@ export interface FeeEstimateResponse { p95: string p99: string } + history: Array<{ + ledger: number + baseFee: number + capacityUsage: number + }> // Human-friendly additions context: string networkCongestion: 'low' | 'medium' | 'high'