@tldraw/store 5.2.0-next.ee0fa4d6244f → 5.2.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/DOCS.md +790 -0
- package/README.md +9 -1
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/ImmutableMap.js +0 -13
- package/dist-cjs/lib/ImmutableMap.js.map +2 -2
- package/dist-cjs/lib/RecordType.js +1 -1
- package/dist-cjs/lib/RecordType.js.map +2 -2
- package/dist-cjs/lib/RecordsDiff.js +26 -14
- package/dist-cjs/lib/RecordsDiff.js.map +2 -2
- package/dist-cjs/lib/Store.js +6 -5
- package/dist-cjs/lib/Store.js.map +2 -2
- package/dist-cjs/lib/StoreSchema.js +1 -1
- package/dist-cjs/lib/StoreSchema.js.map +2 -2
- package/dist-cjs/lib/executeQuery.js +1 -1
- package/dist-cjs/lib/executeQuery.js.map +2 -2
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/ImmutableMap.mjs +0 -13
- package/dist-esm/lib/ImmutableMap.mjs.map +2 -2
- package/dist-esm/lib/RecordType.mjs +1 -1
- package/dist-esm/lib/RecordType.mjs.map +2 -2
- package/dist-esm/lib/RecordsDiff.mjs +26 -14
- package/dist-esm/lib/RecordsDiff.mjs.map +2 -2
- package/dist-esm/lib/Store.mjs +7 -6
- package/dist-esm/lib/Store.mjs.map +2 -2
- package/dist-esm/lib/StoreSchema.mjs +1 -1
- package/dist-esm/lib/StoreSchema.mjs.map +2 -2
- package/dist-esm/lib/executeQuery.mjs +1 -1
- package/dist-esm/lib/executeQuery.mjs.map +2 -2
- package/package.json +9 -5
- package/src/lib/{test/AtomMap.test.ts → AtomMap.test.ts} +77 -111
- package/src/lib/AtomSet.test.ts +116 -0
- package/src/lib/BaseRecord.test.ts +10 -22
- package/src/lib/ImmutableMap.test.ts +114 -71
- package/src/lib/ImmutableMap.ts +1 -0
- package/src/lib/IncrementalSetConstructor.test.ts +76 -81
- package/src/lib/RecordType.test.ts +216 -0
- package/src/lib/RecordType.ts +1 -1
- package/src/lib/RecordsDiff.test.ts +112 -106
- package/src/lib/RecordsDiff.ts +43 -18
- package/src/lib/Store.test.ts +570 -630
- package/src/lib/Store.ts +9 -10
- package/src/lib/StoreListeners.test.ts +462 -0
- package/src/lib/StoreQueries.test.ts +586 -434
- package/src/lib/StoreSchema.test.ts +1012 -174
- package/src/lib/StoreSchema.ts +1 -1
- package/src/lib/StoreSideEffects.test.ts +546 -158
- package/src/lib/devFreeze.test.ts +94 -124
- package/src/lib/executeQuery.test.ts +77 -31
- package/src/lib/executeQuery.ts +3 -1
- package/src/lib/migrate.test.ts +273 -296
- package/src/lib/setUtils.test.ts +38 -79
- package/src/lib/test/createMigrations.test.ts +0 -75
- package/src/lib/test/dependsOn.test.ts +0 -166
- package/src/lib/test/getMigrationsSince.test.ts +0 -121
- package/src/lib/test/migrate.test.ts +0 -118
- package/src/lib/test/migratePersistedRecord.test.ts +0 -265
- package/src/lib/test/migrationCaching.test.ts +0 -209
- package/src/lib/test/recordStore.test.ts +0 -1567
- package/src/lib/test/recordStoreQueries.test.ts +0 -814
- package/src/lib/test/recordType.test.ts +0 -19
- package/src/lib/test/sortMigrations.test.ts +0 -83
- package/src/lib/test/upgradeSchema.test.ts +0 -80
- package/src/lib/test/validate.test.ts +0 -178
- package/src/lib/test/validateMigrations.test.ts +0 -165
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { testSchemaV1 } from './testSchema.v1'
|
|
2
|
-
|
|
3
|
-
describe('parseId', () => {
|
|
4
|
-
it('should return the part after the colon', () => {
|
|
5
|
-
expect(testSchemaV1.types.user.parseId('user:123' as any)).toBe('123')
|
|
6
|
-
expect(testSchemaV1.types.shape.parseId('shape:xyz' as any)).toBe('xyz')
|
|
7
|
-
})
|
|
8
|
-
it('should throw if the typename does not match', () => {
|
|
9
|
-
expect(() => testSchemaV1.types.user.parseId('shape:123' as any)).toThrow()
|
|
10
|
-
expect(() => testSchemaV1.types.shape.parseId('user:xyz' as any)).toThrow()
|
|
11
|
-
})
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
describe('createId', () => {
|
|
15
|
-
it('should prepend the typename and a colon', () => {
|
|
16
|
-
expect(testSchemaV1.types.user.createId('123')).toBe('user:123')
|
|
17
|
-
expect(testSchemaV1.types.shape.createId('xyz')).toBe('shape:xyz')
|
|
18
|
-
})
|
|
19
|
-
})
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { Migration, MigrationId, sortMigrations } from '../migrate'
|
|
2
|
-
|
|
3
|
-
describe(sortMigrations, () => {
|
|
4
|
-
const m = (id: MigrationId, others?: { dependsOn?: MigrationId[] }): Migration => ({
|
|
5
|
-
...others,
|
|
6
|
-
id,
|
|
7
|
-
scope: 'record',
|
|
8
|
-
up() {
|
|
9
|
-
// noop
|
|
10
|
-
},
|
|
11
|
-
})
|
|
12
|
-
const sort = (migrations: Migration[]) => {
|
|
13
|
-
return sortMigrations(migrations).map((m) => m.id)
|
|
14
|
-
}
|
|
15
|
-
it('should sort migrations based on version number', () => {
|
|
16
|
-
expect(sort([m('foo/2'), m('foo/1')])).toEqual(['foo/1', 'foo/2'])
|
|
17
|
-
expect(sort([m('foo/1'), m('foo/2')])).toEqual(['foo/1', 'foo/2'])
|
|
18
|
-
})
|
|
19
|
-
it('should sort multiple migration sequences based on version number', () => {
|
|
20
|
-
const result = sort([m('foo/2'), m('bar/2'), m('foo/1'), m('bar/1')])
|
|
21
|
-
expect(result.filter((id) => id.startsWith('foo/'))).toEqual(['foo/1', 'foo/2'])
|
|
22
|
-
expect(result.filter((id) => id.startsWith('bar/'))).toEqual(['bar/1', 'bar/2'])
|
|
23
|
-
})
|
|
24
|
-
it('should use dependsOn to resolve inter-sequence dependencies', () => {
|
|
25
|
-
expect(
|
|
26
|
-
sort([m('foo/2'), m('bar/2'), m('foo/1'), m('bar/1', { dependsOn: ['foo/2'] })])
|
|
27
|
-
).toEqual(['foo/1', 'foo/2', 'bar/1', 'bar/2'])
|
|
28
|
-
|
|
29
|
-
expect(
|
|
30
|
-
sort([m('foo/2'), m('bar/2'), m('foo/1', { dependsOn: ['bar/2'] }), m('bar/1')])
|
|
31
|
-
).toEqual(['bar/1', 'bar/2', 'foo/1', 'foo/2'])
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('should minimize distance between dependencies and dependents', () => {
|
|
35
|
-
// bar/3 depends on foo/1 - should process bar sequence immediately after foo/1
|
|
36
|
-
expect(
|
|
37
|
-
sort([m('foo/2'), m('bar/3', { dependsOn: ['foo/1'] }), m('foo/1'), m('bar/1'), m('bar/2')])
|
|
38
|
-
).toEqual(['foo/1', 'bar/1', 'bar/2', 'bar/3', 'foo/2'])
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('should minimize total distance for multiple explicit dependencies', () => {
|
|
42
|
-
// Both bar/2 and baz/1 depend on foo/1 - minimize total distance
|
|
43
|
-
expect(
|
|
44
|
-
sort([
|
|
45
|
-
m('foo/2'),
|
|
46
|
-
m('bar/2', { dependsOn: ['foo/1'] }),
|
|
47
|
-
m('foo/1'),
|
|
48
|
-
m('bar/1'),
|
|
49
|
-
m('baz/1', { dependsOn: ['foo/1'] }),
|
|
50
|
-
])
|
|
51
|
-
).toEqual(['foo/1', 'bar/1', 'bar/2', 'baz/1', 'foo/2'])
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('should handle chain of explicit dependencies optimally', () => {
|
|
55
|
-
// foo/1 -> bar/1 -> baz/1 chain should be consecutive
|
|
56
|
-
expect(
|
|
57
|
-
sort([
|
|
58
|
-
m('foo/2'),
|
|
59
|
-
m('bar/1', { dependsOn: ['foo/1'] }),
|
|
60
|
-
m('foo/1'),
|
|
61
|
-
m('baz/1', { dependsOn: ['bar/1'] }),
|
|
62
|
-
])
|
|
63
|
-
).toEqual(['foo/1', 'bar/1', 'baz/1', 'foo/2'])
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('should fail if a cycle is created', () => {
|
|
67
|
-
expect(() => {
|
|
68
|
-
sort([m('foo/1', { dependsOn: ['foo/1'] })])
|
|
69
|
-
}).toThrowErrorMatchingInlineSnapshot(`[Error: Circular dependency in migrations: foo/1]`)
|
|
70
|
-
|
|
71
|
-
expect(() => {
|
|
72
|
-
sort([m('foo/1', { dependsOn: ['foo/2'] }), m('foo/2')])
|
|
73
|
-
}).toThrowErrorMatchingInlineSnapshot(`[Error: Circular dependency in migrations: foo/1]`)
|
|
74
|
-
|
|
75
|
-
expect(() => {
|
|
76
|
-
sort([m('foo/1', { dependsOn: ['bar/1'] }), m('bar/1', { dependsOn: ['foo/1'] })])
|
|
77
|
-
}).toThrowErrorMatchingInlineSnapshot(`[Error: Circular dependency in migrations: foo/1]`)
|
|
78
|
-
|
|
79
|
-
expect(() => {
|
|
80
|
-
sort([m('bar/1', { dependsOn: ['foo/1'] }), m('foo/1', { dependsOn: ['bar/1'] })])
|
|
81
|
-
}).toThrowErrorMatchingInlineSnapshot(`[Error: Circular dependency in migrations: bar/1]`)
|
|
82
|
-
})
|
|
83
|
-
})
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { SerializedSchemaV1, upgradeSchema } from '../StoreSchema'
|
|
2
|
-
|
|
3
|
-
describe('upgradeSchema', () => {
|
|
4
|
-
it('should upgrade a schema from v1 to v2, assuming its working with tldraw data', () => {
|
|
5
|
-
const v1: SerializedSchemaV1 = {
|
|
6
|
-
schemaVersion: 1,
|
|
7
|
-
storeVersion: 4,
|
|
8
|
-
recordVersions: {
|
|
9
|
-
asset: {
|
|
10
|
-
version: 1,
|
|
11
|
-
subTypeKey: 'type',
|
|
12
|
-
subTypeVersions: { image: 2, video: 2, bookmark: 0 },
|
|
13
|
-
},
|
|
14
|
-
camera: { version: 1 },
|
|
15
|
-
document: { version: 2 },
|
|
16
|
-
instance: { version: 22 },
|
|
17
|
-
instance_page_state: { version: 5 },
|
|
18
|
-
page: { version: 1 },
|
|
19
|
-
shape: {
|
|
20
|
-
version: 3,
|
|
21
|
-
subTypeKey: 'type',
|
|
22
|
-
subTypeVersions: {
|
|
23
|
-
group: 0,
|
|
24
|
-
text: 1,
|
|
25
|
-
bookmark: 1,
|
|
26
|
-
draw: 1,
|
|
27
|
-
geo: 7,
|
|
28
|
-
note: 4,
|
|
29
|
-
line: 1,
|
|
30
|
-
frame: 0,
|
|
31
|
-
arrow: 1,
|
|
32
|
-
highlight: 0,
|
|
33
|
-
embed: 4,
|
|
34
|
-
image: 2,
|
|
35
|
-
video: 1,
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
instance_presence: { version: 5 },
|
|
39
|
-
pointer: { version: 1 },
|
|
40
|
-
},
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
expect(upgradeSchema(v1)).toMatchInlineSnapshot(`
|
|
44
|
-
{
|
|
45
|
-
"ok": true,
|
|
46
|
-
"value": {
|
|
47
|
-
"schemaVersion": 2,
|
|
48
|
-
"sequences": {
|
|
49
|
-
"com.tldraw.asset": 1,
|
|
50
|
-
"com.tldraw.asset.bookmark": 0,
|
|
51
|
-
"com.tldraw.asset.image": 2,
|
|
52
|
-
"com.tldraw.asset.video": 2,
|
|
53
|
-
"com.tldraw.camera": 1,
|
|
54
|
-
"com.tldraw.document": 2,
|
|
55
|
-
"com.tldraw.instance": 22,
|
|
56
|
-
"com.tldraw.instance_page_state": 5,
|
|
57
|
-
"com.tldraw.instance_presence": 5,
|
|
58
|
-
"com.tldraw.page": 1,
|
|
59
|
-
"com.tldraw.pointer": 1,
|
|
60
|
-
"com.tldraw.shape": 3,
|
|
61
|
-
"com.tldraw.shape.arrow": 1,
|
|
62
|
-
"com.tldraw.shape.bookmark": 1,
|
|
63
|
-
"com.tldraw.shape.draw": 1,
|
|
64
|
-
"com.tldraw.shape.embed": 4,
|
|
65
|
-
"com.tldraw.shape.frame": 0,
|
|
66
|
-
"com.tldraw.shape.geo": 7,
|
|
67
|
-
"com.tldraw.shape.group": 0,
|
|
68
|
-
"com.tldraw.shape.highlight": 0,
|
|
69
|
-
"com.tldraw.shape.image": 2,
|
|
70
|
-
"com.tldraw.shape.line": 1,
|
|
71
|
-
"com.tldraw.shape.note": 4,
|
|
72
|
-
"com.tldraw.shape.text": 1,
|
|
73
|
-
"com.tldraw.shape.video": 1,
|
|
74
|
-
"com.tldraw.store": 4,
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
}
|
|
78
|
-
`)
|
|
79
|
-
})
|
|
80
|
-
})
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import { BaseRecord, IdOf, RecordId } from '../BaseRecord'
|
|
2
|
-
import { createRecordType } from '../RecordType'
|
|
3
|
-
import { SerializedStore, Store } from '../Store'
|
|
4
|
-
import { StoreSchema } from '../StoreSchema'
|
|
5
|
-
|
|
6
|
-
interface Book extends BaseRecord<'book', RecordId<Book>> {
|
|
7
|
-
title: string
|
|
8
|
-
author: IdOf<Author>
|
|
9
|
-
numPages: number
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const Book = createRecordType<Book>('book', {
|
|
13
|
-
validator: {
|
|
14
|
-
validate(value) {
|
|
15
|
-
const book = value as Book
|
|
16
|
-
if (!book.id.startsWith('book:')) throw Error()
|
|
17
|
-
if (book.typeName !== 'book') throw Error()
|
|
18
|
-
if (typeof book.title !== 'string') throw Error()
|
|
19
|
-
if (!Number.isFinite(book.numPages)) throw Error()
|
|
20
|
-
if (book.numPages < 0) throw Error()
|
|
21
|
-
return book
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
scope: 'document',
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
interface Author extends BaseRecord<'author', RecordId<Author>> {
|
|
28
|
-
name: string
|
|
29
|
-
isPseudonym: boolean
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const Author = createRecordType<Author>('author', {
|
|
33
|
-
validator: {
|
|
34
|
-
validate(value) {
|
|
35
|
-
const author = value as Author
|
|
36
|
-
if (author.typeName !== 'author') throw Error()
|
|
37
|
-
if (!author.id.startsWith('author:')) throw Error()
|
|
38
|
-
if (typeof author.name !== 'string') throw Error()
|
|
39
|
-
if (typeof author.isPseudonym !== 'boolean') throw Error()
|
|
40
|
-
return author
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
scope: 'document',
|
|
44
|
-
}).withDefaultProperties(() => ({
|
|
45
|
-
isPseudonym: false,
|
|
46
|
-
}))
|
|
47
|
-
|
|
48
|
-
const schema = StoreSchema.create<Book | Author>({
|
|
49
|
-
book: Book,
|
|
50
|
-
author: Author,
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
describe('Store with validation', () => {
|
|
54
|
-
let store: Store<Book | Author>
|
|
55
|
-
|
|
56
|
-
beforeEach(() => {
|
|
57
|
-
store = new Store({ schema, props: {} })
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('Accepts valid records and rejects invalid records', () => {
|
|
61
|
-
store.put([Author.create({ name: 'J.R.R Tolkein', id: Author.createId('tolkein') })])
|
|
62
|
-
|
|
63
|
-
expect(store.query.records('author').get()).toEqual([
|
|
64
|
-
{ id: 'author:tolkein', typeName: 'author', name: 'J.R.R Tolkein', isPseudonym: false },
|
|
65
|
-
])
|
|
66
|
-
|
|
67
|
-
expect(() => {
|
|
68
|
-
store.put([
|
|
69
|
-
{
|
|
70
|
-
id: Book.createId('the-hobbit'),
|
|
71
|
-
typeName: 'book',
|
|
72
|
-
title: 'The Hobbit',
|
|
73
|
-
numPages: -1, // <---- Invalid!
|
|
74
|
-
author: Author.createId('tolkein'),
|
|
75
|
-
},
|
|
76
|
-
])
|
|
77
|
-
}).toThrow()
|
|
78
|
-
|
|
79
|
-
expect(store.query.records('book').get()).toEqual([])
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
describe('Validating initial data', () => {
|
|
84
|
-
let snapshot: SerializedStore<Book | Author>
|
|
85
|
-
|
|
86
|
-
beforeEach(() => {
|
|
87
|
-
const authorId = Author.createId('tolkein')
|
|
88
|
-
const authorRecord = Author.create({ name: 'J.R.R Tolkein', id: authorId })
|
|
89
|
-
const bookId = Book.createId('the-hobbit')
|
|
90
|
-
const bookRecord = Book.create({
|
|
91
|
-
title: 'The Hobbit',
|
|
92
|
-
numPages: 300,
|
|
93
|
-
author: authorId,
|
|
94
|
-
id: bookId,
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
snapshot = {
|
|
98
|
-
[authorId]: authorRecord,
|
|
99
|
-
[bookId]: bookRecord,
|
|
100
|
-
}
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
it('Validates initial data', () => {
|
|
104
|
-
expect(() => {
|
|
105
|
-
new Store<Book | Author>({ schema, initialData: snapshot, props: {} })
|
|
106
|
-
}).not.toThrow()
|
|
107
|
-
|
|
108
|
-
expect(() => {
|
|
109
|
-
// @ts-expect-error
|
|
110
|
-
snapshot[0].name = 4
|
|
111
|
-
|
|
112
|
-
new Store<Book | Author>({ schema, initialData: snapshot, props: {} })
|
|
113
|
-
}).toThrow()
|
|
114
|
-
})
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
describe('Create & update validations', () => {
|
|
118
|
-
const authorId = Author.createId('tolkein')
|
|
119
|
-
const bookId = Book.createId('the-hobbit')
|
|
120
|
-
const initialAuthor = Author.create({ name: 'J.R.R Tolkein', id: authorId })
|
|
121
|
-
const invalidBook = Book.create({
|
|
122
|
-
// @ts-expect-error - deliberately invalid data
|
|
123
|
-
title: 4,
|
|
124
|
-
numPages: 300,
|
|
125
|
-
author: authorId,
|
|
126
|
-
id: bookId,
|
|
127
|
-
})
|
|
128
|
-
const validBook = Book.create({
|
|
129
|
-
title: 'The Hobbit',
|
|
130
|
-
numPages: 300,
|
|
131
|
-
author: authorId,
|
|
132
|
-
id: bookId,
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it('Prevents creating a store with invalid records', () => {
|
|
136
|
-
expect(
|
|
137
|
-
() =>
|
|
138
|
-
new Store<Book | Author>({
|
|
139
|
-
schema,
|
|
140
|
-
initialData: { [bookId]: invalidBook, [authorId]: initialAuthor },
|
|
141
|
-
props: {},
|
|
142
|
-
})
|
|
143
|
-
).toThrow()
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it('Prevents updating invalid records to a store', () => {
|
|
147
|
-
const store = new Store<Book | Author>({
|
|
148
|
-
schema,
|
|
149
|
-
initialData: { [bookId]: validBook, [authorId]: initialAuthor },
|
|
150
|
-
props: {},
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
expect(() => {
|
|
154
|
-
store.put([invalidBook])
|
|
155
|
-
}).toThrow()
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
it('Prevents adding invalid records to a store', () => {
|
|
159
|
-
const newAuthorId = Author.createId('shearing')
|
|
160
|
-
const store = new Store<Book | Author>({
|
|
161
|
-
schema,
|
|
162
|
-
initialData: { [bookId]: validBook, [authorId]: initialAuthor },
|
|
163
|
-
props: {},
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
expect(() => {
|
|
167
|
-
store.put([
|
|
168
|
-
Author.create({
|
|
169
|
-
// @ts-expect-error - deliberately invalid data
|
|
170
|
-
name: 5,
|
|
171
|
-
id: newAuthorId,
|
|
172
|
-
}),
|
|
173
|
-
])
|
|
174
|
-
}).toThrow()
|
|
175
|
-
|
|
176
|
-
expect(store.get(newAuthorId)).toBeUndefined()
|
|
177
|
-
})
|
|
178
|
-
})
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { validateMigrations } from '../migrate'
|
|
2
|
-
|
|
3
|
-
describe(validateMigrations, () => {
|
|
4
|
-
it('should throw if a migration id is invalid', () => {
|
|
5
|
-
expect(() =>
|
|
6
|
-
validateMigrations({
|
|
7
|
-
retroactive: false,
|
|
8
|
-
sequence: [
|
|
9
|
-
{
|
|
10
|
-
id: 'foo.1' as any,
|
|
11
|
-
scope: 'record',
|
|
12
|
-
up() {
|
|
13
|
-
// noop
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
],
|
|
17
|
-
|
|
18
|
-
sequenceId: 'foo',
|
|
19
|
-
})
|
|
20
|
-
).toThrowErrorMatchingInlineSnapshot(
|
|
21
|
-
`[Error: Every migration in sequence 'foo' must have an id starting with 'foo/'. Got invalid id: 'foo.1']`
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
expect(() =>
|
|
25
|
-
validateMigrations({
|
|
26
|
-
retroactive: false,
|
|
27
|
-
sequence: [
|
|
28
|
-
{
|
|
29
|
-
id: 'foo/one' as any,
|
|
30
|
-
scope: 'record',
|
|
31
|
-
up() {
|
|
32
|
-
// noop
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
|
|
37
|
-
sequenceId: 'foo',
|
|
38
|
-
})
|
|
39
|
-
).toThrowErrorMatchingInlineSnapshot(`[Error: Invalid migration id: 'foo/one']`)
|
|
40
|
-
|
|
41
|
-
expect(() =>
|
|
42
|
-
validateMigrations({
|
|
43
|
-
retroactive: false,
|
|
44
|
-
sequence: [
|
|
45
|
-
{
|
|
46
|
-
id: 'foo/1' as any,
|
|
47
|
-
scope: 'record',
|
|
48
|
-
up() {
|
|
49
|
-
// noop
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
id: 'foo.2' as any,
|
|
54
|
-
scope: 'record',
|
|
55
|
-
up() {
|
|
56
|
-
// noop
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
|
|
61
|
-
sequenceId: 'foo',
|
|
62
|
-
})
|
|
63
|
-
).toThrowErrorMatchingInlineSnapshot(
|
|
64
|
-
`[Error: Every migration in sequence 'foo' must have an id starting with 'foo/'. Got invalid id: 'foo.2']`
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
expect(() =>
|
|
68
|
-
validateMigrations({
|
|
69
|
-
retroactive: false,
|
|
70
|
-
sequence: [
|
|
71
|
-
{
|
|
72
|
-
id: 'foo/1' as any,
|
|
73
|
-
scope: 'record',
|
|
74
|
-
up() {
|
|
75
|
-
// noop
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
id: 'foo/two' as any,
|
|
80
|
-
scope: 'record',
|
|
81
|
-
up() {
|
|
82
|
-
// noop
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
|
|
87
|
-
sequenceId: 'foo',
|
|
88
|
-
})
|
|
89
|
-
).toThrowErrorMatchingInlineSnapshot(`[Error: Invalid migration id: 'foo/two']`)
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('should throw if the sequenceId is invalid', () => {
|
|
93
|
-
expect(() =>
|
|
94
|
-
validateMigrations({
|
|
95
|
-
retroactive: false,
|
|
96
|
-
sequence: [],
|
|
97
|
-
sequenceId: 'foo/bar',
|
|
98
|
-
})
|
|
99
|
-
).toThrowErrorMatchingInlineSnapshot(`[Error: sequenceId cannot contain a '/', got foo/bar]`)
|
|
100
|
-
|
|
101
|
-
expect(() =>
|
|
102
|
-
validateMigrations({
|
|
103
|
-
retroactive: false,
|
|
104
|
-
sequence: [],
|
|
105
|
-
sequenceId: '',
|
|
106
|
-
})
|
|
107
|
-
).toThrowErrorMatchingInlineSnapshot(`[Error: sequenceId must be a non-empty string]`)
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('should throw if the version numbers do not start at 1', () => {
|
|
111
|
-
expect(() =>
|
|
112
|
-
validateMigrations({
|
|
113
|
-
retroactive: false,
|
|
114
|
-
sequence: [
|
|
115
|
-
{
|
|
116
|
-
id: 'foo/2',
|
|
117
|
-
scope: 'record',
|
|
118
|
-
up() {
|
|
119
|
-
// noop
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
],
|
|
123
|
-
|
|
124
|
-
sequenceId: 'foo',
|
|
125
|
-
})
|
|
126
|
-
).toThrowErrorMatchingInlineSnapshot(
|
|
127
|
-
`[Error: Expected the first migrationId to be 'foo/1' but got 'foo/2']`
|
|
128
|
-
)
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it('should throw if the version numbers do not increase monotonically', () => {
|
|
132
|
-
expect(() =>
|
|
133
|
-
validateMigrations({
|
|
134
|
-
retroactive: false,
|
|
135
|
-
sequence: [
|
|
136
|
-
{
|
|
137
|
-
id: 'foo/1',
|
|
138
|
-
scope: 'record',
|
|
139
|
-
up() {
|
|
140
|
-
// noop
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
id: 'foo/2',
|
|
145
|
-
scope: 'record',
|
|
146
|
-
up() {
|
|
147
|
-
// noop
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
id: 'foo/4',
|
|
152
|
-
scope: 'record',
|
|
153
|
-
up() {
|
|
154
|
-
// noop
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
],
|
|
158
|
-
|
|
159
|
-
sequenceId: 'foo',
|
|
160
|
-
})
|
|
161
|
-
).toThrowErrorMatchingInlineSnapshot(
|
|
162
|
-
`[Error: Migration id numbers must increase in increments of 1, expected foo/3 but got 'foo/4']`
|
|
163
|
-
)
|
|
164
|
-
})
|
|
165
|
-
})
|