@@ -142,5 +142,201 @@ void main() {
142
142
143
143
expect (results2[0 ]['detail' ], contains ('SCAN' ));
144
144
});
145
+
146
+ test ('Validation runs on setup' , () async {
147
+ final schema = Schema ([
148
+ Table ('#assets' , [
149
+ Column .text ('name' ),
150
+ ]),
151
+ ]);
152
+
153
+ try {
154
+ await testUtils.setupPowerSync (path: path, schema: schema);
155
+ } catch (e) {
156
+ expect (
157
+ e,
158
+ isA <AssertionError >().having ((e) => e.message, 'message' ,
159
+ 'Invalid characters in table name: #assets' ));
160
+ }
161
+ });
162
+
163
+ test ('Validation runs on update' , () async {
164
+ final schema = Schema ([
165
+ Table ('works' , [
166
+ Column .text ('name' ),
167
+ ]),
168
+ ]);
169
+
170
+ final powersync =
171
+ await testUtils.setupPowerSync (path: path, schema: schema);
172
+
173
+ final schema2 = Schema ([
174
+ Table ('#notworking' , [
175
+ Column .text ('created_at' ),
176
+ ]),
177
+ ]);
178
+
179
+ try {
180
+ powersync.updateSchema (schema2);
181
+ } catch (e) {
182
+ expect (
183
+ e,
184
+ isA <AssertionError >().having ((e) => e.message, 'message' ,
185
+ 'Invalid characters in table name: #notworking' ));
186
+ }
187
+ });
188
+ });
189
+
190
+ group ('Table' , () {
191
+ test ('Create a synced table' , () {
192
+ final table = Table ('users' , [
193
+ Column ('name' , ColumnType .text),
194
+ Column ('age' , ColumnType .integer),
195
+ ]);
196
+
197
+ expect (table.name, equals ('users' ));
198
+ expect (table.columns.length, equals (2 ));
199
+ expect (table.localOnly, isFalse);
200
+ expect (table.insertOnly, isFalse);
201
+ expect (table.internalName, equals ('ps_data__users' ));
202
+ expect (table.viewName, equals ('users' ));
203
+ });
204
+
205
+ test ('Create a local-only table' , () {
206
+ final table = Table .localOnly (
207
+ 'local_users' ,
208
+ [
209
+ Column ('name' , ColumnType .text),
210
+ ],
211
+ viewName: 'local_user_view' );
212
+
213
+ expect (table.name, equals ('local_users' ));
214
+ expect (table.localOnly, isTrue);
215
+ expect (table.insertOnly, isFalse);
216
+ expect (table.internalName, equals ('ps_data_local__local_users' ));
217
+ expect (table.viewName, equals ('local_user_view' ));
218
+ });
219
+
220
+ test ('Create an insert-only table' , () {
221
+ final table = Table .insertOnly ('logs' , [
222
+ Column ('message' , ColumnType .text),
223
+ Column ('timestamp' , ColumnType .integer),
224
+ ]);
225
+
226
+ expect (table.name, equals ('logs' ));
227
+ expect (table.localOnly, isFalse);
228
+ expect (table.insertOnly, isTrue);
229
+ expect (table.internalName, equals ('ps_data__logs' ));
230
+ expect (table.indexes, isEmpty);
231
+ });
232
+
233
+ test ('Access column by name' , () {
234
+ final table = Table ('products' , [
235
+ Column ('name' , ColumnType .text),
236
+ Column ('price' , ColumnType .real),
237
+ ]);
238
+
239
+ expect (table['name' ].type, equals (ColumnType .text));
240
+ expect (table['price' ].type, equals (ColumnType .real));
241
+ expect (() => table['nonexistent' ], throwsStateError);
242
+ });
243
+
244
+ test ('Validate table name' , () {
245
+ final invalidTableName =
246
+ Table ('#invalid_table_name' , [Column ('name' , ColumnType .text)]);
247
+
248
+ expect (
249
+ () => invalidTableName.validate (),
250
+ throwsA (
251
+ isA <AssertionError >().having (
252
+ (e) => e.message,
253
+ 'message' ,
254
+ 'Invalid characters in table name: #invalid_table_name' ,
255
+ ),
256
+ ),
257
+ );
258
+ });
259
+
260
+ test ('Validate view name' , () {
261
+ final invalidTableName = Table (
262
+ 'valid_table_name' , [Column ('name' , ColumnType .text)],
263
+ viewName: '#invalid_view_name' );
264
+
265
+ expect (
266
+ () => invalidTableName.validate (),
267
+ throwsA (
268
+ isA <AssertionError >().having (
269
+ (e) => e.message,
270
+ 'message' ,
271
+ 'Invalid characters in view name: #invalid_view_name' ,
272
+ ),
273
+ ),
274
+ );
275
+ });
276
+
277
+ test ('Validate table definition' , () {
278
+ final validTable = Table ('valid_table' , [
279
+ Column ('name' , ColumnType .text),
280
+ Column ('age' , ColumnType .integer),
281
+ ]);
282
+
283
+ expect (() => validTable.validate (), returnsNormally);
284
+ });
285
+
286
+ test ('Table with id column' , () {
287
+ final invalidTable = Table ('invalid_table' , [
288
+ Column ('id' , ColumnType .integer), // Duplicate 'id' column
289
+ Column ('name' , ColumnType .text),
290
+ ]);
291
+
292
+ expect (
293
+ () => invalidTable.validate (),
294
+ throwsA (
295
+ isA <AssertionError >().having (
296
+ (e) => e.message,
297
+ 'message' ,
298
+ 'invalid_table: id column is automatically added, custom id columns are not supported' ,
299
+ ),
300
+ ),
301
+ );
302
+ });
303
+
304
+ test ('Table with too many columns' , () {
305
+ final List <Column > manyColumns = List .generate (
306
+ 64 , // Exceeds MAX_NUMBER_OF_COLUMNS
307
+ (index) => Column ('col$index ' , ColumnType .text),
308
+ );
309
+
310
+ final tableTooManyColumns = Table ('too_many_columns' , manyColumns);
311
+
312
+ expect (
313
+ () => tableTooManyColumns.validate (),
314
+ throwsA (
315
+ isA <AssertionError >().having (
316
+ (e) => e.message,
317
+ 'message' ,
318
+ 'Table too_many_columns has more than 63 columns, which is not supported' ,
319
+ ),
320
+ ),
321
+ );
322
+ });
323
+
324
+ test ('toJson method' , () {
325
+ final table = Table ('users' , [
326
+ Column ('name' , ColumnType .text),
327
+ Column ('age' , ColumnType .integer),
328
+ ], indexes: [
329
+ Index ('name_index' , [IndexedColumn ('name' )])
330
+ ]);
331
+
332
+ final json = table.toJson ();
333
+
334
+ expect (json['name' ], equals ('users' ));
335
+ expect (json['view_name' ], isNull);
336
+ expect (json['local_only' ], isFalse);
337
+ expect (json['insert_only' ], isFalse);
338
+ expect (json['columns' ].length, equals (2 ));
339
+ expect (json['indexes' ].length, equals (1 ));
340
+ });
145
341
});
146
342
}
0 commit comments