Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Job Launcher Server] Move withdrawal creation logic to payments module #3147

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
export enum ErrorJob {
NotFound = 'Job not found',
NotCreated = 'Job has not been created',
NotEnoughFunds = 'Not enough funds',
NotActiveCard = 'Credit card not found',
ManifestNotFound = 'Manifest not found',
ManifestValidationFailed = 'Manifest validation failed',
Expand Down Expand Up @@ -91,6 +90,7 @@ export enum ErrorPayment {
NotFound = 'Payment not found',
InvoiceNotFound = 'Invoice not found',
NotSuccess = 'Unsuccessful payment',
NotEnoughFunds = 'Not enough funds',
IntentNotCreated = 'Payment intent not created',
CardNotAssigned = 'Card not assigned',
SetupNotFound = 'Setup not found',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export const CVAT_JOB_TYPES = [
];

export const CANCEL_JOB_STATUSES = [
JobStatus.PENDING,
JobStatus.PAID,
JobStatus.FAILED,
JobStatus.LAUNCHED,
Expand Down
1 change: 0 additions & 1 deletion packages/apps/job-launcher/server/src/common/enums/job.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export enum JobStatus {
PENDING = 'pending',
PAID = 'paid',
CREATED = 'created',
FUNDED = 'funded',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ describe('TransformEnumInterceptor', () => {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue({
body: {
status: 'PENDING',
status: 'PAID',
userType: 'OPERATOR',
amount: 5,
address: '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e',
},
query: {
status: 'PENDING',
status: 'PAID',
userType: 'OPERATOR',
},
}),
Expand Down Expand Up @@ -97,9 +97,9 @@ describe('TransformEnumInterceptor', () => {

// Expectations
expect(request.query.userType).toBe('operator');
expect(request.query.status).toBe('pending');
expect(request.query.status).toBe('paid');
expect(request.query).toEqual({
status: 'pending',
status: 'paid',
userType: 'operator',
});
expect(callHandler.handle).toBeCalled(); // Ensure the handler is called
Expand Down Expand Up @@ -136,9 +136,9 @@ describe('TransformEnumInterceptor', () => {

// Expectations
expect(request.body.userType).toBe('operator'); // Should be transformed to lowercase
expect(request.body.status).toBe('pending'); // Should be transformed to lowercase
expect(request.body.status).toBe('paid'); // Should be transformed to lowercase
expect(request.body).toEqual({
status: 'pending',
status: 'paid',
userType: 'operator',
amount: 5,
address: '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e',
Expand Down Expand Up @@ -191,7 +191,7 @@ describe('TransformEnumInterceptor', () => {
getRequest: jest.fn().mockReturnValue({
body: {
transaction: {
status: 'PENDING',
status: 'PAID',
userType: 'OPERATOR',
address: '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e',
},
Expand All @@ -208,11 +208,11 @@ describe('TransformEnumInterceptor', () => {
const request = executionContext.switchToHttp().getRequest();

// Expectations
expect(request.body.transaction.status).toBe('pending'); // Nested enum should be transformed
expect(request.body.transaction.status).toBe('paid'); // Nested enum should be transformed
expect(request.body.transaction.userType).toBe('operator');
expect(request.body).toEqual({
transaction: {
status: 'pending',
status: 'paid',
userType: 'operator',
address: '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class RemovePendingJobStatus1741181015584 implements MigrationInterface {
name = 'RemovePendingJobStatus1741181015584';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TYPE "hmt"."jobs_status_enum"
RENAME TO "jobs_status_enum_old"
`);
await queryRunner.query(`
CREATE TYPE "hmt"."jobs_status_enum" AS ENUM(
'paid',
'created',
'funded',
'launched',
'partial',
'completed',
'failed',
'to_cancel',
'canceled'
)
`);
await queryRunner.query(`
ALTER TABLE "hmt"."jobs"
ALTER COLUMN "status" TYPE "hmt"."jobs_status_enum" USING "status"::"text"::"hmt"."jobs_status_enum"
`);
await queryRunner.query(`
DROP TYPE "hmt"."jobs_status_enum_old"
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE TYPE "hmt"."jobs_status_enum_old" AS ENUM(
'pending',
'paid',
'created',
'funded',
'launched',
'partial',
'completed',
'failed',
'to_cancel',
'canceled'
)
`);
await queryRunner.query(`
ALTER TABLE "hmt"."jobs"
ALTER COLUMN "status" TYPE "hmt"."jobs_status_enum_old" USING "status"::"text"::"hmt"."jobs_status_enum_old"
`);
await queryRunner.query(`
DROP TYPE "hmt"."jobs_status_enum"
`);
await queryRunner.query(`
ALTER TYPE "hmt"."jobs_status_enum_old"
RENAME TO "jobs_status_enum"
`);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -961,7 +961,7 @@ describe('CronJobService', () => {
id: 1,
chainId: ChainId.LOCALHOST,
escrowAddress: MOCK_ADDRESS,
status: JobStatus.PENDING,
status: JobStatus.PAID,
};

escrowEventMock = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export class JobEntity extends BaseEntity implements IJob {
public userId: number;

@OneToMany(() => PaymentEntity, (payment) => payment.job)
public payment: PaymentEntity;
public payments: PaymentEntity[];

@Column({ type: 'int', default: 0 })
public retriesCount: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export class JobRepository extends BaseRepository<JobEntity> {
where: {
userId,
status: Not(In([JobStatus.COMPLETED, JobStatus.CANCELED])),
payment: {
payments: {
source: paymentSource,
},
},
Expand All @@ -106,12 +106,7 @@ export class JobRepository extends BaseRepository<JobEntity> {

switch (data.status) {
case JobStatusFilter.PENDING:
statusFilter = [
JobStatus.PENDING,
JobStatus.PAID,
JobStatus.CREATED,
JobStatus.FUNDED,
];
statusFilter = [JobStatus.PAID, JobStatus.CREATED, JobStatus.FUNDED];
break;
case JobStatusFilter.CANCELED:
statusFilter = [JobStatus.TO_CANCEL, JobStatus.CANCELED];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@ import { createMock } from '@golevelup/ts-jest';
import { Encryption } from '@human-protocol/sdk';
import { ConfigService } from '@nestjs/config';
import { Test } from '@nestjs/testing';
import {
PaymentCurrency,
PaymentSource,
PaymentStatus,
PaymentType,
} from '../../common/enums/payment';
import { PaymentCurrency } from '../../common/enums/payment';
import {
JobRequestType,
JobStatus,
Expand Down Expand Up @@ -39,7 +34,6 @@ import { mul } from '../../common/utils/decimal';
describe('JobService', () => {
let jobService: JobService,
paymentService: PaymentService,
paymentRepository: PaymentRepository,
jobRepository: JobRepository,
rateService: RateService;

Expand Down Expand Up @@ -110,7 +104,6 @@ describe('JobService', () => {

jobService = moduleRef.get<JobService>(JobService);
paymentService = moduleRef.get<PaymentService>(PaymentService);
paymentRepository = moduleRef.get<PaymentRepository>(PaymentRepository);
rateService = moduleRef.get<RateService>(RateService);
jobRepository = moduleRef.get<JobRepository>(JobRepository);
});
Expand All @@ -123,12 +116,6 @@ describe('JobService', () => {

describe('Fortune', () => {
describe('Successful job creation', () => {
beforeAll(() => {
jest
.spyOn(paymentService, 'getUserBalanceByCurrency')
.mockResolvedValue(100000000);
});

afterEach(() => {
jest.clearAllMocks();
});
Expand Down Expand Up @@ -163,35 +150,31 @@ describe('JobService', () => {
fortuneJobDto,
);

expect(paymentRepository.createUnique).toHaveBeenCalledWith({
userId: userMock.id,
jobId: jobEntityMock.id,
source: PaymentSource.BALANCE,
type: PaymentType.WITHDRAWAL,
currency: fortuneJobDto.paymentCurrency,
amount: expect.any(Number),
rate: await rateService.getRate(
expect(paymentService.createWithdrawalPayment).toHaveBeenCalledWith(
userMock.id,
expect.any(Number),
fortuneJobDto.paymentCurrency,
await rateService.getRate(
fortuneJobDto.paymentCurrency,
PaymentCurrency.USD,
),
status: PaymentStatus.SUCCEEDED,
});
expect(jobRepository.createUnique).toHaveBeenCalledWith({
);
expect(jobRepository.updateOne).toHaveBeenCalledWith({
chainId: fortuneJobDto.chainId,
userId: userMock.id,
manifestUrl: MOCK_FILE_URL,
manifestHash: MOCK_FILE_HASH,
requestType: JobRequestType.FORTUNE,
fee: expect.any(Number),
fundAmount: fortuneJobDto.paymentAmount,
status: JobStatus.PENDING,
status: JobStatus.PAID,
waitUntil: expect.any(Date),
token: fortuneJobDto.escrowFundToken,
exchangeOracle: fortuneJobDto.exchangeOracle,
recordingOracle: fortuneJobDto.recordingOracle,
reputationOracle: fortuneJobDto.reputationOracle,
payments: expect.any(Array),
});
expect(jobRepository.updateOne).toHaveBeenCalledTimes(1);
});

it('should create a Fortune job successfully paid and funded with different currencies', async () => {
Expand Down Expand Up @@ -234,17 +217,13 @@ describe('JobService', () => {
fortuneJobDto,
);

expect(paymentRepository.createUnique).toHaveBeenCalledWith({
userId: userMock.id,
jobId: jobEntityMock.id,
source: PaymentSource.BALANCE,
type: PaymentType.WITHDRAWAL,
currency: fortuneJobDto.paymentCurrency,
amount: expect.any(Number),
rate: paymentToUsdRate,
status: PaymentStatus.SUCCEEDED,
});
expect(jobRepository.createUnique).toHaveBeenCalledWith({
expect(paymentService.createWithdrawalPayment).toHaveBeenCalledWith(
userMock.id,
expect.any(Number),
fortuneJobDto.paymentCurrency,
paymentToUsdRate,
);
expect(jobRepository.updateOne).toHaveBeenCalledWith({
chainId: fortuneJobDto.chainId,
userId: userMock.id,
manifestUrl: MOCK_FILE_URL,
Expand All @@ -255,14 +234,14 @@ describe('JobService', () => {
mul(fortuneJobDto.paymentAmount, paymentToUsdRate),
usdToTokenRate,
),
status: JobStatus.PENDING,
status: JobStatus.PAID,
waitUntil: expect.any(Date),
token: fortuneJobDto.escrowFundToken,
exchangeOracle: fortuneJobDto.exchangeOracle,
recordingOracle: fortuneJobDto.recordingOracle,
reputationOracle: fortuneJobDto.reputationOracle,
payments: expect.any(Array),
});
expect(jobRepository.updateOne).toHaveBeenCalledTimes(1);
});
});
});
Expand Down
Loading