Skip to content

Commit e1dd02c

Browse files
authored
[ENG-1623] Do not return error when pnl/parentSubaccountNumber returns empty array (#3291)
1 parent d227095 commit e1dd02c

File tree

3 files changed

+162
-7
lines changed

3 files changed

+162
-7
lines changed

indexer/packages/postgres/__tests__/stores/pnl-table.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,4 +853,68 @@ describe('Pnl store', () => {
853853
expect(minHeight).toBeGreaterThanOrEqual(2000);
854854
expect(maxHeight).toBeLessThanOrEqual(4000);
855855
});
856+
857+
it('Returns empty array when no data exists for multiple subaccounts with date filter', async () => {
858+
const subaccountIds = [defaultSubaccountId, defaultSubaccountId2];
859+
860+
// Create some records but with dates after our filter
861+
const records = [];
862+
for (let i = 0; i < subaccountIds.length; i++) {
863+
records.push({
864+
...defaultPnl,
865+
subaccountId: subaccountIds[i],
866+
createdAt: '2024-01-01T00:00:00.000Z',
867+
createdAtHeight: (1000 + i).toString(),
868+
equity: (1000 + i).toString(),
869+
});
870+
}
871+
872+
await Promise.all(records.map((record) => PnlTable.create(record)));
873+
874+
// Query with a date filter that excludes all records
875+
const hourlyResults = await PnlTable.findAllHourlyAggregate(
876+
{
877+
subaccountId: subaccountIds,
878+
createdBeforeOrAt: '2023-01-01T00:00:00.000Z', // Before any records exist
879+
},
880+
[],
881+
{},
882+
);
883+
884+
expect(hourlyResults.results).toEqual([]);
885+
886+
const dailyResults = await PnlTable.findAllDailyAggregate(
887+
{
888+
subaccountId: subaccountIds,
889+
createdBeforeOrAt: '2023-01-01T00:00:00.000Z', // Before any records exist
890+
},
891+
[],
892+
{},
893+
);
894+
895+
expect(dailyResults.results).toEqual([]);
896+
});
897+
898+
it('Returns empty array when no data exists for multiple subaccounts', async () => {
899+
// Use subaccount IDs that have no PNL records at all
900+
const subaccountIds = [defaultSubaccountId, defaultSubaccountId2];
901+
902+
// Don't create any records - just query
903+
904+
const hourlyResults = await PnlTable.findAllHourlyAggregate(
905+
{ subaccountId: subaccountIds },
906+
[],
907+
{},
908+
);
909+
910+
expect(hourlyResults.results).toEqual([]);
911+
912+
const dailyResults = await PnlTable.findAllDailyAggregate(
913+
{ subaccountId: subaccountIds },
914+
[],
915+
{},
916+
);
917+
918+
expect(dailyResults.results).toEqual([]);
919+
});
856920
});

indexer/services/comlink/__tests__/controllers/api/v4/pnl-controller.test.ts

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -979,7 +979,7 @@ describe('pnl-controller#V4', () => {
979979
expect(response.body).toEqual({
980980
errors: [
981981
{
982-
msg: 'No PnL data found for address nonexistentaddress and parentSubaccountNumber 0',
982+
msg: 'No subaccount found with address nonexistentaddress and parentSubaccountNumber 0',
983983
},
984984
],
985985
});
@@ -1222,5 +1222,92 @@ describe('pnl-controller#V4', () => {
12221222
expect(Number(lastDailyRecord.totalPnl)).toEqual(expectedTotalPnl);
12231223
expect(Number(lastDailyRecord.netTransfers)).toEqual(expectedNetTransfers);
12241224
}, 120000);
1225+
1226+
it('Get /pnl/parentSubaccountNumber returns empty array when date filter excludes all data', async () => {
1227+
await testMocks.seedData();
1228+
1229+
// Create PNL records with dates AFTER the filter date
1230+
const records = [];
1231+
const subaccountIds = [
1232+
testConstants.defaultSubaccountId,
1233+
testConstants.isolatedSubaccountId,
1234+
];
1235+
1236+
// Create records in 2024 (after our filter date of 2023-12-10)
1237+
for (const subaccountId of subaccountIds) {
1238+
records.push({
1239+
...testConstants.defaultPnl,
1240+
subaccountId,
1241+
createdAt: '2024-01-01T00:00:00.000Z',
1242+
createdAtHeight: '1000',
1243+
equity: '1000',
1244+
totalPnl: '100',
1245+
netTransfers: '500',
1246+
});
1247+
}
1248+
1249+
await Promise.all(records.map((record) => PnlTable.create(record)));
1250+
1251+
// Test with daily=true and createdBeforeOrAt that excludes all data
1252+
const dailyResponse: request.Response = await sendRequest({
1253+
type: RequestMethod.GET,
1254+
path: `/v4/pnl/parentSubaccountNumber?${getQueryString({
1255+
address: testConstants.defaultAddress,
1256+
parentSubaccountNumber: 0,
1257+
daily: 'true',
1258+
createdBeforeOrAt: '2023-12-10T23:59:59.000Z',
1259+
})}`,
1260+
});
1261+
1262+
// Should return 200 with empty array, not an error
1263+
expect(dailyResponse.status).toBe(200);
1264+
expect(dailyResponse.body.pnl).toEqual([]);
1265+
1266+
// Test with daily=false (hourly) and same filter
1267+
const hourlyResponse: request.Response = await sendRequest({
1268+
type: RequestMethod.GET,
1269+
path: `/v4/pnl/parentSubaccountNumber?${getQueryString({
1270+
address: testConstants.defaultAddress,
1271+
parentSubaccountNumber: 0,
1272+
daily: 'false',
1273+
createdBeforeOrAt: '2023-12-10T23:59:59.000Z',
1274+
})}`,
1275+
});
1276+
1277+
expect(hourlyResponse.status).toBe(200);
1278+
expect(hourlyResponse.body.pnl).toEqual([]);
1279+
});
1280+
1281+
it('Get /pnl/parentSubaccountNumber returns empty array when no PNL records exist', async () => {
1282+
await testMocks.seedData();
1283+
1284+
// Don't create any PNL records - subaccounts exist but have no PNL data
1285+
1286+
// Test with daily=true
1287+
const dailyResponse: request.Response = await sendRequest({
1288+
type: RequestMethod.GET,
1289+
path: `/v4/pnl/parentSubaccountNumber?${getQueryString({
1290+
address: testConstants.defaultAddress,
1291+
parentSubaccountNumber: 0,
1292+
daily: 'true',
1293+
})}`,
1294+
});
1295+
1296+
expect(dailyResponse.status).toBe(200);
1297+
expect(dailyResponse.body.pnl).toEqual([]);
1298+
1299+
// Test with daily=false (hourly)
1300+
const hourlyResponse: request.Response = await sendRequest({
1301+
type: RequestMethod.GET,
1302+
path: `/v4/pnl/parentSubaccountNumber?${getQueryString({
1303+
address: testConstants.defaultAddress,
1304+
parentSubaccountNumber: 0,
1305+
daily: 'false',
1306+
})}`,
1307+
});
1308+
1309+
expect(hourlyResponse.status).toBe(200);
1310+
expect(hourlyResponse.body.pnl).toEqual([]);
1311+
});
12251312
});
12261313
});

indexer/services/comlink/src/controllers/api/v4/pnl-controller.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,16 @@ class PnlController extends Controller {
126126
@Query() createdOnOrAfter?: IsoString,
127127
@Query() daily?: boolean,
128128
): Promise<PnlResponse> {
129+
// Check if the parent subaccount exists first
130+
const parentSubaccountId: string = SubaccountTable.uuid(address, parentSubaccountNumber);
131+
const parentSubaccount = await SubaccountTable.findById(parentSubaccountId);
132+
133+
if (parentSubaccount === undefined) {
134+
throw new NotFoundError(
135+
`No subaccount found with address ${address} and parentSubaccountNumber ${parentSubaccountNumber}`,
136+
);
137+
}
138+
129139
const childSubaccountIds: string[] = getChildSubaccountIds(address, parentSubaccountNumber);
130140

131141
const queryParams = {
@@ -158,12 +168,6 @@ class PnlController extends Controller {
158168
);
159169
}
160170

161-
if (pnlData.results.length === 0) {
162-
throw new NotFoundError(
163-
`No PnL data found for address ${address} and parentSubaccountNumber ${parentSubaccountNumber}`,
164-
);
165-
}
166-
167171
return {
168172
pnl: pnlData.results.map(pnlToResponseObject),
169173
totalResults: pnlData.total,

0 commit comments

Comments
 (0)