Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions .github/workflows/indexer-build-and-push-dev-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on: # yamllint disable-line rule:truthy
- main
- 'release/indexer/v[0-9]+.[0-9]+.x' # e.g. release/indexer/v0.1.x
- 'release/indexer/v[0-9]+.x' # e.g. release/indexer/v1.x
- davidli/pnl_endpoint
# TODO(DEC-837): Customize github build and push to ECR by service with paths

jobs:
Expand Down
64 changes: 64 additions & 0 deletions indexer/packages/postgres/__tests__/stores/pnl-table.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -853,4 +853,68 @@ describe('Pnl store', () => {
expect(minHeight).toBeGreaterThanOrEqual(2000);
expect(maxHeight).toBeLessThanOrEqual(4000);
});

it('Returns empty array when no data exists for multiple subaccounts with date filter', async () => {
const subaccountIds = [defaultSubaccountId, defaultSubaccountId2];

// Create some records but with dates after our filter
const records = [];
for (let i = 0; i < subaccountIds.length; i++) {
records.push({
...defaultPnl,
subaccountId: subaccountIds[i],
createdAt: '2024-01-01T00:00:00.000Z',
createdAtHeight: (1000 + i).toString(),
equity: (1000 + i).toString(),
});
}

await Promise.all(records.map((record) => PnlTable.create(record)));

// Query with a date filter that excludes all records
const hourlyResults = await PnlTable.findAllHourlyAggregate(
{
subaccountId: subaccountIds,
createdBeforeOrAt: '2023-01-01T00:00:00.000Z', // Before any records exist
},
[],
{},
);

expect(hourlyResults.results).toEqual([]);

const dailyResults = await PnlTable.findAllDailyAggregate(
{
subaccountId: subaccountIds,
createdBeforeOrAt: '2023-01-01T00:00:00.000Z', // Before any records exist
},
[],
{},
);

expect(dailyResults.results).toEqual([]);
});

it('Returns empty array when no data exists for multiple subaccounts', async () => {
// Use subaccount IDs that have no PNL records at all
const subaccountIds = [defaultSubaccountId, defaultSubaccountId2];

// Don't create any records - just query

const hourlyResults = await PnlTable.findAllHourlyAggregate(
{ subaccountId: subaccountIds },
[],
{},
);

expect(hourlyResults.results).toEqual([]);

const dailyResults = await PnlTable.findAllDailyAggregate(
{ subaccountId: subaccountIds },
[],
{},
);

expect(dailyResults.results).toEqual([]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -979,7 +979,7 @@ describe('pnl-controller#V4', () => {
expect(response.body).toEqual({
errors: [
{
msg: 'No PnL data found for address nonexistentaddress and parentSubaccountNumber 0',
msg: 'No subaccount found with address nonexistentaddress and parentSubaccountNumber 0',
},
],
});
Expand Down Expand Up @@ -1222,5 +1222,92 @@ describe('pnl-controller#V4', () => {
expect(Number(lastDailyRecord.totalPnl)).toEqual(expectedTotalPnl);
expect(Number(lastDailyRecord.netTransfers)).toEqual(expectedNetTransfers);
}, 120000);

it('Get /pnl/parentSubaccountNumber returns empty array when date filter excludes all data', async () => {
await testMocks.seedData();

// Create PNL records with dates AFTER the filter date
const records = [];
const subaccountIds = [
testConstants.defaultSubaccountId,
testConstants.isolatedSubaccountId,
];

// Create records in 2024 (after our filter date of 2023-12-10)
for (const subaccountId of subaccountIds) {
records.push({
...testConstants.defaultPnl,
subaccountId,
createdAt: '2024-01-01T00:00:00.000Z',
createdAtHeight: '1000',
equity: '1000',
totalPnl: '100',
netTransfers: '500',
});
}

await Promise.all(records.map((record) => PnlTable.create(record)));

// Test with daily=true and createdBeforeOrAt that excludes all data
const dailyResponse: request.Response = await sendRequest({
type: RequestMethod.GET,
path: `/v4/pnl/parentSubaccountNumber?${getQueryString({
address: testConstants.defaultAddress,
parentSubaccountNumber: 0,
daily: 'true',
createdBeforeOrAt: '2023-12-10T23:59:59.000Z',
})}`,
});

// Should return 200 with empty array, not an error
expect(dailyResponse.status).toBe(200);
expect(dailyResponse.body.pnl).toEqual([]);

// Test with daily=false (hourly) and same filter
const hourlyResponse: request.Response = await sendRequest({
type: RequestMethod.GET,
path: `/v4/pnl/parentSubaccountNumber?${getQueryString({
address: testConstants.defaultAddress,
parentSubaccountNumber: 0,
daily: 'false',
createdBeforeOrAt: '2023-12-10T23:59:59.000Z',
})}`,
});

expect(hourlyResponse.status).toBe(200);
expect(hourlyResponse.body.pnl).toEqual([]);
});

it('Get /pnl/parentSubaccountNumber returns empty array when no PNL records exist', async () => {
await testMocks.seedData();

// Don't create any PNL records - subaccounts exist but have no PNL data

// Test with daily=true
const dailyResponse: request.Response = await sendRequest({
type: RequestMethod.GET,
path: `/v4/pnl/parentSubaccountNumber?${getQueryString({
address: testConstants.defaultAddress,
parentSubaccountNumber: 0,
daily: 'true',
})}`,
});

expect(dailyResponse.status).toBe(200);
expect(dailyResponse.body.pnl).toEqual([]);

// Test with daily=false (hourly)
const hourlyResponse: request.Response = await sendRequest({
type: RequestMethod.GET,
path: `/v4/pnl/parentSubaccountNumber?${getQueryString({
address: testConstants.defaultAddress,
parentSubaccountNumber: 0,
daily: 'false',
})}`,
});

expect(hourlyResponse.status).toBe(200);
expect(hourlyResponse.body.pnl).toEqual([]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ class PnlController extends Controller {
@Query() createdOnOrAfter?: IsoString,
@Query() daily?: boolean,
): Promise<PnlResponse> {
// Check if the parent subaccount exists first
const parentSubaccountId: string = SubaccountTable.uuid(address, parentSubaccountNumber);
const parentSubaccount = await SubaccountTable.findById(parentSubaccountId);

if (parentSubaccount === undefined) {
throw new NotFoundError(
`No subaccount found with address ${address} and parentSubaccountNumber ${parentSubaccountNumber}`,
);
}

const childSubaccountIds: string[] = getChildSubaccountIds(address, parentSubaccountNumber);

const queryParams = {
Expand Down Expand Up @@ -158,12 +168,6 @@ class PnlController extends Controller {
);
}

if (pnlData.results.length === 0) {
throw new NotFoundError(
`No PnL data found for address ${address} and parentSubaccountNumber ${parentSubaccountNumber}`,
);
}

return {
pnl: pnlData.results.map(pnlToResponseObject),
totalResults: pnlData.total,
Expand Down
Loading