@mastra/upstash 0.1.6-alpha.1 → 0.1.6-alpha.3
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.
- package/.turbo/turbo-build.log +11 -6
- package/CHANGELOG.md +25 -0
- package/README.md +20 -1
- package/dist/_tsup-dts-rollup.d.cts +133 -0
- package/dist/_tsup-dts-rollup.d.ts +12 -8
- package/dist/index.cjs +480 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.js +8 -4
- package/package.json +9 -5
- package/src/storage/upstash.test.ts +332 -330
- package/src/vector/filter.ts +5 -5
- package/src/vector/index.test.ts +440 -150
- package/src/vector/index.ts +20 -21
|
@@ -1,387 +1,389 @@
|
|
|
1
1
|
import { MastraStorage } from '@mastra/core/storage';
|
|
2
|
-
import { describe, it, expect, beforeAll, beforeEach, afterAll } from 'vitest';
|
|
2
|
+
import { describe, it, expect, beforeAll, beforeEach, afterAll, vi } from 'vitest';
|
|
3
3
|
|
|
4
4
|
import { UpstashStore } from './index';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
() => {
|
|
9
|
-
let store: UpstashStore;
|
|
10
|
-
const testTableName = 'test_table';
|
|
11
|
-
const testTableName2 = 'test_table2';
|
|
12
|
-
|
|
13
|
-
beforeAll(async () => {
|
|
14
|
-
store = new UpstashStore({
|
|
15
|
-
url: 'http://localhost:8079',
|
|
16
|
-
token: 'test_token',
|
|
17
|
-
});
|
|
18
|
-
await store.init();
|
|
19
|
-
});
|
|
6
|
+
// Increase timeout for all tests in this file to 30 seconds
|
|
7
|
+
vi.setConfig({ testTimeout: 60_000, hookTimeout: 60_000 });
|
|
20
8
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
await store.clearTable({ tableName: MastraStorage.TABLE_THREADS });
|
|
26
|
-
await store.clearTable({ tableName: MastraStorage.TABLE_MESSAGES });
|
|
27
|
-
});
|
|
9
|
+
describe('UpstashStore', () => {
|
|
10
|
+
let store: UpstashStore;
|
|
11
|
+
const testTableName = 'test_table';
|
|
12
|
+
const testTableName2 = 'test_table2';
|
|
28
13
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
await store.createTable({
|
|
32
|
-
tableName: testTableName,
|
|
33
|
-
schema: {
|
|
34
|
-
id: { type: 'text', primaryKey: true },
|
|
35
|
-
data: { type: 'text', nullable: true },
|
|
36
|
-
},
|
|
37
|
-
});
|
|
14
|
+
beforeAll(async () => {
|
|
15
|
+
console.log('Initializing UpstashStore...');
|
|
38
16
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
17
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
18
|
+
store = new UpstashStore({
|
|
19
|
+
url: 'http://localhost:8079',
|
|
20
|
+
token: 'test_token',
|
|
21
|
+
});
|
|
44
22
|
|
|
45
|
-
|
|
46
|
-
|
|
23
|
+
await store.init();
|
|
24
|
+
console.log('UpstashStore initialized');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterAll(async () => {
|
|
28
|
+
// Clean up test tables
|
|
29
|
+
await store.clearTable({ tableName: testTableName });
|
|
30
|
+
await store.clearTable({ tableName: testTableName2 });
|
|
31
|
+
await store.clearTable({ tableName: MastraStorage.TABLE_THREADS });
|
|
32
|
+
await store.clearTable({ tableName: MastraStorage.TABLE_MESSAGES });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('Table Operations', () => {
|
|
36
|
+
it('should create a new table with schema', async () => {
|
|
37
|
+
await store.createTable({
|
|
38
|
+
tableName: testTableName,
|
|
39
|
+
schema: {
|
|
40
|
+
id: { type: 'text', primaryKey: true },
|
|
41
|
+
data: { type: 'text', nullable: true },
|
|
42
|
+
},
|
|
47
43
|
});
|
|
48
44
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
id: { type: 'text', primaryKey: true },
|
|
54
|
-
data: { type: 'text', nullable: true },
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// Verify both tables work independently
|
|
59
|
-
await store.insert({
|
|
60
|
-
tableName: testTableName2,
|
|
61
|
-
record: { id: 'test2', data: 'test-data-2' },
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const result = await store.load({ tableName: testTableName2, keys: { id: 'test2' } });
|
|
65
|
-
expect(result).toBeTruthy();
|
|
45
|
+
// Verify table exists by inserting and retrieving data
|
|
46
|
+
await store.insert({
|
|
47
|
+
tableName: testTableName,
|
|
48
|
+
record: { id: 'test1', data: 'test-data' },
|
|
66
49
|
});
|
|
67
|
-
});
|
|
68
50
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
});
|
|
51
|
+
const result = await store.load({ tableName: testTableName, keys: { id: 'test1' } });
|
|
52
|
+
expect(result).toBeTruthy();
|
|
53
|
+
});
|
|
73
54
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
updatedAt: now,
|
|
82
|
-
metadata: { key: 'value' },
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const savedThread = await store.saveThread({ thread });
|
|
86
|
-
expect(savedThread).toEqual(thread);
|
|
87
|
-
|
|
88
|
-
const retrievedThread = await store.getThreadById({ threadId: thread.id });
|
|
89
|
-
expect(retrievedThread).toEqual({
|
|
90
|
-
...thread,
|
|
91
|
-
createdAt: new Date(now.toISOString()),
|
|
92
|
-
updatedAt: new Date(now.toISOString()),
|
|
93
|
-
});
|
|
55
|
+
it('should handle multiple table creation', async () => {
|
|
56
|
+
await store.createTable({
|
|
57
|
+
tableName: testTableName2,
|
|
58
|
+
schema: {
|
|
59
|
+
id: { type: 'text', primaryKey: true },
|
|
60
|
+
data: { type: 'text', nullable: true },
|
|
61
|
+
},
|
|
94
62
|
});
|
|
95
63
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
64
|
+
// Verify both tables work independently
|
|
65
|
+
await store.insert({
|
|
66
|
+
tableName: testTableName2,
|
|
67
|
+
record: { id: 'test2', data: 'test-data-2' },
|
|
99
68
|
});
|
|
100
69
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
id: 'thread-1',
|
|
106
|
-
resourceId,
|
|
107
|
-
title: 'Thread 1',
|
|
108
|
-
createdAt: new Date(),
|
|
109
|
-
updatedAt: new Date(),
|
|
110
|
-
metadata: {},
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
id: 'thread-2',
|
|
114
|
-
resourceId,
|
|
115
|
-
title: 'Thread 2',
|
|
116
|
-
createdAt: new Date(),
|
|
117
|
-
updatedAt: new Date(),
|
|
118
|
-
metadata: {},
|
|
119
|
-
},
|
|
120
|
-
];
|
|
70
|
+
const result = await store.load({ tableName: testTableName2, keys: { id: 'test2' } });
|
|
71
|
+
expect(result).toBeTruthy();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
121
74
|
|
|
122
|
-
|
|
75
|
+
describe('Thread Operations', () => {
|
|
76
|
+
beforeEach(async () => {
|
|
77
|
+
await store.clearTable({ tableName: MastraStorage.TABLE_THREADS });
|
|
78
|
+
});
|
|
123
79
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
80
|
+
it('should create and retrieve a thread', async () => {
|
|
81
|
+
const now = new Date();
|
|
82
|
+
const thread = {
|
|
83
|
+
id: 'thread-1',
|
|
84
|
+
resourceId: 'resource-1',
|
|
85
|
+
title: 'Test Thread',
|
|
86
|
+
createdAt: now,
|
|
87
|
+
updatedAt: now,
|
|
88
|
+
metadata: { key: 'value' },
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const savedThread = await store.saveThread({ thread });
|
|
92
|
+
expect(savedThread).toEqual(thread);
|
|
93
|
+
|
|
94
|
+
const retrievedThread = await store.getThreadById({ threadId: thread.id });
|
|
95
|
+
expect(retrievedThread).toEqual({
|
|
96
|
+
...thread,
|
|
97
|
+
createdAt: new Date(now.toISOString()),
|
|
98
|
+
updatedAt: new Date(now.toISOString()),
|
|
127
99
|
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should return null for non-existent thread', async () => {
|
|
103
|
+
const result = await store.getThreadById({ threadId: 'non-existent' });
|
|
104
|
+
expect(result).toBeNull();
|
|
105
|
+
});
|
|
128
106
|
|
|
129
|
-
|
|
130
|
-
|
|
107
|
+
it('should get threads by resource ID', async () => {
|
|
108
|
+
const resourceId = 'resource-1';
|
|
109
|
+
const threads = [
|
|
110
|
+
{
|
|
131
111
|
id: 'thread-1',
|
|
132
|
-
resourceId
|
|
133
|
-
title: '
|
|
112
|
+
resourceId,
|
|
113
|
+
title: 'Thread 1',
|
|
134
114
|
createdAt: new Date(),
|
|
135
115
|
updatedAt: new Date(),
|
|
136
|
-
metadata: {
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
metadata: {
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
116
|
+
metadata: {},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: 'thread-2',
|
|
120
|
+
resourceId,
|
|
121
|
+
title: 'Thread 2',
|
|
122
|
+
createdAt: new Date(),
|
|
123
|
+
updatedAt: new Date(),
|
|
124
|
+
metadata: {},
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
await Promise.all(threads.map(thread => store.saveThread({ thread })));
|
|
129
|
+
|
|
130
|
+
const retrievedThreads = await store.getThreadsByResourceId({ resourceId });
|
|
131
|
+
expect(retrievedThreads).toHaveLength(2);
|
|
132
|
+
expect(retrievedThreads.map(t => t.id)).toEqual(expect.arrayContaining(['thread-1', 'thread-2']));
|
|
153
133
|
});
|
|
154
134
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
135
|
+
it('should update thread metadata', async () => {
|
|
136
|
+
const thread = {
|
|
137
|
+
id: 'thread-1',
|
|
138
|
+
resourceId: 'resource-1',
|
|
139
|
+
title: 'Test Thread',
|
|
140
|
+
createdAt: new Date(),
|
|
141
|
+
updatedAt: new Date(),
|
|
142
|
+
metadata: { initial: 'value' },
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
await store.saveThread({ thread });
|
|
146
|
+
|
|
147
|
+
const updatedThread = await store.updateThread({
|
|
148
|
+
id: thread.id,
|
|
149
|
+
title: 'Updated Title',
|
|
150
|
+
metadata: { updated: 'value' },
|
|
158
151
|
});
|
|
159
152
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
153
|
+
expect(updatedThread.title).toBe('Updated Title');
|
|
154
|
+
expect(updatedThread.metadata).toEqual({
|
|
155
|
+
initial: 'value',
|
|
156
|
+
updated: 'value',
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe('Date Handling', () => {
|
|
162
|
+
beforeEach(async () => {
|
|
163
|
+
await store.clearTable({ tableName: MastraStorage.TABLE_THREADS });
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should handle Date objects in thread operations', async () => {
|
|
167
|
+
const now = new Date();
|
|
168
|
+
const thread = {
|
|
169
|
+
id: 'thread-1',
|
|
170
|
+
resourceId: 'resource-1',
|
|
171
|
+
title: 'Test Thread',
|
|
172
|
+
createdAt: now,
|
|
173
|
+
updatedAt: now,
|
|
174
|
+
metadata: {},
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
await store.saveThread({ thread });
|
|
178
|
+
const retrievedThread = await store.getThreadById({ threadId: thread.id });
|
|
179
|
+
expect(retrievedThread?.createdAt).toBeInstanceOf(Date);
|
|
180
|
+
expect(retrievedThread?.updatedAt).toBeInstanceOf(Date);
|
|
181
|
+
expect(retrievedThread?.createdAt.toISOString()).toBe(now.toISOString());
|
|
182
|
+
expect(retrievedThread?.updatedAt.toISOString()).toBe(now.toISOString());
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should handle ISO string dates in thread operations', async () => {
|
|
186
|
+
const now = new Date();
|
|
187
|
+
const thread = {
|
|
188
|
+
id: 'thread-2',
|
|
189
|
+
resourceId: 'resource-1',
|
|
190
|
+
title: 'Test Thread',
|
|
191
|
+
createdAt: now.toISOString(),
|
|
192
|
+
updatedAt: now.toISOString(),
|
|
193
|
+
metadata: {},
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
await store.saveThread({ thread: thread as any });
|
|
197
|
+
const retrievedThread = await store.getThreadById({ threadId: thread.id });
|
|
198
|
+
expect(retrievedThread?.createdAt).toBeInstanceOf(Date);
|
|
199
|
+
expect(retrievedThread?.updatedAt).toBeInstanceOf(Date);
|
|
200
|
+
expect(retrievedThread?.createdAt.toISOString()).toBe(now.toISOString());
|
|
201
|
+
expect(retrievedThread?.updatedAt.toISOString()).toBe(now.toISOString());
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should handle mixed date formats in thread operations', async () => {
|
|
205
|
+
const now = new Date();
|
|
206
|
+
const thread = {
|
|
207
|
+
id: 'thread-3',
|
|
208
|
+
resourceId: 'resource-1',
|
|
209
|
+
title: 'Test Thread',
|
|
210
|
+
createdAt: now,
|
|
211
|
+
updatedAt: now.toISOString(),
|
|
212
|
+
metadata: {},
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
await store.saveThread({ thread: thread as any });
|
|
216
|
+
const retrievedThread = await store.getThreadById({ threadId: thread.id });
|
|
217
|
+
expect(retrievedThread?.createdAt).toBeInstanceOf(Date);
|
|
218
|
+
expect(retrievedThread?.updatedAt).toBeInstanceOf(Date);
|
|
219
|
+
expect(retrievedThread?.createdAt.toISOString()).toBe(now.toISOString());
|
|
220
|
+
expect(retrievedThread?.updatedAt.toISOString()).toBe(now.toISOString());
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('should handle date serialization in getThreadsByResourceId', async () => {
|
|
224
|
+
const now = new Date();
|
|
225
|
+
const threads = [
|
|
226
|
+
{
|
|
163
227
|
id: 'thread-1',
|
|
164
228
|
resourceId: 'resource-1',
|
|
165
|
-
title: '
|
|
229
|
+
title: 'Thread 1',
|
|
166
230
|
createdAt: now,
|
|
167
|
-
updatedAt: now,
|
|
231
|
+
updatedAt: now.toISOString(),
|
|
168
232
|
metadata: {},
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
await store.saveThread({ thread });
|
|
172
|
-
const retrievedThread = await store.getThreadById({ threadId: thread.id });
|
|
173
|
-
expect(retrievedThread?.createdAt).toBeInstanceOf(Date);
|
|
174
|
-
expect(retrievedThread?.updatedAt).toBeInstanceOf(Date);
|
|
175
|
-
expect(retrievedThread?.createdAt.toISOString()).toBe(now.toISOString());
|
|
176
|
-
expect(retrievedThread?.updatedAt.toISOString()).toBe(now.toISOString());
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it('should handle ISO string dates in thread operations', async () => {
|
|
180
|
-
const now = new Date();
|
|
181
|
-
const thread = {
|
|
233
|
+
},
|
|
234
|
+
{
|
|
182
235
|
id: 'thread-2',
|
|
183
236
|
resourceId: 'resource-1',
|
|
184
|
-
title: '
|
|
237
|
+
title: 'Thread 2',
|
|
185
238
|
createdAt: now.toISOString(),
|
|
186
|
-
updatedAt: now
|
|
239
|
+
updatedAt: now,
|
|
187
240
|
metadata: {},
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
241
|
+
},
|
|
242
|
+
];
|
|
243
|
+
|
|
244
|
+
await Promise.all(threads.map(thread => store.saveThread({ thread: thread as any })));
|
|
245
|
+
|
|
246
|
+
const retrievedThreads = await store.getThreadsByResourceId({ resourceId: 'resource-1' });
|
|
247
|
+
expect(retrievedThreads).toHaveLength(2);
|
|
248
|
+
retrievedThreads.forEach(thread => {
|
|
249
|
+
expect(thread.createdAt).toBeInstanceOf(Date);
|
|
250
|
+
expect(thread.updatedAt).toBeInstanceOf(Date);
|
|
251
|
+
expect(thread.createdAt.toISOString()).toBe(now.toISOString());
|
|
252
|
+
expect(thread.updatedAt.toISOString()).toBe(now.toISOString());
|
|
196
253
|
});
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe('Message Operations', () => {
|
|
258
|
+
const threadId = 'test-thread';
|
|
259
|
+
|
|
260
|
+
beforeEach(async () => {
|
|
261
|
+
await store.clearTable({ tableName: MastraStorage.TABLE_MESSAGES });
|
|
262
|
+
await store.clearTable({ tableName: MastraStorage.TABLE_THREADS });
|
|
197
263
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
id:
|
|
264
|
+
// Create a test thread
|
|
265
|
+
await store.saveThread({
|
|
266
|
+
thread: {
|
|
267
|
+
id: threadId,
|
|
202
268
|
resourceId: 'resource-1',
|
|
203
269
|
title: 'Test Thread',
|
|
204
|
-
createdAt:
|
|
205
|
-
updatedAt:
|
|
270
|
+
createdAt: new Date(),
|
|
271
|
+
updatedAt: new Date(),
|
|
206
272
|
metadata: {},
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
await store.saveThread({ thread: thread as any });
|
|
210
|
-
const retrievedThread = await store.getThreadById({ threadId: thread.id });
|
|
211
|
-
expect(retrievedThread?.createdAt).toBeInstanceOf(Date);
|
|
212
|
-
expect(retrievedThread?.updatedAt).toBeInstanceOf(Date);
|
|
213
|
-
expect(retrievedThread?.createdAt.toISOString()).toBe(now.toISOString());
|
|
214
|
-
expect(retrievedThread?.updatedAt.toISOString()).toBe(now.toISOString());
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('should handle date serialization in getThreadsByResourceId', async () => {
|
|
218
|
-
const now = new Date();
|
|
219
|
-
const threads = [
|
|
220
|
-
{
|
|
221
|
-
id: 'thread-1',
|
|
222
|
-
resourceId: 'resource-1',
|
|
223
|
-
title: 'Thread 1',
|
|
224
|
-
createdAt: now,
|
|
225
|
-
updatedAt: now.toISOString(),
|
|
226
|
-
metadata: {},
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
id: 'thread-2',
|
|
230
|
-
resourceId: 'resource-1',
|
|
231
|
-
title: 'Thread 2',
|
|
232
|
-
createdAt: now.toISOString(),
|
|
233
|
-
updatedAt: now,
|
|
234
|
-
metadata: {},
|
|
235
|
-
},
|
|
236
|
-
];
|
|
237
|
-
|
|
238
|
-
await Promise.all(threads.map(thread => store.saveThread({ thread: thread as any })));
|
|
239
|
-
|
|
240
|
-
const retrievedThreads = await store.getThreadsByResourceId({ resourceId: 'resource-1' });
|
|
241
|
-
expect(retrievedThreads).toHaveLength(2);
|
|
242
|
-
retrievedThreads.forEach(thread => {
|
|
243
|
-
expect(thread.createdAt).toBeInstanceOf(Date);
|
|
244
|
-
expect(thread.updatedAt).toBeInstanceOf(Date);
|
|
245
|
-
expect(thread.createdAt.toISOString()).toBe(now.toISOString());
|
|
246
|
-
expect(thread.updatedAt.toISOString()).toBe(now.toISOString());
|
|
247
|
-
});
|
|
273
|
+
},
|
|
248
274
|
});
|
|
249
275
|
});
|
|
250
276
|
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
|
|
277
|
+
it('should save and retrieve messages in order', async () => {
|
|
278
|
+
const messages = [
|
|
279
|
+
{
|
|
280
|
+
id: 'msg-1',
|
|
281
|
+
threadId,
|
|
282
|
+
role: 'user',
|
|
283
|
+
type: 'text',
|
|
284
|
+
content: [{ type: 'text', text: 'First' }],
|
|
285
|
+
createdAt: new Date().toISOString(),
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
id: 'msg-2',
|
|
289
|
+
threadId,
|
|
290
|
+
role: 'assistant',
|
|
291
|
+
type: 'text',
|
|
292
|
+
content: [{ type: 'text', text: 'Second' }],
|
|
293
|
+
createdAt: new Date().toISOString(),
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
id: 'msg-3',
|
|
297
|
+
threadId,
|
|
298
|
+
role: 'user',
|
|
299
|
+
type: 'text',
|
|
300
|
+
content: [{ type: 'text', text: 'Third' }],
|
|
301
|
+
createdAt: new Date().toISOString(),
|
|
302
|
+
},
|
|
303
|
+
];
|
|
304
|
+
|
|
305
|
+
await store.saveMessages({ messages });
|
|
306
|
+
|
|
307
|
+
const retrievedMessages = await store.getMessages({ threadId });
|
|
308
|
+
expect(retrievedMessages).toHaveLength(3);
|
|
309
|
+
expect(retrievedMessages.map(m => m.content[0].text)).toEqual(['First', 'Second', 'Third']);
|
|
310
|
+
});
|
|
270
311
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
threadId,
|
|
276
|
-
role: 'user',
|
|
277
|
-
type: 'text',
|
|
278
|
-
content: [{ type: 'text', text: 'First' }],
|
|
279
|
-
createdAt: new Date().toISOString(),
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
id: 'msg-2',
|
|
283
|
-
threadId,
|
|
284
|
-
role: 'assistant',
|
|
285
|
-
type: 'text',
|
|
286
|
-
content: [{ type: 'text', text: 'Second' }],
|
|
287
|
-
createdAt: new Date().toISOString(),
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
id: 'msg-3',
|
|
291
|
-
threadId,
|
|
292
|
-
role: 'user',
|
|
293
|
-
type: 'text',
|
|
294
|
-
content: [{ type: 'text', text: 'Third' }],
|
|
295
|
-
createdAt: new Date().toISOString(),
|
|
296
|
-
},
|
|
297
|
-
];
|
|
312
|
+
it('should handle empty message array', async () => {
|
|
313
|
+
const result = await store.saveMessages({ messages: [] });
|
|
314
|
+
expect(result).toEqual([]);
|
|
315
|
+
});
|
|
298
316
|
|
|
299
|
-
|
|
317
|
+
it('should handle messages with complex content', async () => {
|
|
318
|
+
const messages = [
|
|
319
|
+
{
|
|
320
|
+
id: 'msg-1',
|
|
321
|
+
threadId,
|
|
322
|
+
role: 'user',
|
|
323
|
+
type: 'text',
|
|
324
|
+
content: [
|
|
325
|
+
{ type: 'text', text: 'Message with' },
|
|
326
|
+
{ type: 'code', text: 'code block', language: 'typescript' },
|
|
327
|
+
{ type: 'text', text: 'and more text' },
|
|
328
|
+
],
|
|
329
|
+
createdAt: new Date().toISOString(),
|
|
330
|
+
},
|
|
331
|
+
];
|
|
332
|
+
|
|
333
|
+
await store.saveMessages({ messages });
|
|
334
|
+
|
|
335
|
+
const retrievedMessages = await store.getMessages({ threadId });
|
|
336
|
+
expect(retrievedMessages[0].content).toEqual(messages[0].content);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
300
339
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
340
|
+
describe('Workflow Operations', () => {
|
|
341
|
+
const testNamespace = 'test';
|
|
342
|
+
const testWorkflow = 'test-workflow';
|
|
343
|
+
const testRunId = 'test-run';
|
|
305
344
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
});
|
|
345
|
+
beforeEach(async () => {
|
|
346
|
+
await store.clearTable({ tableName: MastraStorage.TABLE_WORKFLOW_SNAPSHOT });
|
|
347
|
+
});
|
|
310
348
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
type: 'text',
|
|
318
|
-
content: [
|
|
319
|
-
{ type: 'text', text: 'Message with' },
|
|
320
|
-
{ type: 'code', text: 'code block', language: 'typescript' },
|
|
321
|
-
{ type: 'text', text: 'and more text' },
|
|
322
|
-
],
|
|
323
|
-
createdAt: new Date().toISOString(),
|
|
349
|
+
it('should persist and load workflow snapshots', async () => {
|
|
350
|
+
const mockSnapshot = {
|
|
351
|
+
value: { step1: 'completed' },
|
|
352
|
+
context: {
|
|
353
|
+
stepResults: {
|
|
354
|
+
step1: { status: 'success', payload: { result: 'done' } },
|
|
324
355
|
},
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
356
|
+
attempts: {},
|
|
357
|
+
triggerData: {},
|
|
358
|
+
},
|
|
359
|
+
runId: testRunId,
|
|
360
|
+
activePaths: [],
|
|
361
|
+
timestamp: Date.now(),
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
await store.persistWorkflowSnapshot({
|
|
365
|
+
namespace: testNamespace,
|
|
366
|
+
workflowName: testWorkflow,
|
|
367
|
+
runId: testRunId,
|
|
368
|
+
snapshot: mockSnapshot,
|
|
331
369
|
});
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
describe('Workflow Operations', () => {
|
|
335
|
-
const testNamespace = 'test';
|
|
336
|
-
const testWorkflow = 'test-workflow';
|
|
337
|
-
const testRunId = 'test-run';
|
|
338
370
|
|
|
339
|
-
|
|
340
|
-
|
|
371
|
+
const loadedSnapshot = await store.loadWorkflowSnapshot({
|
|
372
|
+
namespace: testNamespace,
|
|
373
|
+
workflowName: testWorkflow,
|
|
374
|
+
runId: testRunId,
|
|
341
375
|
});
|
|
342
376
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
value: { step1: 'completed' },
|
|
346
|
-
context: {
|
|
347
|
-
stepResults: {
|
|
348
|
-
step1: { status: 'success', payload: { result: 'done' } },
|
|
349
|
-
},
|
|
350
|
-
attempts: {},
|
|
351
|
-
triggerData: {},
|
|
352
|
-
},
|
|
353
|
-
runId: testRunId,
|
|
354
|
-
activePaths: [],
|
|
355
|
-
timestamp: Date.now(),
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
await store.persistWorkflowSnapshot({
|
|
359
|
-
namespace: testNamespace,
|
|
360
|
-
workflowName: testWorkflow,
|
|
361
|
-
runId: testRunId,
|
|
362
|
-
snapshot: mockSnapshot,
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
const loadedSnapshot = await store.loadWorkflowSnapshot({
|
|
366
|
-
namespace: testNamespace,
|
|
367
|
-
workflowName: testWorkflow,
|
|
368
|
-
runId: testRunId,
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
expect(loadedSnapshot).toEqual(mockSnapshot);
|
|
372
|
-
});
|
|
377
|
+
expect(loadedSnapshot).toEqual(mockSnapshot);
|
|
378
|
+
});
|
|
373
379
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
});
|
|
380
|
-
expect(result).toBeNull();
|
|
380
|
+
it('should return null for non-existent snapshot', async () => {
|
|
381
|
+
const result = await store.loadWorkflowSnapshot({
|
|
382
|
+
namespace: testNamespace,
|
|
383
|
+
workflowName: 'non-existent',
|
|
384
|
+
runId: 'non-existent',
|
|
381
385
|
});
|
|
386
|
+
expect(result).toBeNull();
|
|
382
387
|
});
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
timeout: 30000,
|
|
386
|
-
},
|
|
387
|
-
);
|
|
388
|
+
});
|
|
389
|
+
});
|