From 3d957bfb7629fb73cdc9c5acb6bed51ba4de578e Mon Sep 17 00:00:00 2001 From: openhands Date: Tue, 17 Mar 2026 19:34:58 +0000 Subject: [PATCH 1/2] fix: txnBot has zero test coverage after deleting recenterAccess.test.ts (#919) Co-Authored-By: Claude Sonnet 4.6 --- services/txnBot/src/service.test.ts | 134 ++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 services/txnBot/src/service.test.ts diff --git a/services/txnBot/src/service.test.ts b/services/txnBot/src/service.test.ts new file mode 100644 index 0000000..ff095e3 --- /dev/null +++ b/services/txnBot/src/service.test.ts @@ -0,0 +1,134 @@ +import { describe, it } from 'node:test'; +import assert from 'node:assert/strict'; +import { createTxnBot, type TxnBotDependencies } from './service.js'; +import type { BotConfigService } from './services/BotConfigService.js'; +import type { BlockchainService } from './services/BlockchainService.js'; +import type { GraphQLService } from './services/GraphQLService.js'; + +const BASE_CONFIG = { + PROVIDER_URL: 'http://localhost:8545', + PRIVATE_KEY: '0xdeadbeef000000000000000000000000000000000000000000000000deadbeef', + LM_CONTRACT_ADDRESS: '0x0000000000000000000000000000000000000001', + STAKE_CONTRACT_ADDRESS: '0x0000000000000000000000000000000000000002', + GRAPHQL_ENDPOINT: 'http://localhost:42069/graphql', + ENVIRONMENT: 'test', + PORT: '3000', +}; + +function makeTx(hash: string) { + return { hash, wait: (): Promise => Promise.resolve(null) }; +} + +function makeDeps( + blockchain?: Partial<{ + estimateRecenterGas: () => Promise; + recenter: () => Promise>; + }>, +): TxnBotDependencies { + return { + configService: { + getConfig: () => ({ ...BASE_CONFIG }), + getPort: () => '3000', + } as unknown as BotConfigService, + + blockchainService: { + estimateRecenterGas: blockchain?.estimateRecenterGas ?? (() => Promise.resolve()), + recenter: blockchain?.recenter ?? (() => Promise.resolve(makeTx('0xabc'))), + checkFunds: () => Promise.resolve('1.0'), + payTax: () => Promise.resolve(makeTx('0xpay')), + } as unknown as BlockchainService, + + graphQLService: { + fetchActivePositions: () => Promise.resolve([]), + } as unknown as GraphQLService, + }; +} + +describe('evaluateRecenterOpportunity', () => { + it('returns canRecenter: true when gas estimation succeeds', async () => { + const bot = createTxnBot(makeDeps()); + const result = await bot.evaluateRecenterOpportunity(); + + assert.equal(result.canRecenter, true); + assert.equal(result.reason, null); + assert.equal(result.error, null); + assert.ok(result.checkedAtMs > 0); + }); + + it('returns canRecenter: false with extracted revert reason', async () => { + const bot = createTxnBot( + makeDeps({ + estimateRecenterGas: () => + Promise.reject({ + shortMessage: 'execution reverted: recenter not needed', + message: 'execution reverted: recenter not needed', + }), + }), + ); + const result = await bot.evaluateRecenterOpportunity(); + + assert.equal(result.canRecenter, false); + assert.equal(result.reason, 'recenter not needed'); + assert.equal(result.error, 'execution reverted: recenter not needed'); + }); + + it('returns canRecenter: false with generic message when no revert prefix', async () => { + const bot = createTxnBot( + makeDeps({ + estimateRecenterGas: () => Promise.reject({ message: 'connection refused' }), + }), + ); + const result = await bot.evaluateRecenterOpportunity(); + + assert.equal(result.canRecenter, false); + assert.equal(result.reason, 'connection refused'); + assert.ok(result.checkedAtMs > 0); + }); +}); + +describe('attemptRecenter', () => { + it('calls recenter() and returns executed: true when eligible', async () => { + let recenterCalled = false; + const bot = createTxnBot( + makeDeps({ + recenter: () => { + recenterCalled = true; + return Promise.resolve(makeTx('0xdeadbeef')); + }, + }), + ); + const result = await bot.attemptRecenter(); + + assert.equal(result.executed, true); + assert.equal(result.txHash, '0xdeadbeef'); + assert.ok(recenterCalled, 'recenter() should have been called'); + }); + + it('skips recenter() and returns executed: false when not eligible', async () => { + let recenterCalled = false; + const bot = createTxnBot( + makeDeps({ + estimateRecenterGas: () => Promise.reject({ message: 'recenter not needed' }), + recenter: () => { + recenterCalled = true; + return Promise.resolve(makeTx('0xabc')); + }, + }), + ); + const result = await bot.attemptRecenter(); + + assert.equal(result.executed, false); + assert.equal(recenterCalled, false, 'recenter() must not be called when not eligible'); + assert.ok(result.message); + }); + + it('propagates error thrown by recenter() — caught by liquidityLoop', async () => { + const bot = createTxnBot( + makeDeps({ + recenter: () => Promise.reject(new Error('tx submission failed')), + }), + ); + + await assert.rejects(bot.attemptRecenter(), /tx submission failed/); + }); +}); From 074c94dc0412c7d6993d80a5ad66c16c7c611c08 Mon Sep 17 00:00:00 2001 From: openhands Date: Tue, 17 Mar 2026 19:35:13 +0000 Subject: [PATCH 2/2] fix: txnBot has zero test coverage after deleting recenterAccess.test.ts (#919) --- kraiken-lib/package-lock.json | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/kraiken-lib/package-lock.json b/kraiken-lib/package-lock.json index 58c0c9e..2c47f7c 100644 --- a/kraiken-lib/package-lock.json +++ b/kraiken-lib/package-lock.json @@ -226,6 +226,7 @@ "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.2", @@ -2742,6 +2743,7 @@ "integrity": "sha512-F1CBxgqwOMc4GKJ7eY22hWhBVQuMYTtqI8L0FcszYcpYX0fzfDGpez22Xau8Mgm7O9fI+zA/TYIdq3tGWfweBA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.13.0" } @@ -2802,6 +2804,7 @@ "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/types": "8.45.0", @@ -3413,6 +3416,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3737,6 +3741,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001587", "electron-to-chromium": "^1.4.668", @@ -4525,6 +4530,7 @@ "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5222,6 +5228,7 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", "license": "MIT", + "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -5304,8 +5311,9 @@ "version": "5.16.0", "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.16.0.tgz", "integrity": "sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==", - "dev": true, + "devOptional": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -7212,6 +7220,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -8146,8 +8155,9 @@ "version": "5.4.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8354,6 +8364,7 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -8452,6 +8463,7 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", @@ -8667,6 +8679,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" },