@mastra/pg 0.10.2-alpha.0 → 0.10.2-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,15 @@
1
1
  import { randomUUID } from 'crypto';
2
- import { createSampleEval, createSampleTraceForDB } from '@internal/storage-test-utils';
3
- import type { MastraMessageV1 } from '@mastra/core/memory';
2
+ import {
3
+ createSampleEval,
4
+ createSampleTraceForDB,
5
+ createSampleThread,
6
+ createSampleMessageV1,
7
+ createSampleMessageV2,
8
+ createSampleWorkflowSnapshot,
9
+ resetRole,
10
+ } from '@internal/storage-test-utils';
11
+ import type { MastraMessageV1, StorageThreadType } from '@mastra/core/memory';
12
+ import type { StorageColumn, TABLE_NAMES } from '@mastra/core/storage';
4
13
  import {
5
14
  TABLE_WORKFLOW_SNAPSHOT,
6
15
  TABLE_MESSAGES,
@@ -27,58 +36,6 @@ const connectionString = `postgresql://${TEST_CONFIG.user}:${TEST_CONFIG.passwor
27
36
 
28
37
  vi.setConfig({ testTimeout: 60_000, hookTimeout: 60_000 });
29
38
 
30
- // Sample test data factory functions
31
- const createSampleThread = () => ({
32
- id: `thread-${randomUUID()}`,
33
- resourceId: `resource-${randomUUID()}`,
34
- title: 'Test Thread',
35
- createdAt: new Date(),
36
- updatedAt: new Date(),
37
- metadata: { key: 'value' },
38
- });
39
-
40
- let role: 'user' | 'assistant' = 'assistant';
41
- const getRole = () => {
42
- if (role === `user`) role = `assistant`;
43
- else role = `user`;
44
- return role;
45
- };
46
- const createSampleMessage = (threadId: string): MastraMessageV1 => ({
47
- id: `msg-${randomUUID()}`,
48
- resourceId: `resource-${randomUUID()}`,
49
- role: getRole(),
50
- type: 'text',
51
- threadId,
52
- content: [{ type: 'text', text: 'Hello' }],
53
- createdAt: new Date(),
54
- });
55
-
56
- const createSampleWorkflowSnapshot = (status: WorkflowRunState['context'][string]['status'], createdAt?: Date) => {
57
- const runId = `run-${randomUUID()}`;
58
- const stepId = `step-${randomUUID()}`;
59
- const timestamp = createdAt || new Date();
60
- const snapshot = {
61
- result: { success: true },
62
- value: {},
63
- context: {
64
- [stepId]: {
65
- status,
66
- payload: {},
67
- error: undefined,
68
- startedAt: timestamp.getTime(),
69
- endedAt: new Date(timestamp.getTime() + 15000).getTime(),
70
- },
71
- input: {},
72
- },
73
- serializedStepGraph: [],
74
- activePaths: [],
75
- suspendedPaths: {},
76
- runId,
77
- timestamp: timestamp.getTime(),
78
- } as unknown as WorkflowRunState;
79
- return { snapshot, runId, stepId };
80
- };
81
-
82
39
  const checkWorkflowSnapshot = (snapshot: WorkflowRunState | string, stepId: string, status: string) => {
83
40
  if (typeof snapshot === 'string') {
84
41
  throw new Error('Expected WorkflowRunState, got string');
@@ -210,7 +167,7 @@ describe('PostgresStore', () => {
210
167
  await store.saveThread({ thread });
211
168
 
212
169
  // Add some messages
213
- const messages = [createSampleMessage(thread.id), createSampleMessage(thread.id)];
170
+ const messages = [createSampleMessageV1({ threadId: thread.id }), createSampleMessageV1({ threadId: thread.id })];
214
171
  await store.saveMessages({ messages });
215
172
 
216
173
  await store.deleteThread({ threadId: thread.id });
@@ -229,7 +186,7 @@ describe('PostgresStore', () => {
229
186
  const thread = createSampleThread();
230
187
  await store.saveThread({ thread });
231
188
 
232
- const messages = [createSampleMessage(thread.id), createSampleMessage(thread.id)];
189
+ const messages = [createSampleMessageV1({ threadId: thread.id }), createSampleMessageV1({ threadId: thread.id })];
233
190
 
234
191
  // Save messages
235
192
  const savedMessages = await store.saveMessages({ messages });
@@ -254,24 +211,18 @@ describe('PostgresStore', () => {
254
211
  const thread = createSampleThread();
255
212
  await store.saveThread({ thread });
256
213
 
257
- const messages = [
258
- { ...createSampleMessage(thread.id), content: [{ type: 'text', text: 'First' }] },
259
- {
260
- ...createSampleMessage(thread.id),
261
- content: [{ type: 'text', text: 'Second' }],
262
- },
263
- { ...createSampleMessage(thread.id), content: [{ type: 'text', text: 'Third' }] },
264
- ] satisfies MastraMessageV1[];
214
+ const messageContent = ['First', 'Second', 'Third'];
265
215
 
266
- await store.saveMessages({ messages });
216
+ const messages = messageContent.map(content => createSampleMessageV2({ threadId: thread.id, content }));
267
217
 
268
- const retrievedMessages = await store.getMessages({ threadId: thread.id, format: 'v1' });
218
+ await store.saveMessages({ messages, format: 'v2' });
219
+
220
+ const retrievedMessages = await store.getMessages({ threadId: thread.id, format: 'v2' });
269
221
  expect(retrievedMessages).toHaveLength(3);
270
222
 
271
223
  // Verify order is maintained
272
224
  retrievedMessages.forEach((msg, idx) => {
273
- // @ts-expect-error
274
- expect(msg.content[0].text).toBe(messages[idx].content[0].text);
225
+ expect((msg.content.parts[0] as any).text).toEqual(messageContent[idx]);
275
226
  });
276
227
  });
277
228
 
@@ -280,8 +231,8 @@ describe('PostgresStore', () => {
280
231
  await store.saveThread({ thread });
281
232
 
282
233
  const messages = [
283
- createSampleMessage(thread.id),
284
- { ...createSampleMessage(thread.id), id: null } as any, // This will cause an error
234
+ createSampleMessageV1({ threadId: thread.id }),
235
+ { ...createSampleMessageV1({ threadId: thread.id }), id: null } as any, // This will cause an error
285
236
  ];
286
237
 
287
238
  await expect(store.saveMessages({ messages })).rejects.toThrow();
@@ -290,43 +241,6 @@ describe('PostgresStore', () => {
290
241
  const savedMessages = await store.getMessages({ threadId: thread.id });
291
242
  expect(savedMessages).toHaveLength(0);
292
243
  });
293
-
294
- it('should filter by date with pagination for getMessages', async () => {
295
- const thread = createSampleThread();
296
- await store.saveThread({ thread });
297
- const now = new Date();
298
- const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
299
- const dayBeforeYesterday = new Date(now.getTime() - 48 * 60 * 60 * 1000);
300
-
301
- const createMsgAtDate = (date: Date) => {
302
- return store.saveMessages({ messages: [{ ...createSampleMessage(thread.id), createdAt: date }] });
303
- };
304
- await Promise.all([
305
- createMsgAtDate(dayBeforeYesterday),
306
- createMsgAtDate(dayBeforeYesterday),
307
- createMsgAtDate(yesterday),
308
- createMsgAtDate(yesterday),
309
- createMsgAtDate(yesterday),
310
- createMsgAtDate(now),
311
- createMsgAtDate(now),
312
- ]);
313
-
314
- const resultPage = await store.getMessages({
315
- threadId: thread.id,
316
- fromDate: yesterday,
317
- page: 0,
318
- perPage: 3,
319
- format: 'v1',
320
- });
321
- expect(resultPage.total).toBe(5);
322
- expect(resultPage.messages).toHaveLength(3);
323
-
324
- expect(new Date((resultPage.messages[0] as MastraMessageV1).createdAt).toISOString()).toBe(now.toISOString());
325
- expect(new Date((resultPage.messages[1] as MastraMessageV1).createdAt).toISOString()).toBe(now.toISOString());
326
- expect(new Date((resultPage.messages[2] as MastraMessageV1).createdAt).toISOString()).toBe(
327
- yesterday.toISOString(),
328
- );
329
- });
330
244
  });
331
245
 
332
246
  describe('Edge Cases and Error Handling', () => {
@@ -887,6 +801,96 @@ describe('PostgresStore', () => {
887
801
  });
888
802
  });
889
803
 
804
+ describe('alterTable', () => {
805
+ const TEST_TABLE = 'test_alter_table';
806
+ const BASE_SCHEMA = {
807
+ id: { type: 'integer', primaryKey: true, nullable: false },
808
+ name: { type: 'text', nullable: true },
809
+ } as Record<string, StorageColumn>;
810
+
811
+ beforeEach(async () => {
812
+ await store.createTable({ tableName: TEST_TABLE as TABLE_NAMES, schema: BASE_SCHEMA });
813
+ });
814
+
815
+ afterEach(async () => {
816
+ await store.clearTable({ tableName: TEST_TABLE as TABLE_NAMES });
817
+ });
818
+
819
+ it('adds a new column to an existing table', async () => {
820
+ await store.alterTable({
821
+ tableName: TEST_TABLE as TABLE_NAMES,
822
+ schema: { ...BASE_SCHEMA, age: { type: 'integer', nullable: true } },
823
+ ifNotExists: ['age'],
824
+ });
825
+
826
+ await store.insert({
827
+ tableName: TEST_TABLE as TABLE_NAMES,
828
+ record: { id: 1, name: 'Alice', age: 42 },
829
+ });
830
+
831
+ const row = await store.load<{ id: string; name: string; age?: number }>({
832
+ tableName: TEST_TABLE as TABLE_NAMES,
833
+ keys: { id: '1' },
834
+ });
835
+ expect(row?.age).toBe(42);
836
+ });
837
+
838
+ it('is idempotent when adding an existing column', async () => {
839
+ await store.alterTable({
840
+ tableName: TEST_TABLE as TABLE_NAMES,
841
+ schema: { ...BASE_SCHEMA, foo: { type: 'text', nullable: true } },
842
+ ifNotExists: ['foo'],
843
+ });
844
+ // Add the column again (should not throw)
845
+ await expect(
846
+ store.alterTable({
847
+ tableName: TEST_TABLE as TABLE_NAMES,
848
+ schema: { ...BASE_SCHEMA, foo: { type: 'text', nullable: true } },
849
+ ifNotExists: ['foo'],
850
+ }),
851
+ ).resolves.not.toThrow();
852
+ });
853
+
854
+ it('should add a default value to a column when using not null', async () => {
855
+ await store.insert({
856
+ tableName: TEST_TABLE as TABLE_NAMES,
857
+ record: { id: 1, name: 'Bob' },
858
+ });
859
+
860
+ await expect(
861
+ store.alterTable({
862
+ tableName: TEST_TABLE as TABLE_NAMES,
863
+ schema: { ...BASE_SCHEMA, text_column: { type: 'text', nullable: false } },
864
+ ifNotExists: ['text_column'],
865
+ }),
866
+ ).resolves.not.toThrow();
867
+
868
+ await expect(
869
+ store.alterTable({
870
+ tableName: TEST_TABLE as TABLE_NAMES,
871
+ schema: { ...BASE_SCHEMA, timestamp_column: { type: 'timestamp', nullable: false } },
872
+ ifNotExists: ['timestamp_column'],
873
+ }),
874
+ ).resolves.not.toThrow();
875
+
876
+ await expect(
877
+ store.alterTable({
878
+ tableName: TEST_TABLE as TABLE_NAMES,
879
+ schema: { ...BASE_SCHEMA, bigint_column: { type: 'bigint', nullable: false } },
880
+ ifNotExists: ['bigint_column'],
881
+ }),
882
+ ).resolves.not.toThrow();
883
+
884
+ await expect(
885
+ store.alterTable({
886
+ tableName: TEST_TABLE as TABLE_NAMES,
887
+ schema: { ...BASE_SCHEMA, jsonb_column: { type: 'jsonb', nullable: false } },
888
+ ifNotExists: ['jsonb_column'],
889
+ }),
890
+ ).resolves.not.toThrow();
891
+ });
892
+ });
893
+
890
894
  describe('Schema Support', () => {
891
895
  const customSchema = 'mastra_test';
892
896
  let customSchemaStore: PostgresStore;
@@ -1022,11 +1026,11 @@ describe('PostgresStore', () => {
1022
1026
  });
1023
1027
  await Promise.all(evalPromises);
1024
1028
 
1025
- const result = await store.getEvals({ agentName, limit: 5, offset: 10 });
1029
+ const result = await store.getEvals({ agentName, perPage: 5, page: 2 });
1026
1030
  expect(result.evals).toHaveLength(5);
1027
1031
  expect(result.total).toBe(15);
1028
- expect(result.page).toBeUndefined(); // Page is undefined for limit/offset
1029
- expect(result.perPage).toBeUndefined(); // PerPage is undefined for limit/offset
1032
+ expect(result.page).toBe(2);
1033
+ expect(result.perPage).toBe(5);
1030
1034
  expect(result.hasMore).toBe(false);
1031
1035
  });
1032
1036
 
@@ -1117,7 +1121,7 @@ describe('PostgresStore', () => {
1117
1121
  createEvalAtDate(now),
1118
1122
  ]);
1119
1123
 
1120
- const fromYesterday = await store.getEvals({ agentName, fromDate: yesterday, page: 0, perPage: 3 });
1124
+ const fromYesterday = await store.getEvals({ agentName, dateRange: { start: yesterday }, page: 0, perPage: 3 });
1121
1125
  expect(fromYesterday.total).toBe(7); // 3 yesterday + 4 now
1122
1126
  expect(fromYesterday.evals).toHaveLength(3);
1123
1127
  // Evals are sorted DESC, so first 3 are from 'now'
@@ -1127,7 +1131,9 @@ describe('PostgresStore', () => {
1127
1131
 
1128
1132
  const onlyDayBefore = await store.getEvals({
1129
1133
  agentName,
1130
- toDate: new Date(yesterday.getTime() - 1),
1134
+ dateRange: {
1135
+ end: new Date(yesterday.getTime() - 1),
1136
+ },
1131
1137
  page: 0,
1132
1138
  perPage: 5,
1133
1139
  });
@@ -1143,11 +1149,10 @@ describe('PostgresStore', () => {
1143
1149
  );
1144
1150
  await Promise.all(tracePromises);
1145
1151
 
1146
- const page1 = await store.getTraces({
1152
+ const page1 = await store.getTracesPaginated({
1147
1153
  scope: 'pg-test-scope',
1148
1154
  page: 0,
1149
1155
  perPage: 8,
1150
- returnPaginationResults: true,
1151
1156
  });
1152
1157
  expect(page1.traces).toHaveLength(8);
1153
1158
  expect(page1.total).toBe(18);
@@ -1155,11 +1160,10 @@ describe('PostgresStore', () => {
1155
1160
  expect(page1.perPage).toBe(8);
1156
1161
  expect(page1.hasMore).toBe(true);
1157
1162
 
1158
- const page3 = await store.getTraces({
1163
+ const page3 = await store.getTracesPaginated({
1159
1164
  scope: 'pg-test-scope',
1160
1165
  page: 2,
1161
1166
  perPage: 8,
1162
- returnPaginationResults: true,
1163
1167
  });
1164
1168
  expect(page3.traces).toHaveLength(2);
1165
1169
  expect(page3.total).toBe(18);
@@ -1181,12 +1185,11 @@ describe('PostgresStore', () => {
1181
1185
  );
1182
1186
  await Promise.all([...tracesWithAttr, ...tracesWithoutAttr]);
1183
1187
 
1184
- const prodTraces = await store.getTraces({
1188
+ const prodTraces = await store.getTracesPaginated({
1185
1189
  scope: 'pg-attr-scope',
1186
1190
  attributes: { environment: 'prod' },
1187
1191
  page: 0,
1188
1192
  perPage: 5,
1189
- returnPaginationResults: true,
1190
1193
  });
1191
1194
  expect(prodTraces.traces).toHaveLength(5);
1192
1195
  expect(prodTraces.total).toBe(8);
@@ -1210,12 +1213,13 @@ describe('PostgresStore', () => {
1210
1213
  store.insert({ tableName: TABLE_TRACES, record: createSampleTraceForDB('t5', scope, undefined, now) }),
1211
1214
  ]);
1212
1215
 
1213
- const fromYesterday = await store.getTraces({
1216
+ const fromYesterday = await store.getTracesPaginated({
1214
1217
  scope,
1215
- fromDate: yesterday,
1218
+ dateRange: {
1219
+ start: yesterday,
1220
+ },
1216
1221
  page: 0,
1217
1222
  perPage: 2,
1218
- returnPaginationResults: true,
1219
1223
  });
1220
1224
  expect(fromYesterday.total).toBe(4); // 2 yesterday + 2 now
1221
1225
  expect(fromYesterday.traces).toHaveLength(2);
@@ -1223,97 +1227,109 @@ describe('PostgresStore', () => {
1223
1227
  expect(new Date(t.createdAt).getTime()).toBeGreaterThanOrEqual(yesterday.getTime()),
1224
1228
  );
1225
1229
 
1226
- const onlyNow = await store.getTraces({
1230
+ const onlyNow = await store.getTracesPaginated({
1227
1231
  scope,
1228
- fromDate: now,
1229
- toDate: now,
1232
+ dateRange: {
1233
+ start: now,
1234
+ end: now,
1235
+ },
1230
1236
  page: 0,
1231
1237
  perPage: 5,
1232
- returnPaginationResults: true,
1233
1238
  });
1234
1239
  expect(onlyNow.total).toBe(2);
1235
1240
  expect(onlyNow.traces).toHaveLength(2);
1236
1241
  });
1237
-
1238
- it('should return array when returnPaginationResults is false or undefined', async () => {
1239
- await store.insert({ tableName: TABLE_TRACES, record: createSampleTraceForDB('trace-arr', 'pg-array-scope') });
1240
- const tracesArray = await store.getTraces({ scope: 'pg-array-scope', page: 0, perPage: 5 }); // returnPaginationResults is undefined
1241
- expect(Array.isArray(tracesArray)).toBe(true);
1242
- expect(tracesArray.length).toBe(1);
1243
- });
1244
1242
  });
1245
1243
 
1246
1244
  describe('getMessages with pagination', () => {
1247
1245
  it('should return paginated messages with total count', async () => {
1248
1246
  const thread = createSampleThread();
1249
1247
  await store.saveThread({ thread });
1250
- const messagePromises = Array.from({ length: 15 }, (_, i) =>
1251
- store.saveMessages({
1252
- messages: [{ ...createSampleMessage(thread.id), content: [{ type: 'text', text: `Message ${i + 1}` }] }],
1253
- }),
1254
- );
1255
- await Promise.all(messagePromises);
1248
+ // Reset role to 'assistant' before creating messages
1249
+ resetRole();
1250
+ // Create messages sequentially to ensure unique timestamps
1251
+ for (let i = 0; i < 15; i++) {
1252
+ const message = createSampleMessageV1({ threadId: thread.id, content: `Message ${i + 1}` });
1253
+ await store.saveMessages({
1254
+ messages: [message],
1255
+ });
1256
+ await new Promise(r => setTimeout(r, 5));
1257
+ }
1256
1258
 
1257
- const page1 = await store.getMessages({ threadId: thread.id, page: 0, perPage: 5, format: 'v1' });
1259
+ const page1 = await store.getMessagesPaginated({
1260
+ threadId: thread.id,
1261
+ selectBy: { pagination: { page: 0, perPage: 5 } },
1262
+ format: 'v2',
1263
+ });
1264
+ console.log(page1);
1258
1265
  expect(page1.messages).toHaveLength(5);
1259
1266
  expect(page1.total).toBe(15);
1260
1267
  expect(page1.page).toBe(0);
1261
1268
  expect(page1.perPage).toBe(5);
1262
1269
  expect(page1.hasMore).toBe(true);
1263
1270
 
1264
- const page3 = await store.getMessages({ threadId: thread.id, page: 2, perPage: 5, format: 'v1' });
1271
+ const page3 = await store.getMessagesPaginated({
1272
+ threadId: thread.id,
1273
+ selectBy: { pagination: { page: 2, perPage: 5 } },
1274
+ format: 'v2',
1275
+ });
1265
1276
  expect(page3.messages).toHaveLength(5);
1266
1277
  expect(page3.total).toBe(15);
1267
1278
  expect(page3.hasMore).toBe(false);
1268
1279
  });
1269
1280
 
1270
1281
  it('should filter by date with pagination for getMessages', async () => {
1271
- const thread = createSampleThread();
1272
- await store.saveThread({ thread });
1282
+ const threadData = createSampleThread();
1283
+ const thread = await store.saveThread({ thread: threadData as StorageThreadType });
1273
1284
  const now = new Date();
1274
- const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
1275
- const dayBeforeYesterday = new Date(now.getTime() - 48 * 60 * 60 * 1000);
1276
-
1277
- const createMsgAtDate = (date: Date) => {
1278
- return store.saveMessages({ messages: [{ ...createSampleMessage(thread.id), createdAt: date }] });
1279
- };
1280
- await Promise.all([
1281
- createMsgAtDate(dayBeforeYesterday),
1282
- createMsgAtDate(dayBeforeYesterday),
1283
- createMsgAtDate(yesterday),
1284
- createMsgAtDate(yesterday),
1285
- createMsgAtDate(yesterday),
1286
- createMsgAtDate(now),
1287
- createMsgAtDate(now),
1288
- ]);
1285
+ const yesterday = new Date(
1286
+ now.getFullYear(),
1287
+ now.getMonth(),
1288
+ now.getDate() - 1,
1289
+ now.getHours(),
1290
+ now.getMinutes(),
1291
+ now.getSeconds(),
1292
+ );
1293
+ const dayBeforeYesterday = new Date(
1294
+ now.getFullYear(),
1295
+ now.getMonth(),
1296
+ now.getDate() - 2,
1297
+ now.getHours(),
1298
+ now.getMinutes(),
1299
+ now.getSeconds(),
1300
+ );
1289
1301
 
1290
- const resultPage = await store.getMessages({
1302
+ // Ensure timestamps are distinct for reliable sorting by creating them with a slight delay for testing clarity
1303
+ const messagesToSave: MastraMessageV1[] = [];
1304
+ messagesToSave.push(createSampleMessageV1({ threadId: thread.id, createdAt: dayBeforeYesterday }));
1305
+ await new Promise(r => setTimeout(r, 5));
1306
+ messagesToSave.push(createSampleMessageV1({ threadId: thread.id, createdAt: dayBeforeYesterday }));
1307
+ await new Promise(r => setTimeout(r, 5));
1308
+ messagesToSave.push(createSampleMessageV1({ threadId: thread.id, createdAt: yesterday }));
1309
+ await new Promise(r => setTimeout(r, 5));
1310
+ messagesToSave.push(createSampleMessageV1({ threadId: thread.id, createdAt: yesterday }));
1311
+ await new Promise(r => setTimeout(r, 5));
1312
+ messagesToSave.push(createSampleMessageV1({ threadId: thread.id, createdAt: now }));
1313
+ await new Promise(r => setTimeout(r, 5));
1314
+ messagesToSave.push(createSampleMessageV1({ threadId: thread.id, createdAt: now }));
1315
+
1316
+ await store.saveMessages({ messages: messagesToSave, format: 'v1' });
1317
+ // Total 6 messages: 2 now, 2 yesterday, 2 dayBeforeYesterday (oldest to newest)
1318
+
1319
+ const fromYesterday = await store.getMessagesPaginated({
1291
1320
  threadId: thread.id,
1292
- fromDate: yesterday,
1293
- page: 0,
1294
- perPage: 3,
1295
- format: 'v1',
1321
+ selectBy: { pagination: { page: 0, perPage: 3, dateRange: { start: yesterday } } },
1322
+ format: 'v2',
1296
1323
  });
1297
- expect(resultPage.total).toBe(5);
1298
- expect(resultPage.messages).toHaveLength(3);
1299
-
1300
- expect(new Date((resultPage.messages[0] as MastraMessageV1).createdAt).toISOString()).toBe(now.toISOString());
1301
- expect(new Date((resultPage.messages[1] as MastraMessageV1).createdAt).toISOString()).toBe(now.toISOString());
1302
- expect(new Date((resultPage.messages[2] as MastraMessageV1).createdAt).toISOString()).toBe(
1303
- yesterday.toISOString(),
1304
- );
1305
- });
1306
-
1307
- it('should maintain backward compatibility for getMessages (no pagination params)', async () => {
1308
- const thread = createSampleThread();
1309
- await store.saveThread({ thread });
1310
- await store.saveMessages({ messages: [createSampleMessage(thread.id)] });
1311
-
1312
- const messages = await store.getMessages({ threadId: thread.id, format: 'v1' });
1313
- expect(Array.isArray(messages)).toBe(true);
1314
- expect(messages.length).toBe(1);
1315
- // @ts-expect-error - messages should not have pagination properties
1316
- expect(messages.total).toBeUndefined();
1324
+ expect(fromYesterday.total).toBe(4);
1325
+ expect(fromYesterday.messages).toHaveLength(3);
1326
+ const firstMessageTime = new Date((fromYesterday.messages[0] as MastraMessageV1).createdAt).getTime();
1327
+ expect(firstMessageTime).toBeGreaterThanOrEqual(new Date(yesterday.toISOString()).getTime());
1328
+ if (fromYesterday.messages.length > 0) {
1329
+ expect(new Date((fromYesterday.messages[0] as MastraMessageV1).createdAt).toISOString().slice(0, 10)).toEqual(
1330
+ yesterday.toISOString().slice(0, 10),
1331
+ );
1332
+ }
1317
1333
  });
1318
1334
  });
1319
1335
 
@@ -1325,28 +1341,30 @@ describe('PostgresStore', () => {
1325
1341
  );
1326
1342
  await Promise.all(threadPromises);
1327
1343
 
1328
- const page1 = await store.getThreadsByResourceId({ resourceId, page: 0, perPage: 7 });
1344
+ const page1 = await store.getThreadsByResourceIdPaginated({ resourceId, page: 0, perPage: 7 });
1329
1345
  expect(page1.threads).toHaveLength(7);
1330
1346
  expect(page1.total).toBe(17);
1331
1347
  expect(page1.page).toBe(0);
1332
1348
  expect(page1.perPage).toBe(7);
1333
1349
  expect(page1.hasMore).toBe(true);
1334
1350
 
1335
- const page3 = await store.getThreadsByResourceId({ resourceId, page: 2, perPage: 7 });
1351
+ const page3 = await store.getThreadsByResourceIdPaginated({ resourceId, page: 2, perPage: 7 });
1336
1352
  expect(page3.threads).toHaveLength(3); // 17 total, 7 per page, 3rd page has 17 - 2*7 = 3
1337
1353
  expect(page3.total).toBe(17);
1338
1354
  expect(page3.hasMore).toBe(false);
1339
1355
  });
1340
1356
 
1341
- it('should return array when no pagination params for getThreadsByResourceId', async () => {
1357
+ it('should return paginated results when no pagination params for getThreadsByResourceId', async () => {
1342
1358
  const resourceId = `pg-non-paginated-resource-${randomUUID()}`;
1343
1359
  await store.saveThread({ thread: { ...createSampleThread(), resourceId } });
1344
1360
 
1345
- const threads = await store.getThreadsByResourceId({ resourceId });
1346
- expect(Array.isArray(threads)).toBe(true);
1347
- expect(threads.length).toBe(1);
1348
- // @ts-expect-error - threads should not have pagination properties
1349
- expect(threads.total).toBeUndefined();
1361
+ const results = await store.getThreadsByResourceIdPaginated({ resourceId });
1362
+ expect(Array.isArray(results.threads)).toBe(true);
1363
+ expect(results.threads.length).toBe(1);
1364
+ expect(results.total).toBe(1);
1365
+ expect(results.page).toBe(0);
1366
+ expect(results.perPage).toBe(100);
1367
+ expect(results.hasMore).toBe(false);
1350
1368
  });
1351
1369
  });
1352
1370
  });