From b93eedfb62b5d19ece300bfc8e60e7109289d5ea Mon Sep 17 00:00:00 2001 From: Iaroslav Gryshaiev Date: Wed, 5 Feb 2025 16:54:49 +0100 Subject: [PATCH] fix(deployment): searches leases with the correct params (#794) --- .../deployment-setting.service.ts | 2 +- .../draining-deployment.service.spec.ts | 80 ++++++++++++++++--- .../draining-deployment.service.ts | 12 ++- .../functional/deployment-setting.spec.ts | 16 ++-- 4 files changed, 88 insertions(+), 22 deletions(-) diff --git a/apps/api/src/deployment/services/deployment-setting/deployment-setting.service.ts b/apps/api/src/deployment/services/deployment-setting/deployment-setting.service.ts index 2bb5a4b86..c832ce1da 100644 --- a/apps/api/src/deployment/services/deployment-setting/deployment-setting.service.ts +++ b/apps/api/src/deployment/services/deployment-setting/deployment-setting.service.ts @@ -65,7 +65,7 @@ export class DeploymentSettingService { return { ...params, estimatedTopUpAmount: 0, topUpFrequencyMs: this.topUpFrequencyMs }; } - const estimatedTopUpAmount = await this.drainingDeploymentService.calculateTopUpAmountForDseqAndOwner(params.dseq, params.userId); + const estimatedTopUpAmount = await this.drainingDeploymentService.calculateTopUpAmountForDseqAndUserId(params.dseq, params.userId); return { ...params, estimatedTopUpAmount, topUpFrequencyMs: this.topUpFrequencyMs }; } diff --git a/apps/api/src/deployment/services/draining-deployment/draining-deployment.service.spec.ts b/apps/api/src/deployment/services/draining-deployment/draining-deployment.service.spec.ts index b7acc8eca..f113263db 100644 --- a/apps/api/src/deployment/services/draining-deployment/draining-deployment.service.spec.ts +++ b/apps/api/src/deployment/services/draining-deployment/draining-deployment.service.spec.ts @@ -1,28 +1,29 @@ import "@test/mocks/logger-service.mock"; +import { faker } from "@faker-js/faker"; + +import { UserWalletRepository } from "@src/billing/repositories"; import { BlockHttpService } from "@src/chain/services/block-http/block-http.service"; import { AutoTopUpDeployment, DeploymentSettingRepository } from "@src/deployment/repositories/deployment-setting/deployment-setting.repository"; import { LeaseRepository } from "@src/deployment/repositories/lease/lease.repository"; import { averageBlockCountInAnHour } from "@src/utils/constants"; +import { DeploymentConfigService } from "../deployment-config/deployment-config.service"; import { DrainingDeploymentService } from "./draining-deployment.service"; -import { MockConfigService } from "@test/mocks/config-service.mock"; +import { AkashAddressSeeder } from "@test/seeders/akash-address.seeder"; import { AutoTopUpDeploymentSeeder } from "@test/seeders/auto-top-up-deployment.seeder"; import { DrainingDeploymentSeeder } from "@test/seeders/draining-deployment.seeder"; +import { UserWalletSeeder } from "@test/seeders/user-wallet.seeder"; jest.mock("@akashnetwork/logging"); -type DeploymentConfigValues = { - AUTO_TOP_UP_JOB_INTERVAL_IN_H: number; - AUTO_TOP_UP_DEPLOYMENT_INTERVAL_IN_H: number; -}; - describe(DrainingDeploymentService.name, () => { let blockHttpService: jest.Mocked; let leaseRepository: jest.Mocked; + let userWalletRepository: jest.Mocked; let deploymentSettingRepository: jest.Mocked; let service: DrainingDeploymentService; - let config: MockConfigService; + let config: jest.Mocked; const CURRENT_BLOCK_HEIGHT = 7481457; beforeEach(() => { @@ -32,19 +33,29 @@ describe(DrainingDeploymentService.name, () => { } as Partial> as jest.Mocked; leaseRepository = { - findManyByDseqAndOwner: jest.fn() + findManyByDseqAndOwner: jest.fn(), + findOneByDseqAndOwner: jest.fn() } as Partial> as jest.Mocked; + userWalletRepository = { + findOneByUserId: jest.fn() + } as Partial> as jest.Mocked; + deploymentSettingRepository = { paginateAutoTopUpDeployments: jest.fn() } as Partial> as jest.Mocked; - config = new MockConfigService({ + const configValues = { AUTO_TOP_UP_JOB_INTERVAL_IN_H: 1, AUTO_TOP_UP_DEPLOYMENT_INTERVAL_IN_H: 3 - }); + }; + + config = { + get: jest.fn().mockImplementation((key: keyof typeof configValues) => configValues[key]), + config: configValues + } as unknown as jest.Mocked; - service = new DrainingDeploymentService(blockHttpService, leaseRepository, deploymentSettingRepository, config); + service = new DrainingDeploymentService(blockHttpService, leaseRepository, userWalletRepository, deploymentSettingRepository, config); }); describe("paginate", () => { @@ -156,4 +167,51 @@ describe(DrainingDeploymentService.name, () => { }); }); }); + + describe("calculateTopUpAmountForDseqAndUserId", () => { + const userId = faker.string.uuid(); + const dseq = faker.string.numeric(6); + const address = AkashAddressSeeder.create(); + const userWallet = UserWalletSeeder.create({ address }); + const expectedTopUpAmount = 100000; + + beforeEach(() => { + userWalletRepository.findOneByUserId.mockResolvedValue(userWallet); + jest.spyOn(service, "calculateTopUpAmount").mockResolvedValue(expectedTopUpAmount); + }); + + it("should calculate top up amount for valid deployment", async () => { + const deployment = DrainingDeploymentSeeder.create(); + leaseRepository.findOneByDseqAndOwner.mockResolvedValue(deployment); + + const amount = await service.calculateTopUpAmountForDseqAndUserId(dseq, userId); + + expect(userWalletRepository.findOneByUserId).toHaveBeenCalledWith(userId); + expect(leaseRepository.findOneByDseqAndOwner).toHaveBeenCalledWith(dseq, address); + expect(service.calculateTopUpAmount).toHaveBeenCalledWith(deployment); + expect(amount).toBe(expectedTopUpAmount); + }); + + it("should return 0 if user wallet not found", async () => { + userWalletRepository.findOneByUserId.mockResolvedValue(null); + + const amount = await service.calculateTopUpAmountForDseqAndUserId(dseq, userId); + + expect(userWalletRepository.findOneByUserId).toHaveBeenCalledWith(userId); + expect(leaseRepository.findOneByDseqAndOwner).not.toHaveBeenCalled(); + expect(service.calculateTopUpAmount).not.toHaveBeenCalled(); + expect(amount).toBe(0); + }); + + it("should return 0 if lease not found", async () => { + leaseRepository.findOneByDseqAndOwner.mockResolvedValue(null); + + const amount = await service.calculateTopUpAmountForDseqAndUserId(dseq, userId); + + expect(userWalletRepository.findOneByUserId).toHaveBeenCalledWith(userId); + expect(leaseRepository.findOneByDseqAndOwner).toHaveBeenCalledWith(dseq, address); + expect(service.calculateTopUpAmount).not.toHaveBeenCalled(); + expect(amount).toBe(0); + }); + }); }); diff --git a/apps/api/src/deployment/services/draining-deployment/draining-deployment.service.ts b/apps/api/src/deployment/services/draining-deployment/draining-deployment.service.ts index 5ddd16b4e..21cb90597 100644 --- a/apps/api/src/deployment/services/draining-deployment/draining-deployment.service.ts +++ b/apps/api/src/deployment/services/draining-deployment/draining-deployment.service.ts @@ -1,6 +1,7 @@ import keyBy from "lodash/keyBy"; import { singleton } from "tsyringe"; +import { UserWalletRepository } from "@src/billing/repositories"; import { BlockHttpService } from "@src/chain/services/block-http/block-http.service"; import { AutoTopUpDeployment, DeploymentSettingRepository } from "@src/deployment/repositories/deployment-setting/deployment-setting.repository"; import { DrainingDeploymentOutput, LeaseRepository } from "@src/deployment/repositories/lease/lease.repository"; @@ -16,6 +17,7 @@ export class DrainingDeploymentService { constructor( private readonly blockHttpService: BlockHttpService, private readonly leaseRepository: LeaseRepository, + private readonly userWalletRepository: UserWalletRepository, private readonly deploymentSettingRepository: DeploymentSettingRepository, private readonly config: DeploymentConfigService ) {} @@ -47,8 +49,14 @@ export class DrainingDeploymentService { }); } - async calculateTopUpAmountForDseqAndOwner(dseq: string, owner: string): Promise { - const deploymentSetting = await this.leaseRepository.findOneByDseqAndOwner(dseq, owner); + async calculateTopUpAmountForDseqAndUserId(dseq: string, userId: string): Promise { + const userWallet = await this.userWalletRepository.findOneByUserId(userId); + + if (!userWallet) { + return 0; + } + + const deploymentSetting = await this.leaseRepository.findOneByDseqAndOwner(dseq, userWallet.address); if (!deploymentSetting) { return 0; diff --git a/apps/api/test/functional/deployment-setting.spec.ts b/apps/api/test/functional/deployment-setting.spec.ts index fe41aa9c8..e13f197fe 100644 --- a/apps/api/test/functional/deployment-setting.spec.ts +++ b/apps/api/test/functional/deployment-setting.spec.ts @@ -74,7 +74,7 @@ describe("Deployment Settings", () => { }); it("should return deployment settings if found", async () => { - const { token, user } = await walletService.createUserAndWallet(); + const { token, user, wallet } = await walletService.createUserAndWallet(); const dseq = faker.string.numeric(); const settings = await deploymentSettingRepository.create({ @@ -102,7 +102,7 @@ describe("Deployment Settings", () => { topUpFrequencyMs: expect.any(Number) } }); - expect(leaseRepository.findOneByDseqAndOwner).toHaveBeenCalledWith(dseq, user.id); + expect(leaseRepository.findOneByDseqAndOwner).toHaveBeenCalledWith(dseq, wallet.address); }); }); @@ -153,7 +153,7 @@ describe("Deployment Settings", () => { }); it("should create deployment settings", async () => { - const { token, user } = await walletService.createUserAndWallet(); + const { token, user, wallet } = await walletService.createUserAndWallet(); const dseq = faker.string.numeric(); const response = await app.request("/v1/deployment-settings", { @@ -186,7 +186,7 @@ describe("Deployment Settings", () => { dseq, autoTopUpEnabled: true }); - expect(leaseRepository.findOneByDseqAndOwner).toHaveBeenCalledWith(dseq, user.id); + expect(leaseRepository.findOneByDseqAndOwner).toHaveBeenCalledWith(dseq, wallet.address); }); }); @@ -208,7 +208,7 @@ describe("Deployment Settings", () => { }); it("should create and return new setting if not found", async () => { - const { token, user } = await walletService.createUserAndWallet(); + const { token, user, wallet } = await walletService.createUserAndWallet(); const dseq = faker.string.numeric(); const response = await app.request(`/v1/deployment-settings/${user.id}/${dseq}`, { @@ -239,7 +239,7 @@ describe("Deployment Settings", () => { dseq, autoTopUpEnabled: true }); - expect(leaseRepository.findOneByDseqAndOwner).toHaveBeenCalledWith(dseq, user.id); + expect(leaseRepository.findOneByDseqAndOwner).toHaveBeenCalledWith(dseq, wallet.address); }); it("should return 404 when updating other user's deployment settings", async () => { @@ -273,7 +273,7 @@ describe("Deployment Settings", () => { }); it("should update deployment settings", async () => { - const { token, user } = await walletService.createUserAndWallet(); + const { token, user, wallet } = await walletService.createUserAndWallet(); const dseq = faker.string.numeric(); const settings = await deploymentSettingRepository.create({ @@ -312,7 +312,7 @@ describe("Deployment Settings", () => { dseq, autoTopUpEnabled: true }); - expect(leaseRepository.findOneByDseqAndOwner).toHaveBeenCalledWith(dseq, user.id); + expect(leaseRepository.findOneByDseqAndOwner).toHaveBeenCalledWith(dseq, wallet.address); }); }); });