schemaTests method

void schemaTests()

Implementation

void schemaTests() {
  group('Schema operations', () {
    Connection? connection;

    tearDown(() async {
      await connection?.close();
    });

    group('initialization', () {
      group('AddCollection', () {
        test('collection is available after connection', () async {
          final schema = CollectionSchema(
            tableName,
            properties: [
              PropertySchema("name", type: PropertyType.text),
              PropertySchema("age", type: PropertyType.integer),
            ],
          );
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            await initCursor.execute(AddCollection(schema));
          });

          final cursor = await connection!.cursor();
          await cursor.execute(Query(from: schema));

          expect(await cursor.fetchall(), equals([]));
        });

        test('ifNotExists does not complain if run twice', () async {
          final schema = CollectionSchema(
            tableName,
            properties: [
              PropertySchema("name", type: PropertyType.text),
              PropertySchema("age", type: PropertyType.integer),
            ],
          );
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            await initCursor.execute(AddCollection(schema));
            await initCursor.execute(AddCollection(
              schema,
              ifNotExists: true,
            ));
          });

          final cursor = await connection!.cursor();
          await cursor.execute(Query(from: schema));

          expect(await cursor.fetchall(), equals([]));
        });

        test('cannot add collections with same name', () async {
          final failing = connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            final op = AddCollection(
              CollectionSchema(
                tableName,
                properties: [
                  PropertySchema("name", type: PropertyType.text),
                  PropertySchema("age", type: PropertyType.integer),
                ],
              ),
            );
            await initCursor.execute(op);
            await initCursor.execute(op);
          });
          expectLater(failing, throwsA(isA<ProgrammingError>()));
        });
      });

      group('RemoveCollection', () {
        test('It removes the collection', () async {
          final schema = CollectionSchema(
            tableName,
            properties: [
              PropertySchema("name", type: PropertyType.text),
              PropertySchema("age", type: PropertyType.integer),
            ],
          );
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            await initCursor.execute(AddCollection(schema));
            await initCursor.execute(RemoveCollection(tableName));
          });

          final cursor = await connection!.cursor();
          final failing = cursor.execute(Query(from: schema));

          expectLater(failing, throwsA(isA<ProgrammingError>()));
        });

        test('Cannot remove a non-existant collection', () async {
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            final failing =
                initCursor.execute(RemoveCollection(" I do not exist"));
            expectLater(failing, throwsA(isA<ProgrammingError>()));
          });
        });
      }, skip: requireFeature(DatabaseFeature.removeCollection));

      group('AddProperty', () {
        test('It adds a property to the collection', () async {
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            final collectionSchema = CollectionSchema(
              tableName,
              properties: [
                PropertySchema("name", type: PropertyType.text),
                PropertySchema("age", type: PropertyType.integer),
              ],
            );
            await initCursor.execute(AddCollection(collectionSchema));
            await initCursor.execute(AddProperty(
                collection: collectionSchema,
                property: PropertySchema("email", type: PropertyType.text)));
          });

          final finalSchema = CollectionSchema(
            tableName,
            properties: [
              PropertySchema("name", type: PropertyType.text),
              PropertySchema("age", type: PropertyType.integer),
              PropertySchema("email", type: PropertyType.text),
            ],
          );

          final cursor = await connection!.cursor();
          await cursor.execute(Insert(finalSchema, [
            {
              "name": "Me",
              "age": 123,
              "email": "me@me.com",
            }
          ]));

          await cursor.execute(Query(from: finalSchema));
          final results = await cursor.fetchall();
          expect(results[0]["email"], "me@me.com");
        });

        test('Cannot add existing property', () async {
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            final collection = CollectionSchema(
              tableName,
              properties: [
                PropertySchema("name", type: PropertyType.text),
                PropertySchema("age", type: PropertyType.integer),
              ],
            );
            await initCursor.execute(AddCollection(collection));
            final failing = initCursor.execute(AddProperty(
                collection: collection,
                property: PropertySchema("name", type: PropertyType.text)));
            expectLater(failing, throwsA(isA<ProgrammingError>()));
          });
        });

        test('Cannot add property to nonexisting collection', () async {
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            final collection = CollectionSchema(
              tableName,
              properties: [
                PropertySchema("name", type: PropertyType.text),
                PropertySchema("age", type: PropertyType.integer),
              ],
            );
            final failing = initCursor.execute(AddProperty(
                collection: collection,
                property: PropertySchema("name", type: PropertyType.text)));
            expectLater(failing, throwsA(isA<ProgrammingError>()));
          });
        });
      }, skip: requireFeature(DatabaseFeature.addProperty));

      group('RemoveProperty', () {
        /// This test is very lenient - basically, removing a
        /// property should allow to later insert an object without
        /// that property (which is a very lax requirement).
        test('It removes a property from the collection', () async {
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            final collectionSchema = CollectionSchema(
              tableName,
              properties: [
                PropertySchema("name", type: PropertyType.text),
                PropertySchema("age", type: PropertyType.integer),
              ],
            );
            await initCursor.execute(AddCollection(collectionSchema));
            await initCursor.execute(RemoveProperty(
                collection: collectionSchema, propertyName: "age"));
          });

          final finalSchema = CollectionSchema(
            tableName,
            properties: [
              PropertySchema("name", type: PropertyType.text),
            ],
          );

          final cursor = await connection!.cursor();
          await cursor.execute(Insert(finalSchema, [
            {
              "name": "Me",
            }
          ]));

          await cursor.execute(Query(from: finalSchema));
          final results = await cursor.fetchall();
          expect(results[0]["name"], "Me");
        });

        test('cannot remove property from non-existant collection', () async {
          final failing = connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            final collectionSchema = CollectionSchema(
              tableName,
              properties: [
                PropertySchema("name", type: PropertyType.text),
                PropertySchema("age", type: PropertyType.integer),
              ],
            );
            // collection declared, but not used
            await initCursor.execute(RemoveProperty(
                collection: collectionSchema, propertyName: "email"));
          });
          expectLater(failing, throwsA(isA<ProgrammingError>()));
        });

        test('cannot remove nonexistant property', () async {
          final failing = connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            final collectionSchema = CollectionSchema(
              tableName,
              properties: [
                PropertySchema("name", type: PropertyType.text),
                PropertySchema("age", type: PropertyType.integer),
              ],
            );
            await initCursor.execute(AddCollection(collectionSchema));
            await initCursor.execute(RemoveProperty(
                collection: collectionSchema, propertyName: "email"));
          });
          expectLater(failing, throwsA(isA<ProgrammingError>()));
        });
      }, skip: requireFeature(DatabaseFeature.removeProperty));

      group('RenameCollection', () {
        test('the new collection can be used', () async {
          final initialSchema = CollectionSchema(
            tableName,
            properties: [
              PropertySchema("name", type: PropertyType.text),
              PropertySchema("age", type: PropertyType.integer),
            ],
          );
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            await initCursor.execute(AddCollection(initialSchema));

            await initCursor.execute(
                RenameCollection(oldName: tableName, newName: "new_users"));
          });

          final finalSchema = initialSchema.copyWith(
            name: "new_users",
          );

          final cursor = await connection!.cursor();
          await cursor.execute(Insert(finalSchema, [
            {
              "name": "Me",
              "age": 35,
            }
          ]));

          await cursor.execute(Query(from: finalSchema));
          final results = await cursor.fetchall();
          expect(results[0]["name"], "Me");
        });

        test('the old collection cannot be used', () async {
          final initialCollection = CollectionSchema(
            tableName,
            properties: [
              PropertySchema("name", type: PropertyType.text),
              PropertySchema("age", type: PropertyType.integer),
            ],
          );
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            await initCursor.execute(AddCollection(initialCollection));

            await initCursor.execute(
                RenameCollection(oldName: tableName, newName: "new_users"));
          });

          final cursor = await connection!.cursor();
          final failing = cursor.execute(Insert(initialCollection, [
            {
              "name": "Me",
              "age": 35,
            }
          ]));

          expectLater(failing, throwsA(isA<ProgrammingError>()));
        });

        test('Cannot rename a non-existant collection', () async {
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            final failing = initCursor.execute(RenameCollection(
                oldName: "not_existant", newName: "does_not_matter"));
            expectLater(failing, throwsA(isA<ProgrammingError>()));
          });
        });
      }, skip: requireFeature(DatabaseFeature.renameCollection));

      group('AlterProperty', () {
        test('non-null -> null', () async {
          final name = PropertySchema("name",
              type: PropertyType.text, nullable: false);
          final nameNew = name.copyWith(nullable: true);
          final initialSchema = CollectionSchema(
            tableName,
            properties: [
              name,
            ],
          );
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            await initCursor.execute(AddCollection(initialSchema));

            await initCursor.execute(Insert(initialSchema, [
              {
                "name": "Paul Sellers",
              }
            ]));
            await initCursor.execute(AlterProperty(
              oldCollection: initialSchema,
              property: nameNew,
            ));

            await initCursor.execute(Insert(initialSchema, [
              {
                "name": null,
              }
            ]));
          });

          final finalSchema = initialSchema.copyWith(properties: [
            nameNew,
          ]);

          final cursor = await connection!.cursor();

          await cursor.execute(Query(from: finalSchema));
          final results = await cursor.fetchall();
          expect(results, hasLength(2));
        });

        test('null -> non-null with no null values works', () async {
          final name =
              PropertySchema("name", type: PropertyType.text, nullable: true);
          final nameNew = name.copyWith(nullable: false);
          final initialSchema = CollectionSchema(
            tableName,
            properties: [
              name,
            ],
          );
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            await initCursor.execute(AddCollection(initialSchema));

            await initCursor.execute(Insert(initialSchema, [
              {
                "name": "Paul Sellers",
              }
            ]));
            await initCursor.execute(AlterProperty(
              oldCollection: initialSchema,
              property: nameNew,
            ));
          });

          final finalSchema = initialSchema.copyWith(properties: [
            nameNew,
          ]);

          final cursor = await connection!.cursor();

          await cursor.execute(Query(from: finalSchema));
          final results = await cursor.fetchall();
          expect(results, hasLength(1));

          final failing = cursor.execute(Insert(finalSchema, [
            {
              "name": null,
            }
          ]));

          expect(failing, throwsA(isA<IntegrityError>()));
        });

        test('unique -> non-unique', () async {
          final name =
              PropertySchema("name", type: PropertyType.text, unique: true);
          final nameNew = name.copyWith(unique: false);
          final initialSchema = CollectionSchema(
            tableName,
            properties: [
              name,
            ],
          );
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            await initCursor.execute(AddCollection(initialSchema));

            await initCursor.execute(Insert(initialSchema, [
              {
                "name": "Paul Sellers",
              }
            ]));
            await initCursor.execute(AlterProperty(
              oldCollection: initialSchema,
              property: nameNew,
            ));

            await initCursor.execute(Insert(initialSchema, [
              {
                "name": "Paul Sellers",
              }
            ]));
          });

          final finalSchema = initialSchema.copyWith(properties: [
            nameNew,
          ]);

          final cursor = await connection!.cursor();

          await cursor.execute(Query(from: finalSchema));
          final results = await cursor.fetchall();
          expect(results, hasLength(2));
        }, skip: requireFeature(DatabaseFeature.uniqueProperty));

        test('non-unique -> unique with no conflicts works', () async {
          final name =
              PropertySchema("name", type: PropertyType.text, unique: false);
          final nameNew = name.copyWith(unique: true);
          final initialSchema = CollectionSchema(
            tableName,
            properties: [
              name,
            ],
          );
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            await initCursor.execute(AddCollection(initialSchema));

            await initCursor.execute(Insert(initialSchema, [
              {
                "name": "Paul Sellers",
              }
            ]));
            await initCursor.execute(AlterProperty(
              oldCollection: initialSchema,
              property: nameNew,
            ));
          });

          final finalSchema = initialSchema.copyWith(properties: [
            nameNew,
          ]);

          final cursor = await connection!.cursor();

          await cursor.execute(Query(from: finalSchema));
          final results = await cursor.fetchall();
          expect(results, hasLength(1));

          final failing = cursor.execute(Insert(finalSchema, [
            {
              "name": "Paul Sellers",
            }
          ]));

          expect(failing, throwsA(isA<IntegrityError>()));
        }, skip: requireFeature(DatabaseFeature.uniqueProperty));

        test('Cannot alter property of a non-existant collection', () async {
          final p1 = PropertySchema('nopes', type: PropertyType.boolean);
          final p2 = p1.copyWith(nullable: false);

          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            final failing = initCursor.execute(AlterProperty(
              oldCollection:
                  CollectionSchema('notExistant', properties: [p1]),
              property: p2,
            ));
            expectLater(failing, throwsA(isA<ProgrammingError>()));
          });
        });

        test('Cannot alter non-existant property', () async {
          final p1 = PropertySchema('nopes', type: PropertyType.boolean);
          final collection = CollectionSchema(
            tableName,
            properties: [
              PropertySchema("name", type: PropertyType.text),
            ],
          );
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            await initCursor.execute(AddCollection(collection));
            final failing = initCursor.execute(AlterProperty(
              oldCollection: collection,
              property: p1,
            ));

            expectLater(failing, throwsA(isA<ProgrammingError>()));
          });
        });
      }, skip: requireFeature(DatabaseFeature.alterProperty));

      group('RenameProperty', () {
        test('the new property can be used', () async {
          final name = PropertySchema("name", type: PropertyType.text);
          final age = PropertySchema("age", type: PropertyType.integer);
          final initialSchema = CollectionSchema(
            tableName,
            properties: [
              name,
              age,
            ],
          );
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            await initCursor.execute(AddCollection(initialSchema));

            await initCursor.execute(Insert(initialSchema, [
              {
                "name": "Paul Sellers",
                "age": 75,
              }
            ]));
            await initCursor.execute(RenameProperty(
              collectionName: tableName,
              oldName: "name",
              newName: "full_name",
            ));
          });

          final finalSchema = initialSchema.copyWith(properties: [
            name.copyWith(name: "full_name"),
            age,
          ]);

          final cursor = await connection!.cursor();

          await cursor.execute(Query(from: finalSchema));
          final results = await cursor.fetchall();
          expect(results[0]["full_name"], "Paul Sellers");
        });

        test('Cannot rename property of a non-existant collection', () async {
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            final failing = initCursor.execute(RenameProperty(
              collectionName: "not_existant",
              oldName: "does_not_matter",
              newName: "does_not_matter",
            ));
            expectLater(failing, throwsA(isA<ProgrammingError>()));
          });
        });

        test('Cannot rename non-existant property', () async {
          connection = await connector.connect((Connection connection) async {
            final initCursor = await connection.cursor();
            await initCursor.execute(AddCollection(
              CollectionSchema(
                tableName,
                properties: [
                  PropertySchema("name", type: PropertyType.text),
                  PropertySchema("age", type: PropertyType.integer),
                ],
              ),
            ));
            final failing = initCursor.execute(RenameProperty(
              collectionName: tableName,
              oldName: "not_existant",
              newName: "does_not_matter",
            ));
            expectLater(failing, throwsA(isA<ProgrammingError>()));
          });
        });
      }, skip: requireFeature(DatabaseFeature.renameProperty));
    });
  });
}