From d9b4cca5c9e5c0f53f6cf790dbb045e224939909 Mon Sep 17 00:00:00 2001 From: PintoPirate <189064953+PintoPirate@users.noreply.github.com> Date: Mon, 26 Jan 2026 15:16:07 -0500 Subject: [PATCH 1/4] Avoid unnecessary introspection on each request --- docker/docker-compose.yml | 1 - .../postgres/startup-seeders/apy-seeder.js | 2 +- src/repository/subgraph/subgraph-cache.js | 15 ++++++++++----- src/scheduled/tasks/IndexingTask.js | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index b3ddb04..213b9b9 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -32,4 +32,3 @@ services: command: ["redis-server", "--appendonly", "yes"] volumes: - ./.data/${DOCKER_ENV}/redis:/data - restart: unless-stopped \ No newline at end of file diff --git a/src/repository/postgres/startup-seeders/apy-seeder.js b/src/repository/postgres/startup-seeders/apy-seeder.js index a998246..b566b5b 100644 --- a/src/repository/postgres/startup-seeders/apy-seeder.js +++ b/src/repository/postgres/startup-seeders/apy-seeder.js @@ -22,7 +22,7 @@ class ApySeeder { // Calculate and save all vapys for each season (this will take a long time for many seasons) const TAG = Concurrent.tag('apySeeder'); for (const season of missingSeasons) { - await Concurrent.run(TAG, 3, async () => { + await Concurrent.run(TAG, 1, async () => { try { await YieldService.saveSeasonalApys({ season }); } catch (e) { diff --git a/src/repository/subgraph/subgraph-cache.js b/src/repository/subgraph/subgraph-cache.js index e1aab99..8d280f6 100644 --- a/src/repository/subgraph/subgraph-cache.js +++ b/src/repository/subgraph/subgraph-cache.js @@ -7,10 +7,15 @@ const CommonSubgraphRepository = require('./common-subgraph'); // Caches past season results for configured queries, enabling retrieval of the full history to be fast class SubgraphCache { + // Introspection is required at runtime to build the schema. + // If the schema of an underlying subgraph changes, the API must be redeployed (or apollo restarted). + // Therefore the schema can be cached here rather than retrieved at runtime on each request. + static initialIntrospection = {}; + static async get(cacheQueryName, where) { const sgName = SG_CACHE_CONFIG[cacheQueryName].subgraph; - const introspection = await this.introspect(sgName); + const introspection = this.initialIntrospection[sgName]; const { latest, cache } = await this._getCachedResults(cacheQueryName, where); const freshResults = await this._queryFreshResults(cacheQueryName, where, latest, introspection); @@ -66,12 +71,10 @@ class SubgraphCache { if (!fromCache) { Log.info(`New deployment detected; clearing subgraph cache for ${sgName}`); await this.clear(sgName); - await redisClient.set(`sg-deployment:${sgName}`, deployment); - await redisClient.set(`sg-introspection:${sgName}`, JSON.stringify(queryInfo)); } - return queryInfo; + return (this.initialIntrospection[sgName] = queryInfo); } // Recursively build a type string to use in the re-exported schema @@ -88,7 +91,8 @@ class SubgraphCache { static async _getCachedResults(cacheQueryName, where) { const cfg = SG_CACHE_CONFIG[cacheQueryName]; - const cachedResults = JSON.parse(await redisClient.get(`sg:${cfg.subgraph}:${cacheQueryName}:${where}`)) ?? []; + const redisResult = await redisClient.get(`sg:${cfg.subgraph}:${cacheQueryName}:${where}`); + const cachedResults = JSON.parse(redisResult) ?? []; return { latest: @@ -101,6 +105,7 @@ class SubgraphCache { static async _queryFreshResults(cacheQueryName, where, latestValue, introspection, c = C()) { const cfg = SG_CACHE_CONFIG[cacheQueryName]; + // TODO: if got different x-deployment, clear the cache and send an alert that API might need restarting const results = await SubgraphQueryUtil.allPaginatedSG( cfg.client(c), `{ ${cfg.queryName} { ${introspection[cacheQueryName].fields diff --git a/src/scheduled/tasks/IndexingTask.js b/src/scheduled/tasks/IndexingTask.js index d82cc9f..15fa544 100644 --- a/src/scheduled/tasks/IndexingTask.js +++ b/src/scheduled/tasks/IndexingTask.js @@ -40,7 +40,7 @@ class IndexingTask { return { countEvents, queuedCallersBehind: this._queueCounter > localCount, - canExecuteAgain: !this.isCaughtUp() + canExecuteAgain: !this.isCaughtUp() && countEvents !== false // false indicates task skipped }; } finally { this._running = false; From a55e35cd7a6ef56d0d53539e8ade8713a1cd4711 Mon Sep 17 00:00:00 2001 From: PintoPirate <189064953+PintoPirate@users.noreply.github.com> Date: Tue, 27 Jan 2026 13:50:37 -0500 Subject: [PATCH 2/4] Detect new deployment using sg response headers --- src/datasources/subgraph-client.js | 14 ++++++++++--- src/repository/subgraph/subgraph-cache.js | 25 ++++++++++++++++++----- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/datasources/subgraph-client.js b/src/datasources/subgraph-client.js index 56077aa..c01a4a1 100644 --- a/src/datasources/subgraph-client.js +++ b/src/datasources/subgraph-client.js @@ -16,9 +16,16 @@ class SubgraphClients { // let callNumber = 1; static fromUrl(url) { - return async (query) => { + const requestFunction = async (query) => { const client = this._getClient(url); - const response = await client.request(query); + const res = await client.rawRequest(query); + + // Attach response metadata to the client function + requestFunction.meta = { + version: res.headers.get('x-version'), + deployment: res.headers.get('x-deployment'), + indexedBlock: res.headers.get('x-indexed-block') + }; // if (EnvUtil.getDeploymentEnv().includes('local')) { // // Use this to assist in mocking. Should be commented in/out as needed. @@ -28,8 +35,9 @@ class SubgraphClients { // ); // console.log('wrote subgraph output to test directory'); // } - return response; + return res.data; }; + return requestFunction; } static _getClient(url) { diff --git a/src/repository/subgraph/subgraph-cache.js b/src/repository/subgraph/subgraph-cache.js index 8d280f6..9808ad4 100644 --- a/src/repository/subgraph/subgraph-cache.js +++ b/src/repository/subgraph/subgraph-cache.js @@ -1,5 +1,6 @@ const { C } = require('../../constants/runtime-constants'); const redisClient = require('../../datasources/redis-client'); +const { sendWebhookMessage } = require('../../utils/discord'); const Log = require('../../utils/logging'); const SubgraphQueryUtil = require('../../utils/subgraph-query'); const { SG_CACHE_CONFIG } = require('./cache-config'); @@ -11,6 +12,7 @@ class SubgraphCache { // If the schema of an underlying subgraph changes, the API must be redeployed (or apollo restarted). // Therefore the schema can be cached here rather than retrieved at runtime on each request. static initialIntrospection = {}; + static introspectionDeployment = {}; static async get(cacheQueryName, where) { const sgName = SG_CACHE_CONFIG[cacheQueryName].subgraph; @@ -69,14 +71,19 @@ class SubgraphCache { } if (!fromCache) { - Log.info(`New deployment detected; clearing subgraph cache for ${sgName}`); - await this.clear(sgName); - await redisClient.set(`sg-deployment:${sgName}`, deployment); + await this._newDeploymentDetected(sgName, deployment); } + this.introspectionDeployment[sgName] = deployment; return (this.initialIntrospection[sgName] = queryInfo); } + static async _newDeploymentDetected(sgName, deployment) { + Log.info(`New deployment detected; clearing subgraph cache for ${sgName}`); + await this.clear(sgName); + await redisClient.set(`sg-deployment:${sgName}`, deployment); + } + // Recursively build a type string to use in the re-exported schema // new Set(schema.types.flatMap((t) => t.fields?.flatMap((f) => f.type.kind))); static _buildTypeName(type) { @@ -105,9 +112,9 @@ class SubgraphCache { static async _queryFreshResults(cacheQueryName, where, latestValue, introspection, c = C()) { const cfg = SG_CACHE_CONFIG[cacheQueryName]; - // TODO: if got different x-deployment, clear the cache and send an alert that API might need restarting + const sgClient = cfg.client(c); const results = await SubgraphQueryUtil.allPaginatedSG( - cfg.client(c), + sgClient, `{ ${cfg.queryName} { ${introspection[cacheQueryName].fields .filter((f) => !cfg.omitFields?.includes(f.name)) .concat(cfg.syntheticFields?.map((f) => ({ name: f.queryAccessor })) ?? []) @@ -118,6 +125,14 @@ class SubgraphCache { { ...cfg.paginationSettings, lastValue: latestValue } ); + // If new deployment detected, clear the cache and send an alert that API might need restarting + if (sgClient.meta.deployment !== this.introspectionDeployment[cfg.subgraph]) { + sendWebhookMessage( + `New deployment detected for ${cfg.subgraph}, the API might need to be restarted (if the schema changed).` + ); + await this._newDeploymentDetected(cfg.subgraph, sgClient.meta.deployment); + } + for (const result of results) { for (const syntheticField of cfg.syntheticFields ?? []) { result[syntheticField.objectRewritePath] = syntheticField.objectAccessor(result); From b8d4276d5de4cecd5c81de13d1b7dafa68476a3f Mon Sep 17 00:00:00 2001 From: PintoPirate <189064953+PintoPirate@users.noreply.github.com> Date: Tue, 27 Jan 2026 13:54:52 -0500 Subject: [PATCH 3/4] npm audit fix --- package-lock.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ed31c86..554c443 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5840,9 +5840,10 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" }, "node_modules/lodash.sortby": { "version": "4.7.0", From baebd0909504ca5918c784a999d232577e6bf92d Mon Sep 17 00:00:00 2001 From: PintoPirate <189064953+PintoPirate@users.noreply.github.com> Date: Tue, 27 Jan 2026 14:08:53 -0500 Subject: [PATCH 4/4] Update tests to use raw request for sg --- test/apy/silo-apy.test.js | 14 +++++++------- test/repository/seeders/deposit-seeder.test.js | 4 ++-- test/scheduled/sunrise.test.js | 14 ++++++++------ test/service/exchange-service.test.js | 8 +++++--- test/service/field-service.test.js | 8 ++++---- test/service/silo-service.test.js | 8 ++++---- test/util/mock-sg.js | 12 +++++++++++- 7 files changed, 41 insertions(+), 27 deletions(-) diff --git a/test/apy/silo-apy.test.js b/test/apy/silo-apy.test.js index ea96652..c725209 100644 --- a/test/apy/silo-apy.test.js +++ b/test/apy/silo-apy.test.js @@ -8,7 +8,7 @@ const GaugeApyUtil = require('../../src/service/utils/apy/gauge'); const PreGaugeApyUtil = require('../../src/service/utils/apy/pre-gauge'); const { toBigInt } = require('../../src/utils/number'); const { mockBeanstalkConstants } = require('../util/mock-constants'); -const { mockBeanstalkSG } = require('../util/mock-sg'); +const { mockBeanstalkSG, mockWrappedSgReturnData } = require('../util/mock-sg'); describe('Window EMA', () => { beforeEach(() => { @@ -20,7 +20,7 @@ describe('Window EMA', () => { it('should calculate window EMA', async () => { const rewardMintResponse = require('../mock-responses/subgraph/silo-apy/siloHourlyRewardMints_1.json'); - jest.spyOn(mockBeanstalkSG, 'request').mockResolvedValue(rewardMintResponse); + jest.spyOn(mockBeanstalkSG, 'rawRequest').mockResolvedValue(mockWrappedSgReturnData(rewardMintResponse)); const emaResult = await SiloApyService.calcWindowEMA(21816, [24, 168, 720]); @@ -45,7 +45,7 @@ describe('Window EMA', () => { it('should use up to as many season as are available', async () => { const rewardMintResponse = require('../mock-responses/subgraph/silo-apy/siloHourlyRewardMints_2.json'); - jest.spyOn(mockBeanstalkSG, 'request').mockResolvedValue(rewardMintResponse); + jest.spyOn(mockBeanstalkSG, 'rawRequest').mockResolvedValue(mockWrappedSgReturnData(rewardMintResponse)); const emaResult = await SiloApyService.calcWindowEMA(6100, [10000, 20000]); @@ -291,9 +291,9 @@ describe('SiloApyService Orchestration', () => { it('pre-gauge should supply appropriate parameters', async () => { const seasonBlockResponse = require('../mock-responses/subgraph/silo-apy/preGaugeApyInputs_1.json'); - jest.spyOn(mockBeanstalkSG, 'request').mockResolvedValueOnce(seasonBlockResponse); + jest.spyOn(mockBeanstalkSG, 'rawRequest').mockResolvedValueOnce(mockWrappedSgReturnData(seasonBlockResponse)); const preGaugeApyInputsResponse = require('../mock-responses/subgraph/silo-apy/preGaugeApyInputs_2.json'); - jest.spyOn(mockBeanstalkSG, 'request').mockResolvedValueOnce(preGaugeApyInputsResponse); + jest.spyOn(mockBeanstalkSG, 'rawRequest').mockResolvedValueOnce(mockWrappedSgReturnData(preGaugeApyInputsResponse)); const spy = jest.spyOn(PreGaugeApyUtil, 'calcApy'); spy.mockReturnValueOnce({ @@ -329,9 +329,9 @@ describe('SiloApyService Orchestration', () => { it('gauge should supply appropriate parameters', async () => { const seasonBlockResponse = require('../mock-responses/subgraph/silo-apy/gaugeApyInputs_1.json'); - jest.spyOn(mockBeanstalkSG, 'request').mockResolvedValueOnce(seasonBlockResponse); + jest.spyOn(mockBeanstalkSG, 'rawRequest').mockResolvedValueOnce(mockWrappedSgReturnData(seasonBlockResponse)); const gaugeApyInputsResponse = require('../mock-responses/subgraph/silo-apy/gaugeApyInputs_2.json'); - jest.spyOn(mockBeanstalkSG, 'request').mockResolvedValueOnce(gaugeApyInputsResponse); + jest.spyOn(mockBeanstalkSG, 'rawRequest').mockResolvedValueOnce(mockWrappedSgReturnData(gaugeApyInputsResponse)); const spy = jest.spyOn(GaugeApyUtil, 'calcApy'); spy.mockReturnValueOnce({ diff --git a/test/repository/seeders/deposit-seeder.test.js b/test/repository/seeders/deposit-seeder.test.js index 4ebc489..fccff3b 100644 --- a/test/repository/seeders/deposit-seeder.test.js +++ b/test/repository/seeders/deposit-seeder.test.js @@ -7,7 +7,7 @@ const AsyncContext = require('../../../src/utils/async/context'); const Log = require('../../../src/utils/logging'); const { allToBigInt } = require('../../../src/utils/number'); const { mockBeanstalkConstants } = require('../../util/mock-constants'); -const { mockBeanstalkSG } = require('../../util/mock-sg'); +const { mockBeanstalkSG, mockWrappedSgReturnData } = require('../../util/mock-sg'); describe('Deposit Seeder', () => { beforeEach(() => { @@ -21,7 +21,7 @@ describe('Deposit Seeder', () => { }); test('Seeds all deposits', async () => { const depositsResponse = require('../../mock-responses/subgraph/silo-service/allDeposits.json'); - jest.spyOn(mockBeanstalkSG, 'request').mockResolvedValueOnce(depositsResponse); + jest.spyOn(mockBeanstalkSG, 'rawRequest').mockResolvedValueOnce(mockWrappedSgReturnData(depositsResponse)); const whitelistInfoResponse = allToBigInt(require('../../mock-responses/service/whitelistedTokenInfo.json')); jest.spyOn(SiloService, 'getWhitelistedTokenInfo').mockResolvedValue(whitelistInfoResponse); diff --git a/test/scheduled/sunrise.test.js b/test/scheduled/sunrise.test.js index 1e16891..d4b5b32 100644 --- a/test/scheduled/sunrise.test.js +++ b/test/scheduled/sunrise.test.js @@ -1,6 +1,6 @@ const OnSunriseUtil = require('../../src/scheduled/util/on-sunrise'); const { mockBeanstalkConstants } = require('../util/mock-constants'); -const { mockBeanSG, mockBasinSG, mockBeanstalkSG } = require('../util/mock-sg'); +const { mockBeanSG, mockBasinSG, mockBeanstalkSG, mockWrappedSgReturnData } = require('../util/mock-sg'); async function checkLastPromiseResult(spy, expected) { const lastCallResult = await spy.mock.results[spy.mock.results.length - 1].value; @@ -22,11 +22,13 @@ describe('OnSunrise', () => { it('identifies when the subgraphs have processed the new season', async () => { const seasonResponse = require('../mock-responses/subgraph/scheduled/sunrise/beanstalkSeason_1.json'); - const beanstalkSGSpy = jest.spyOn(mockBeanstalkSG, 'request').mockResolvedValue(seasonResponse); + const beanstalkSGSpy = jest + .spyOn(mockBeanstalkSG, 'rawRequest') + .mockResolvedValue(mockWrappedSgReturnData(seasonResponse)); const metaNotReady = require('../mock-responses/subgraph/scheduled/sunrise/metaNotReady.json'); - const beanSGSpy = jest.spyOn(mockBeanSG, 'request').mockResolvedValue(metaNotReady); - const basinSGSpy = jest.spyOn(mockBasinSG, 'request').mockResolvedValue(metaNotReady); + const beanSGSpy = jest.spyOn(mockBeanSG, 'rawRequest').mockResolvedValue(mockWrappedSgReturnData(metaNotReady)); + const basinSGSpy = jest.spyOn(mockBasinSG, 'rawRequest').mockResolvedValue(mockWrappedSgReturnData(metaNotReady)); const checkSpy = jest.spyOn(OnSunriseUtil, 'checkSubgraphsForSunrise'); @@ -34,7 +36,7 @@ describe('OnSunrise', () => { await checkLastPromiseResult(checkSpy, false); const seasonResponse2 = require('../mock-responses/subgraph/scheduled/sunrise/beanstalkSeason_2.json'); - beanstalkSGSpy.mockResolvedValue(seasonResponse2); + beanstalkSGSpy.mockResolvedValue(mockWrappedSgReturnData(seasonResponse2)); // Fast-forward timers and continue jest.advanceTimersByTime(5000); jest.runAllTimers(); @@ -55,7 +57,7 @@ describe('OnSunrise', () => { test('fails to identify a new season within the time limit', async () => { const seasonResponse = require('../mock-responses/subgraph/scheduled/sunrise/beanstalkSeason_1.json'); - jest.spyOn(mockBeanstalkSG, 'request').mockResolvedValue(seasonResponse); + jest.spyOn(mockBeanstalkSG, 'rawRequest').mockResolvedValue(mockWrappedSgReturnData(seasonResponse)); const checkSpy = jest.spyOn(OnSunriseUtil, 'checkSubgraphsForSunrise'); diff --git a/test/service/exchange-service.test.js b/test/service/exchange-service.test.js index 104fb95..0d29093 100644 --- a/test/service/exchange-service.test.js +++ b/test/service/exchange-service.test.js @@ -5,7 +5,7 @@ const { getTickers, getWellPriceStats, getTrades } = require('../../src/service/ const { ADDRESSES: { BEANWETH, BEANWSTETH, WETH, BEAN } } = require('../../src/constants/raw/beanstalk-eth'); -const { mockBasinSG } = require('../util/mock-sg'); +const { mockBasinSG, mockWrappedSgReturnData } = require('../util/mock-sg'); const LiquidityUtil = require('../../src/service/utils/pool/liquidity'); const ExchangeService = require('../../src/service/exchange-service'); const { mockBeanstalkConstants } = require('../util/mock-constants'); @@ -23,7 +23,7 @@ describe('ExchangeService', () => { it('should return all Basin tickers in the expected format', async () => { const wellsResponse = require('../mock-responses/subgraph/basin/wells.json'); - jest.spyOn(mockBasinSG, 'request').mockResolvedValue(wellsResponse); + jest.spyOn(mockBasinSG, 'rawRequest').mockResolvedValue(mockWrappedSgReturnData(wellsResponse)); // In practice these 2 values are not necessary since the subsequent getWellPriceRange is also mocked. jest.spyOn(BasinSubgraphRepository, 'getAllTrades').mockResolvedValue(undefined); jest.spyOn(ExchangeService, 'priceEventsByWell').mockReturnValueOnce(undefined); @@ -79,7 +79,9 @@ describe('ExchangeService', () => { }); test('Returns swap history', async () => { - jest.spyOn(mockBasinSG, 'request').mockResolvedValue(require('../mock-responses/subgraph/basin/swapHistory.json')); + jest + .spyOn(mockBasinSG, 'rawRequest') + .mockResolvedValue(mockWrappedSgReturnData(require('../mock-responses/subgraph/basin/swapHistory.json'))); const options = { ticker_id: `${BEAN}_${WETH}`, diff --git a/test/service/field-service.test.js b/test/service/field-service.test.js index 2d5f6b6..2adfa79 100644 --- a/test/service/field-service.test.js +++ b/test/service/field-service.test.js @@ -1,14 +1,14 @@ const Contracts = require('../../src/datasources/contracts/contracts'); const FieldService = require('../../src/service/field-service'); const { toBigInt } = require('../../src/utils/number'); -const { mockBeanstalkSG } = require('../util/mock-sg'); +const { mockBeanstalkSG, mockWrappedSgReturnData } = require('../util/mock-sg'); describe('FieldService', () => { describe('Plot summary', () => { beforeEach(async () => { jest.restoreAllMocks(); const allPlots = require('../mock-responses/service/field/allPlots.json'); - jest.spyOn(mockBeanstalkSG, 'request').mockResolvedValueOnce(allPlots); + jest.spyOn(mockBeanstalkSG, 'rawRequest').mockResolvedValueOnce(mockWrappedSgReturnData(allPlots)); jest.spyOn(Contracts, 'getBeanstalk').mockReturnValue({ harvestableIndex: jest.fn().mockResolvedValue(5000) }); @@ -85,7 +85,7 @@ describe('FieldService', () => { result: 'result' } }); - const sgSpy = jest.spyOn(mockBeanstalkSG, 'request'); + const sgSpy = jest.spyOn(mockBeanstalkSG, 'rawRequest'); const result = await FieldService.getAggregatePlotSummary({ bucketSize: 10000 }); @@ -100,7 +100,7 @@ describe('FieldService', () => { result: 'result' } }); - const sgSpy = jest.spyOn(mockBeanstalkSG, 'request'); + const sgSpy = jest.spyOn(mockBeanstalkSG, 'rawRequest'); jest.setSystemTime(Date.now() + 1000 * 60 * 300 + 1); const result = await FieldService.getAggregatePlotSummary({ bucketSize: 10000 }); diff --git a/test/service/silo-service.test.js b/test/service/silo-service.test.js index 29d83e0..3a06037 100644 --- a/test/service/silo-service.test.js +++ b/test/service/silo-service.test.js @@ -6,7 +6,7 @@ const { } = require('../../src/constants/raw/beanstalk-eth'); const Contracts = require('../../src/datasources/contracts/contracts'); const whitelistedSGResponse = require('../mock-responses/subgraph/silo-service/whitelistedTokens.json'); -const { mockBeanstalkSG } = require('../util/mock-sg'); +const { mockBeanstalkSG, mockWrappedSgReturnData } = require('../util/mock-sg'); const { mockBeanstalkConstants } = require('../util/mock-constants'); const SiloService = require('../../src/service/silo-service'); const { C } = require('../../src/constants/runtime-constants'); @@ -35,7 +35,7 @@ describe('SiloService', () => { }) }; - jest.spyOn(mockBeanstalkSG, 'request').mockResolvedValueOnce(whitelistedSGResponse); + jest.spyOn(mockBeanstalkSG, 'rawRequest').mockResolvedValueOnce(mockWrappedSgReturnData(whitelistedSGResponse)); jest.spyOn(Contracts, 'getBeanstalk').mockReturnValue(mockBeanstalk); const grownStalk = await getMigratedGrownStalk(accounts, defaultOptions); @@ -49,8 +49,8 @@ describe('SiloService', () => { const accounts = ['0xabcd', '0x1234']; const siloSGResponse = require('../mock-responses/subgraph/silo-service/depositedBdvs.json'); - jest.spyOn(mockBeanstalkSG, 'request').mockResolvedValueOnce(siloSGResponse); - jest.spyOn(mockBeanstalkSG, 'request').mockResolvedValueOnce(whitelistedSGResponse); + jest.spyOn(mockBeanstalkSG, 'rawRequest').mockResolvedValueOnce(mockWrappedSgReturnData(siloSGResponse)); + jest.spyOn(mockBeanstalkSG, 'rawRequest').mockResolvedValueOnce(mockWrappedSgReturnData(whitelistedSGResponse)); const mockBeanstalk = { stemTipForToken: jest.fn().mockImplementation((token, options) => { diff --git a/test/util/mock-sg.js b/test/util/mock-sg.js index f3df302..653c197 100644 --- a/test/util/mock-sg.js +++ b/test/util/mock-sg.js @@ -4,5 +4,15 @@ const EnvUtil = require('../../src/utils/env'); module.exports = { mockBeanstalkSG: SubgraphClients._getClient(`https://graph.pinto.money/${EnvUtil.getSG('eth').BEANSTALK}`), mockBeanSG: SubgraphClients._getClient(`https://graph.pinto.money/${EnvUtil.getSG('eth').BEAN}`), - mockBasinSG: SubgraphClients._getClient(`https://graph.pinto.money/${EnvUtil.getSG('eth').BASIN}`) + mockBasinSG: SubgraphClients._getClient(`https://graph.pinto.money/${EnvUtil.getSG('eth').BASIN}`), + mockWrappedSgReturnData: (data) => { + return { + data, + headers: new Map([ + ['x-version', '1.0.0'], + ['x-deployment', 'Qmcfyemdsh6Gw22mLZA797kswQiCYfQyXBNEEmFxYi72HN'], + ['x-indexed-block', 41375750] + ]) + }; + } };