@opensaas/stack-cli 0.5.0 → 0.6.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.
- package/README.md +76 -0
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +94 -268
- package/dist/commands/migrate.js.map +1 -1
- package/package.json +7 -2
- package/.turbo/turbo-build.log +0 -4
- package/CHANGELOG.md +0 -462
- package/CLAUDE.md +0 -298
- package/src/commands/__snapshots__/generate.test.ts.snap +0 -413
- package/src/commands/dev.test.ts +0 -215
- package/src/commands/dev.ts +0 -48
- package/src/commands/generate.test.ts +0 -282
- package/src/commands/generate.ts +0 -182
- package/src/commands/init.ts +0 -34
- package/src/commands/mcp.ts +0 -135
- package/src/commands/migrate.ts +0 -534
- package/src/generator/__snapshots__/context.test.ts.snap +0 -361
- package/src/generator/__snapshots__/prisma.test.ts.snap +0 -174
- package/src/generator/__snapshots__/types.test.ts.snap +0 -1702
- package/src/generator/context.test.ts +0 -139
- package/src/generator/context.ts +0 -227
- package/src/generator/index.ts +0 -7
- package/src/generator/lists.test.ts +0 -335
- package/src/generator/lists.ts +0 -140
- package/src/generator/plugin-types.ts +0 -147
- package/src/generator/prisma-config.ts +0 -46
- package/src/generator/prisma-extensions.ts +0 -159
- package/src/generator/prisma.test.ts +0 -211
- package/src/generator/prisma.ts +0 -161
- package/src/generator/types.test.ts +0 -268
- package/src/generator/types.ts +0 -537
- package/src/index.ts +0 -46
- package/src/mcp/lib/documentation-provider.ts +0 -710
- package/src/mcp/lib/features/catalog.ts +0 -301
- package/src/mcp/lib/generators/feature-generator.ts +0 -598
- package/src/mcp/lib/types.ts +0 -89
- package/src/mcp/lib/wizards/migration-wizard.ts +0 -584
- package/src/mcp/lib/wizards/wizard-engine.ts +0 -427
- package/src/mcp/server/index.ts +0 -361
- package/src/mcp/server/stack-mcp-server.ts +0 -544
- package/src/migration/generators/migration-generator.ts +0 -675
- package/src/migration/introspectors/index.ts +0 -12
- package/src/migration/introspectors/keystone-introspector.ts +0 -296
- package/src/migration/introspectors/nextjs-introspector.ts +0 -209
- package/src/migration/introspectors/prisma-introspector.ts +0 -233
- package/src/migration/types.ts +0 -92
- package/tests/introspectors/keystone-introspector.test.ts +0 -255
- package/tests/introspectors/nextjs-introspector.test.ts +0 -302
- package/tests/introspectors/prisma-introspector.test.ts +0 -268
- package/tests/migration-generator.test.ts +0 -592
- package/tests/migration-wizard.test.ts +0 -442
- package/tsconfig.json +0 -13
- package/tsconfig.tsbuildinfo +0 -1
- package/vitest.config.ts +0 -26
|
@@ -1,442 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for MigrationWizard
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, beforeEach } from 'vitest'
|
|
6
|
-
import { MigrationWizard } from '../src/mcp/lib/wizards/migration-wizard.js'
|
|
7
|
-
import type { IntrospectedSchema } from '../src/migration/types.js'
|
|
8
|
-
|
|
9
|
-
describe('MigrationWizard', () => {
|
|
10
|
-
let wizard: MigrationWizard
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
wizard = new MigrationWizard()
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
describe('startMigration', () => {
|
|
17
|
-
it('should start a migration session without analysis', async () => {
|
|
18
|
-
const result = await wizard.startMigration('prisma')
|
|
19
|
-
|
|
20
|
-
expect(result.content).toHaveLength(1)
|
|
21
|
-
expect(result.content[0].text).toContain('Migration Wizard')
|
|
22
|
-
expect(result.content[0].text).toContain('**Project Type:** prisma')
|
|
23
|
-
expect(result.content[0].text).toContain('sessionId')
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('should start a migration session with analysis', async () => {
|
|
27
|
-
const analysis: IntrospectedSchema = {
|
|
28
|
-
provider: 'postgresql',
|
|
29
|
-
models: [
|
|
30
|
-
{
|
|
31
|
-
name: 'Post',
|
|
32
|
-
fields: [
|
|
33
|
-
{
|
|
34
|
-
name: 'id',
|
|
35
|
-
type: 'String',
|
|
36
|
-
isRequired: true,
|
|
37
|
-
isUnique: true,
|
|
38
|
-
isId: true,
|
|
39
|
-
isList: false,
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
name: 'title',
|
|
43
|
-
type: 'String',
|
|
44
|
-
isRequired: true,
|
|
45
|
-
isUnique: false,
|
|
46
|
-
isId: false,
|
|
47
|
-
isList: false,
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
hasRelations: false,
|
|
51
|
-
primaryKey: 'id',
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
enums: [],
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const result = await wizard.startMigration('prisma', analysis)
|
|
58
|
-
|
|
59
|
-
expect(result.content[0].text).toContain('**Models:** 1')
|
|
60
|
-
expect(result.content[0].text).toContain('**Database:** postgresql')
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it('should extract session ID from response', async () => {
|
|
64
|
-
const result = await wizard.startMigration('prisma')
|
|
65
|
-
const sessionIdMatch = result.content[0].text.match(/sessionId.*?:\s*"([^"]+)"/)
|
|
66
|
-
|
|
67
|
-
expect(sessionIdMatch).toBeDefined()
|
|
68
|
-
expect(sessionIdMatch![1]).toMatch(/^migration_\d+_[a-z0-9]+$/)
|
|
69
|
-
})
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
describe('answerQuestion', () => {
|
|
73
|
-
it('should handle invalid session ID', async () => {
|
|
74
|
-
const result = await wizard.answerQuestion('invalid-session-id', true)
|
|
75
|
-
|
|
76
|
-
expect(result.content[0].text).toContain('Session not found')
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it('should accept valid boolean answer and progress to next question', async () => {
|
|
80
|
-
const startResult = await wizard.startMigration('prisma')
|
|
81
|
-
const sessionIdMatch = startResult.content[0].text.match(/sessionId.*?:\s*"([^"]+)"/)
|
|
82
|
-
const sessionId = sessionIdMatch![1]
|
|
83
|
-
|
|
84
|
-
// Answer first question (boolean: preserve_database)
|
|
85
|
-
const answerResult = await wizard.answerQuestion(sessionId, true)
|
|
86
|
-
|
|
87
|
-
expect(answerResult.content[0].text).toContain('Recorded')
|
|
88
|
-
expect(answerResult.content[0].text).toContain('Question 2')
|
|
89
|
-
expect(answerResult.content[0].text).toContain('Progress')
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('should validate boolean answers', async () => {
|
|
93
|
-
const startResult = await wizard.startMigration('prisma')
|
|
94
|
-
const sessionIdMatch = startResult.content[0].text.match(/sessionId.*?:\s*"([^"]+)"/)
|
|
95
|
-
const sessionId = sessionIdMatch![1]
|
|
96
|
-
|
|
97
|
-
// Try invalid boolean answer
|
|
98
|
-
const invalidResult = await wizard.answerQuestion(sessionId, 'maybe')
|
|
99
|
-
|
|
100
|
-
expect(invalidResult.content[0].text).toContain('Invalid answer')
|
|
101
|
-
expect(invalidResult.content[0].text).toContain('yes/no')
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('should accept yes/no as boolean values', async () => {
|
|
105
|
-
const startResult = await wizard.startMigration('prisma')
|
|
106
|
-
const sessionIdMatch = startResult.content[0].text.match(/sessionId.*?:\s*"([^"]+)"/)
|
|
107
|
-
const sessionId = sessionIdMatch![1]
|
|
108
|
-
|
|
109
|
-
// Answer with "yes"
|
|
110
|
-
const answerResult = await wizard.answerQuestion(sessionId, 'yes')
|
|
111
|
-
|
|
112
|
-
expect(answerResult.content[0].text).toContain('Recorded')
|
|
113
|
-
expect(answerResult.content[0].text).toContain('Question 2')
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('should validate select answers', async () => {
|
|
117
|
-
const startResult = await wizard.startMigration('prisma')
|
|
118
|
-
const sessionIdMatch = startResult.content[0].text.match(/sessionId.*?:\s*"([^"]+)"/)
|
|
119
|
-
const sessionId = sessionIdMatch![1]
|
|
120
|
-
|
|
121
|
-
// Answer first question (boolean)
|
|
122
|
-
await wizard.answerQuestion(sessionId, true)
|
|
123
|
-
|
|
124
|
-
// Try invalid select answer for db_provider
|
|
125
|
-
const invalidResult = await wizard.answerQuestion(sessionId, 'mongodb')
|
|
126
|
-
|
|
127
|
-
expect(invalidResult.content[0].text).toContain('Invalid')
|
|
128
|
-
expect(invalidResult.content[0].text).toContain('sqlite, postgresql, mysql')
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it('should accept valid select answer', async () => {
|
|
132
|
-
const startResult = await wizard.startMigration('prisma')
|
|
133
|
-
const sessionIdMatch = startResult.content[0].text.match(/sessionId.*?:\s*"([^"]+)"/)
|
|
134
|
-
const sessionId = sessionIdMatch![1]
|
|
135
|
-
|
|
136
|
-
// Answer first question
|
|
137
|
-
await wizard.answerQuestion(sessionId, true)
|
|
138
|
-
|
|
139
|
-
// Answer second question with valid select
|
|
140
|
-
const answerResult = await wizard.answerQuestion(sessionId, 'postgresql')
|
|
141
|
-
|
|
142
|
-
expect(answerResult.content[0].text).toContain('Recorded')
|
|
143
|
-
expect(answerResult.content[0].text).toContain('Question 3')
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it('should validate multiselect answers', async () => {
|
|
147
|
-
const startResult = await wizard.startMigration('prisma')
|
|
148
|
-
const sessionIdMatch = startResult.content[0].text.match(/sessionId.*?:\s*"([^"]+)"/)
|
|
149
|
-
const sessionId = sessionIdMatch![1]
|
|
150
|
-
|
|
151
|
-
// Progress to auth_methods question (multiselect)
|
|
152
|
-
await wizard.answerQuestion(sessionId, true) // preserve_database
|
|
153
|
-
await wizard.answerQuestion(sessionId, 'sqlite') // db_provider
|
|
154
|
-
await wizard.answerQuestion(sessionId, true) // enable_auth
|
|
155
|
-
|
|
156
|
-
// Try invalid multiselect
|
|
157
|
-
const invalidResult = await wizard.answerQuestion(sessionId, 'invalid-method')
|
|
158
|
-
|
|
159
|
-
expect(invalidResult.content[0].text).toContain('Invalid')
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
it('should accept valid multiselect answers as array', async () => {
|
|
163
|
-
const startResult = await wizard.startMigration('prisma')
|
|
164
|
-
const sessionIdMatch = startResult.content[0].text.match(/sessionId.*?:\s*"([^"]+)"/)
|
|
165
|
-
const sessionId = sessionIdMatch![1]
|
|
166
|
-
|
|
167
|
-
// Progress to auth_methods question
|
|
168
|
-
await wizard.answerQuestion(sessionId, true)
|
|
169
|
-
await wizard.answerQuestion(sessionId, 'sqlite')
|
|
170
|
-
await wizard.answerQuestion(sessionId, true)
|
|
171
|
-
|
|
172
|
-
// Answer with array
|
|
173
|
-
const answerResult = await wizard.answerQuestion(sessionId, ['email-password', 'google'])
|
|
174
|
-
|
|
175
|
-
expect(answerResult.content[0].text).toContain('Recorded')
|
|
176
|
-
expect(answerResult.content[0].text).toContain('email-password, google')
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
it('should accept valid multiselect answers as comma-separated string', async () => {
|
|
180
|
-
const startResult = await wizard.startMigration('prisma')
|
|
181
|
-
const sessionIdMatch = startResult.content[0].text.match(/sessionId.*?:\s*"([^"]+)"/)
|
|
182
|
-
const sessionId = sessionIdMatch![1]
|
|
183
|
-
|
|
184
|
-
// Progress to auth_methods question
|
|
185
|
-
await wizard.answerQuestion(sessionId, true)
|
|
186
|
-
await wizard.answerQuestion(sessionId, 'sqlite')
|
|
187
|
-
await wizard.answerQuestion(sessionId, true)
|
|
188
|
-
|
|
189
|
-
// Answer with comma-separated string
|
|
190
|
-
const answerResult = await wizard.answerQuestion(sessionId, 'email-password, github')
|
|
191
|
-
|
|
192
|
-
expect(answerResult.content[0].text).toContain('Recorded')
|
|
193
|
-
})
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
describe('generateMigrationConfig', () => {
|
|
197
|
-
it('should complete wizard and generate config', async () => {
|
|
198
|
-
const startResult = await wizard.startMigration('prisma')
|
|
199
|
-
const sessionIdMatch = startResult.content[0].text.match(/sessionId.*?:\s*"([^"]+)"/)
|
|
200
|
-
const sessionId = sessionIdMatch![1]
|
|
201
|
-
|
|
202
|
-
// Answer all questions
|
|
203
|
-
await wizard.answerQuestion(sessionId, true) // preserve_database
|
|
204
|
-
await wizard.answerQuestion(sessionId, 'sqlite') // db_provider
|
|
205
|
-
await wizard.answerQuestion(sessionId, 'no') // enable_auth (use string instead of boolean)
|
|
206
|
-
await wizard.answerQuestion(sessionId, 'public-read-auth-write') // default_access
|
|
207
|
-
await wizard.answerQuestion(sessionId, '/admin') // admin_base_path
|
|
208
|
-
await wizard.answerQuestion(sessionId, []) // additional_features
|
|
209
|
-
const finalResult = await wizard.answerQuestion(sessionId, 'yes') // confirm (use string instead of boolean)
|
|
210
|
-
|
|
211
|
-
expect(finalResult.content[0].text).toContain('Migration Complete')
|
|
212
|
-
expect(finalResult.content[0].text).toContain('opensaas.config.ts')
|
|
213
|
-
expect(finalResult.content[0].text).toContain('Install Dependencies')
|
|
214
|
-
expect(finalResult.content[0].text).toContain('Next Steps')
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
it('should include auth dependencies when auth is enabled', async () => {
|
|
218
|
-
const startResult = await wizard.startMigration('prisma')
|
|
219
|
-
const sessionIdMatch = startResult.content[0].text.match(/sessionId.*?:\s*"([^"]+)"/)
|
|
220
|
-
const sessionId = sessionIdMatch![1]
|
|
221
|
-
|
|
222
|
-
// Answer all questions with auth enabled
|
|
223
|
-
await wizard.answerQuestion(sessionId, true)
|
|
224
|
-
await wizard.answerQuestion(sessionId, 'postgresql')
|
|
225
|
-
await wizard.answerQuestion(sessionId, true) // enable_auth = true
|
|
226
|
-
await wizard.answerQuestion(sessionId, ['email-password'])
|
|
227
|
-
await wizard.answerQuestion(sessionId, 'authenticated-only')
|
|
228
|
-
await wizard.answerQuestion(sessionId, '/admin')
|
|
229
|
-
await wizard.answerQuestion(sessionId, [])
|
|
230
|
-
const finalResult = await wizard.answerQuestion(sessionId, true)
|
|
231
|
-
|
|
232
|
-
expect(finalResult.content[0].text).toContain('@opensaas/stack-auth')
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
it('should clean up session after completion', async () => {
|
|
236
|
-
const startResult = await wizard.startMigration('prisma')
|
|
237
|
-
const sessionIdMatch = startResult.content[0].text.match(/sessionId.*?:\s*"([^"]+)"/)
|
|
238
|
-
const sessionId = sessionIdMatch![1]
|
|
239
|
-
|
|
240
|
-
// Verify session exists
|
|
241
|
-
expect(wizard.getSession(sessionId)).toBeDefined()
|
|
242
|
-
|
|
243
|
-
// Complete wizard
|
|
244
|
-
await wizard.answerQuestion(sessionId, 'yes')
|
|
245
|
-
await wizard.answerQuestion(sessionId, 'sqlite')
|
|
246
|
-
await wizard.answerQuestion(sessionId, 'no')
|
|
247
|
-
await wizard.answerQuestion(sessionId, 'public-read-auth-write')
|
|
248
|
-
await wizard.answerQuestion(sessionId, '/admin')
|
|
249
|
-
await wizard.answerQuestion(sessionId, [])
|
|
250
|
-
await wizard.answerQuestion(sessionId, 'yes')
|
|
251
|
-
|
|
252
|
-
// Verify session is cleaned up
|
|
253
|
-
expect(wizard.getSession(sessionId)).toBeUndefined()
|
|
254
|
-
})
|
|
255
|
-
})
|
|
256
|
-
|
|
257
|
-
describe('model detection', () => {
|
|
258
|
-
it('should ask about auth models when detected', async () => {
|
|
259
|
-
const analysis: IntrospectedSchema = {
|
|
260
|
-
provider: 'postgresql',
|
|
261
|
-
models: [
|
|
262
|
-
{
|
|
263
|
-
name: 'User',
|
|
264
|
-
fields: [],
|
|
265
|
-
hasRelations: false,
|
|
266
|
-
primaryKey: 'id',
|
|
267
|
-
},
|
|
268
|
-
{
|
|
269
|
-
name: 'Account',
|
|
270
|
-
fields: [],
|
|
271
|
-
hasRelations: false,
|
|
272
|
-
primaryKey: 'id',
|
|
273
|
-
},
|
|
274
|
-
{
|
|
275
|
-
name: 'Post',
|
|
276
|
-
fields: [],
|
|
277
|
-
hasRelations: false,
|
|
278
|
-
primaryKey: 'id',
|
|
279
|
-
},
|
|
280
|
-
],
|
|
281
|
-
enums: [],
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const result = await wizard.startMigration('prisma', analysis)
|
|
285
|
-
|
|
286
|
-
// The wizard should include questions about skipping auth models
|
|
287
|
-
expect(result.content[0].text).toBeDefined()
|
|
288
|
-
})
|
|
289
|
-
|
|
290
|
-
it('should ask about owner models when User relationships detected', async () => {
|
|
291
|
-
const analysis: IntrospectedSchema = {
|
|
292
|
-
provider: 'postgresql',
|
|
293
|
-
models: [
|
|
294
|
-
{
|
|
295
|
-
name: 'User',
|
|
296
|
-
fields: [],
|
|
297
|
-
hasRelations: false,
|
|
298
|
-
primaryKey: 'id',
|
|
299
|
-
},
|
|
300
|
-
{
|
|
301
|
-
name: 'Post',
|
|
302
|
-
fields: [
|
|
303
|
-
{
|
|
304
|
-
name: 'author',
|
|
305
|
-
type: 'User',
|
|
306
|
-
isRequired: true,
|
|
307
|
-
isUnique: false,
|
|
308
|
-
isId: false,
|
|
309
|
-
isList: false,
|
|
310
|
-
relation: {
|
|
311
|
-
name: 'PostToUser',
|
|
312
|
-
model: 'User',
|
|
313
|
-
fields: ['authorId'],
|
|
314
|
-
references: ['id'],
|
|
315
|
-
},
|
|
316
|
-
},
|
|
317
|
-
],
|
|
318
|
-
hasRelations: true,
|
|
319
|
-
primaryKey: 'id',
|
|
320
|
-
},
|
|
321
|
-
],
|
|
322
|
-
enums: [],
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const result = await wizard.startMigration('prisma', analysis)
|
|
326
|
-
|
|
327
|
-
expect(result.content[0].text).toBeDefined()
|
|
328
|
-
})
|
|
329
|
-
})
|
|
330
|
-
|
|
331
|
-
describe('session management', () => {
|
|
332
|
-
it('should clear completed sessions', () => {
|
|
333
|
-
const wizard = new MigrationWizard()
|
|
334
|
-
|
|
335
|
-
// Create a mock completed session
|
|
336
|
-
const session = wizard['sessions']
|
|
337
|
-
session['test-session'] = {
|
|
338
|
-
id: 'test-session',
|
|
339
|
-
projectType: 'prisma',
|
|
340
|
-
analysis: { projectTypes: ['prisma'], cwd: '.' },
|
|
341
|
-
currentQuestionIndex: 0,
|
|
342
|
-
answers: {},
|
|
343
|
-
isComplete: true,
|
|
344
|
-
createdAt: new Date(),
|
|
345
|
-
updatedAt: new Date(),
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
wizard.clearCompletedSessions()
|
|
349
|
-
|
|
350
|
-
expect(wizard.getSession('test-session')).toBeUndefined()
|
|
351
|
-
})
|
|
352
|
-
|
|
353
|
-
it('should not clear incomplete sessions', () => {
|
|
354
|
-
const wizard = new MigrationWizard()
|
|
355
|
-
|
|
356
|
-
// Create a mock incomplete session
|
|
357
|
-
const session = wizard['sessions']
|
|
358
|
-
session['test-session'] = {
|
|
359
|
-
id: 'test-session',
|
|
360
|
-
projectType: 'prisma',
|
|
361
|
-
analysis: { projectTypes: ['prisma'], cwd: '.' },
|
|
362
|
-
currentQuestionIndex: 0,
|
|
363
|
-
answers: {},
|
|
364
|
-
isComplete: false,
|
|
365
|
-
createdAt: new Date(),
|
|
366
|
-
updatedAt: new Date(),
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
wizard.clearCompletedSessions()
|
|
370
|
-
|
|
371
|
-
expect(wizard.getSession('test-session')).toBeDefined()
|
|
372
|
-
})
|
|
373
|
-
|
|
374
|
-
it('should clear old sessions', () => {
|
|
375
|
-
const wizard = new MigrationWizard()
|
|
376
|
-
|
|
377
|
-
// Create a mock old session (2 hours ago)
|
|
378
|
-
const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000)
|
|
379
|
-
const session = wizard['sessions']
|
|
380
|
-
session['old-session'] = {
|
|
381
|
-
id: 'old-session',
|
|
382
|
-
projectType: 'prisma',
|
|
383
|
-
analysis: { projectTypes: ['prisma'], cwd: '.' },
|
|
384
|
-
currentQuestionIndex: 0,
|
|
385
|
-
answers: {},
|
|
386
|
-
isComplete: false,
|
|
387
|
-
createdAt: twoHoursAgo,
|
|
388
|
-
updatedAt: twoHoursAgo,
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
wizard.clearOldSessions()
|
|
392
|
-
|
|
393
|
-
expect(wizard.getSession('old-session')).toBeUndefined()
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
it('should not clear recent sessions', () => {
|
|
397
|
-
const wizard = new MigrationWizard()
|
|
398
|
-
|
|
399
|
-
// Create a mock recent session (5 minutes ago)
|
|
400
|
-
const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000)
|
|
401
|
-
const session = wizard['sessions']
|
|
402
|
-
session['recent-session'] = {
|
|
403
|
-
id: 'recent-session',
|
|
404
|
-
projectType: 'prisma',
|
|
405
|
-
analysis: { projectTypes: ['prisma'], cwd: '.' },
|
|
406
|
-
currentQuestionIndex: 0,
|
|
407
|
-
answers: {},
|
|
408
|
-
isComplete: false,
|
|
409
|
-
createdAt: fiveMinutesAgo,
|
|
410
|
-
updatedAt: fiveMinutesAgo,
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
wizard.clearOldSessions()
|
|
414
|
-
|
|
415
|
-
expect(wizard.getSession('recent-session')).toBeDefined()
|
|
416
|
-
})
|
|
417
|
-
})
|
|
418
|
-
|
|
419
|
-
describe('answer formatting', () => {
|
|
420
|
-
it('should format boolean answers', () => {
|
|
421
|
-
expect(wizard['formatAnswer'](true)).toBe('Yes')
|
|
422
|
-
expect(wizard['formatAnswer'](false)).toBe('No')
|
|
423
|
-
})
|
|
424
|
-
|
|
425
|
-
it('should format array answers', () => {
|
|
426
|
-
expect(wizard['formatAnswer'](['a', 'b', 'c'])).toBe('a, b, c')
|
|
427
|
-
expect(wizard['formatAnswer']([])).toBe('(none)')
|
|
428
|
-
})
|
|
429
|
-
|
|
430
|
-
it('should format string answers', () => {
|
|
431
|
-
expect(wizard['formatAnswer']('test')).toBe('test')
|
|
432
|
-
})
|
|
433
|
-
})
|
|
434
|
-
|
|
435
|
-
describe('progress bar', () => {
|
|
436
|
-
it('should render progress bar correctly', () => {
|
|
437
|
-
expect(wizard['renderProgressBar'](0, 10)).toBe('░░░░░░░░░░')
|
|
438
|
-
expect(wizard['renderProgressBar'](5, 10)).toBe('▓▓▓▓▓░░░░░')
|
|
439
|
-
expect(wizard['renderProgressBar'](10, 10)).toBe('▓▓▓▓▓▓▓▓▓▓')
|
|
440
|
-
})
|
|
441
|
-
})
|
|
442
|
-
})
|
package/tsconfig.json
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "./dist",
|
|
5
|
-
"rootDir": "./src",
|
|
6
|
-
"composite": true,
|
|
7
|
-
"declaration": true,
|
|
8
|
-
"declarationMap": true,
|
|
9
|
-
"esModuleInterop": true
|
|
10
|
-
},
|
|
11
|
-
"include": ["src/**/*"],
|
|
12
|
-
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
13
|
-
}
|