123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811 |
- #include "catch2/catch.hpp"
- #include "util/test_file.hpp"
- #include "object_schema.hpp"
- #include "object_store.hpp"
- #include "property.hpp"
- #include "schema.hpp"
- #include <realm/group.hpp>
- #include <realm/table.hpp>
- using namespace realm;
- struct SchemaChangePrinter {
- std::ostream& out;
- template<typename Value>
- void print(Value value) const
- {
- out << value;
- }
- template<typename Value, typename... Rest>
- void print(Value value, Rest... rest) const
- {
- out << value << ", ";
- print(rest...);
- }
- #define REALM_SC_PRINT(type, ...) \
- void operator()(schema_change::type v) const \
- { \
- out << #type << "{"; \
- print(__VA_ARGS__); \
- out << "}"; \
- }
- REALM_SC_PRINT(AddIndex, v.object, v.property)
- REALM_SC_PRINT(AddProperty, v.object, v.property)
- REALM_SC_PRINT(AddTable, v.object)
- REALM_SC_PRINT(RemoveTable, v.object)
- REALM_SC_PRINT(AddInitialProperties, v.object)
- REALM_SC_PRINT(ChangePrimaryKey, v.object, v.property)
- REALM_SC_PRINT(ChangePropertyType, v.object, v.old_property, v.new_property)
- REALM_SC_PRINT(MakePropertyNullable, v.object, v.property)
- REALM_SC_PRINT(MakePropertyRequired, v.object, v.property)
- REALM_SC_PRINT(RemoveIndex, v.object, v.property)
- REALM_SC_PRINT(RemoveProperty, v.object, v.property)
- #undef REALM_SC_PRINT
- };
- namespace Catch {
- template<>
- struct StringMaker<SchemaChange> {
- static std::string convert(SchemaChange const& sc)
- {
- std::stringstream ss;
- sc.visit(SchemaChangePrinter{ss});
- return ss.str();
- }
- };
- }
- #define REQUIRE_THROWS_CONTAINING(expr, msg) \
- REQUIRE_THROWS_WITH(expr, Catch::Matchers::Contains(msg))
- TEST_CASE("ObjectSchema") {
- SECTION("Aliases are still present in schema returned from the Realm") {
- TestFile config;
- config.schema_version = 1;
- config.schema = Schema{
- {"object", {
- {"value", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "alias"}
- }},
- };
- auto realm = Realm::get_shared_realm(config);
- REQUIRE(realm->schema().find("object")->property_for_name("value")->public_name == "alias");
- }
- SECTION("looking up properties by alias matches name if alias is not set") {
- auto schema = Schema{
- {"object", {
- {"value", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "alias"},
- {"other_value", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}}
- }},
- };
- REQUIRE(schema.find("object")->property_for_public_name("value") == nullptr);
- REQUIRE(schema.find("object")->property_for_public_name("alias")->name == "value");
- REQUIRE(schema.find("object")->property_for_public_name("other_value")->name == "other_value");
- }
- SECTION("from a Group") {
- Group g;
- TableRef table = g.add_table_with_primary_key("class_table", type_Int, "pk");
- TableRef target = g.add_table("class_target");
- table->add_column(type_Int, "int");
- table->add_column(type_Bool, "bool");
- table->add_column(type_Float, "float");
- table->add_column(type_Double, "double");
- table->add_column(type_String, "string");
- table->add_column(type_Binary, "data");
- table->add_column(type_Timestamp, "date");
- table->add_column_link(type_Link, "object", *target);
- table->add_column_link(type_LinkList, "array", *target);
- table->add_column(type_Int, "int?", true);
- table->add_column(type_Bool, "bool?", true);
- table->add_column(type_Float, "float?", true);
- table->add_column(type_Double, "double?", true);
- table->add_column(type_String, "string?", true);
- table->add_column(type_Binary, "data?", true);
- table->add_column(type_Timestamp, "date?", true);
- auto add_list = [](TableRef table, DataType type, StringData name, bool nullable) {
- table->add_column_list(type, name, nullable);
- };
- add_list(table, type_Int, "int array", false);
- add_list(table, type_Bool, "bool array", false);
- add_list(table, type_Float, "float array", false);
- add_list(table, type_Double, "double array", false);
- add_list(table, type_String, "string array", false);
- add_list(table, type_Binary, "data array", false);
- add_list(table, type_Timestamp, "date array", false);
- add_list(table, type_Int, "int? array", true);
- add_list(table, type_Bool, "bool? array", true);
- add_list(table, type_Float, "float? array", true);
- add_list(table, type_Double, "double? array", true);
- add_list(table, type_String, "string? array", true);
- add_list(table, type_Binary, "data? array", true);
- add_list(table, type_Timestamp, "date? array", true);
- std::vector<ColKey> indexed_cols;
- indexed_cols.push_back(table->add_column(type_Int, "indexed int"));
- indexed_cols.push_back(table->add_column(type_Bool, "indexed bool"));
- indexed_cols.push_back(table->add_column(type_String, "indexed string"));
- indexed_cols.push_back(table->add_column(type_Timestamp, "indexed date"));
- indexed_cols.push_back(table->add_column(type_Int, "indexed int?", true));
- indexed_cols.push_back(table->add_column(type_Bool, "indexed bool?", true));
- indexed_cols.push_back(table->add_column(type_String, "indexed string?", true));
- indexed_cols.push_back(table->add_column(type_Timestamp, "indexed date?", true));
- for (auto col : indexed_cols)
- table->add_search_index(col);
- ObjectSchema os(g, "table", table->get_key());
- REQUIRE(os.table_key == table->get_key());
- #define REQUIRE_PROPERTY(name, type, ...) do { \
- Property* prop; \
- REQUIRE((prop = os.property_for_name(name))); \
- REQUIRE((*prop == Property{name, PropertyType::type, __VA_ARGS__})); \
- REQUIRE(prop->column_key == *expected_col++); \
- } while (0)
- auto all_column_keys = table->get_column_keys();
- auto expected_col = all_column_keys.begin();
- REQUIRE(os.property_for_name("nonexistent property") == nullptr);
- REQUIRE_PROPERTY("pk", Int, Property::IsPrimary{true});
- REQUIRE_PROPERTY("int", Int);
- REQUIRE_PROPERTY("bool", Bool);
- REQUIRE_PROPERTY("float", Float);
- REQUIRE_PROPERTY("double", Double);
- REQUIRE_PROPERTY("string", String);
- REQUIRE_PROPERTY("data", Data);
- REQUIRE_PROPERTY("date", Date);
- REQUIRE_PROPERTY("object", Object|PropertyType::Nullable, "target");
- REQUIRE_PROPERTY("array", Array|PropertyType::Object, "target");
- REQUIRE_PROPERTY("int?", Int|PropertyType::Nullable);
- REQUIRE_PROPERTY("bool?", Bool|PropertyType::Nullable);
- REQUIRE_PROPERTY("float?", Float|PropertyType::Nullable);
- REQUIRE_PROPERTY("double?", Double|PropertyType::Nullable);
- REQUIRE_PROPERTY("string?", String|PropertyType::Nullable);
- REQUIRE_PROPERTY("data?", Data|PropertyType::Nullable);
- REQUIRE_PROPERTY("date?", Date|PropertyType::Nullable);
- REQUIRE_PROPERTY("int array", Int|PropertyType::Array);
- REQUIRE_PROPERTY("bool array", Bool|PropertyType::Array);
- REQUIRE_PROPERTY("float array", Float|PropertyType::Array);
- REQUIRE_PROPERTY("double array", Double|PropertyType::Array);
- REQUIRE_PROPERTY("string array", String|PropertyType::Array);
- REQUIRE_PROPERTY("data array", Data|PropertyType::Array);
- REQUIRE_PROPERTY("date array", Date|PropertyType::Array);
- REQUIRE_PROPERTY("int? array", Int|PropertyType::Array|PropertyType::Nullable);
- REQUIRE_PROPERTY("bool? array", Bool|PropertyType::Array|PropertyType::Nullable);
- REQUIRE_PROPERTY("float? array", Float|PropertyType::Array|PropertyType::Nullable);
- REQUIRE_PROPERTY("double? array", Double|PropertyType::Array|PropertyType::Nullable);
- REQUIRE_PROPERTY("string? array", String|PropertyType::Array|PropertyType::Nullable);
- REQUIRE_PROPERTY("data? array", Data|PropertyType::Array|PropertyType::Nullable);
- REQUIRE_PROPERTY("date? array", Date|PropertyType::Array|PropertyType::Nullable);
- REQUIRE_PROPERTY("indexed int", Int, Property::IsPrimary{false}, Property::IsIndexed{true});
- REQUIRE_PROPERTY("indexed bool", Bool, Property::IsPrimary{false}, Property::IsIndexed{true});
- REQUIRE_PROPERTY("indexed string", String, Property::IsPrimary{false}, Property::IsIndexed{true});
- REQUIRE_PROPERTY("indexed date", Date, Property::IsPrimary{false}, Property::IsIndexed{true});
- REQUIRE_PROPERTY("indexed int?", Int|PropertyType::Nullable, Property::IsPrimary{false}, Property::IsIndexed{true});
- REQUIRE_PROPERTY("indexed bool?", Bool|PropertyType::Nullable, Property::IsPrimary{false}, Property::IsIndexed{true});
- REQUIRE_PROPERTY("indexed string?", String|PropertyType::Nullable, Property::IsPrimary{false}, Property::IsIndexed{true});
- REQUIRE_PROPERTY("indexed date?", Date|PropertyType::Nullable, Property::IsPrimary{false}, Property::IsIndexed{true});
- }
- }
- TEST_CASE("Schema") {
- SECTION("validate()") {
- SECTION("rejects link properties with no target object") {
- Schema schema = {
- {"object", {
- {"link", PropertyType::Object|PropertyType::Nullable}
- }},
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.link' of type 'object' has unknown object type ''");
- }
- SECTION("rejects array properties with no target object") {
- Schema schema = {
- {"object", {
- {"array", PropertyType::Array|PropertyType::Object}
- }},
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.array' of type 'array' has unknown object type ''");
- }
- SECTION("rejects link properties with a target not in the schema") {
- Schema schema = {
- {"object", {
- {"link", PropertyType::Object|PropertyType::Nullable, "invalid target"}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.link' of type 'object' has unknown object type 'invalid target'");
- }
- SECTION("rejects array properties with a target not in the schema") {
- Schema schema = {
- {"object", {
- {"array", PropertyType::Array|PropertyType::Object, "invalid target"}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.array' of type 'array' has unknown object type 'invalid target'");
- }
- SECTION("rejects linking objects without a source object") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }, {
- {"incoming", PropertyType::Array|PropertyType::LinkingObjects, "", ""}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.incoming' of type 'linking objects' has unknown object type ''");
- }
- SECTION("rejects linking objects without a source property") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }, {
- {"incoming", PropertyType::Array|PropertyType::LinkingObjects, "object", ""}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.incoming' of type 'linking objects' must have an origin property name.");
- }
- SECTION("rejects linking objects with invalid source object") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }, {
- {"incoming", PropertyType::Array|PropertyType::LinkingObjects, "not an object type", ""}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.incoming' of type 'linking objects' has unknown object type 'not an object type'");
- }
- SECTION("rejects linking objects with invalid source property") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }, {
- {"incoming", PropertyType::Array|PropertyType::LinkingObjects, "object", "value"}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.value' declared as origin of linking objects property 'object.incoming' is not a link");
- schema = {
- {"object", {
- {"value", PropertyType::Int},
- {"link", PropertyType::Object|PropertyType::Nullable, "object 2"},
- }, {
- {"incoming", PropertyType::Array|PropertyType::LinkingObjects, "object", "link"}
- }},
- {"object 2", {
- {"value", PropertyType::Int},
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.link' declared as origin of linking objects property 'object.incoming' links to type 'object 2'");
- }
- SECTION("rejects non-array linking objects") {
- Schema schema = {
- {"object", {
- {"link", PropertyType::Object|PropertyType::Nullable, "object"},
- }, {
- {"incoming", PropertyType::LinkingObjects, "object", "link"}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Linking Objects property 'object.incoming' must be an array.");
- }
- SECTION("rejects target object types for non-link properties") {
- Schema schema = {
- {"object", {
- {"int", PropertyType::Int},
- {"bool", PropertyType::Bool},
- {"float", PropertyType::Float},
- {"double", PropertyType::Double},
- {"string", PropertyType::String},
- {"date", PropertyType::Date},
- }}
- };
- for (auto& prop : schema.begin()->persisted_properties) {
- REQUIRE_NOTHROW(schema.validate());
- prop.object_type = "object";
- REQUIRE_THROWS_CONTAINING(schema.validate(), "cannot have an object type.");
- prop.object_type = "";
- }
- }
- SECTION("rejects source property name for non-linking objects properties") {
- Schema schema = {
- {"object", {
- {"int", PropertyType::Int},
- {"bool", PropertyType::Bool},
- {"float", PropertyType::Float},
- {"double", PropertyType::Double},
- {"string", PropertyType::String},
- {"data", PropertyType::Data},
- {"date", PropertyType::Date},
- {"object", PropertyType::Object|PropertyType::Nullable, "object"},
- {"array", PropertyType::Object|PropertyType::Array, "object"},
- }}
- };
- for (auto& prop : schema.begin()->persisted_properties) {
- REQUIRE_NOTHROW(schema.validate());
- prop.link_origin_property_name = "source";
- auto expected = util::format("Property 'object.%1' of type '%1' cannot have an origin property name.", prop.name, prop.name);
- REQUIRE_THROWS_CONTAINING(schema.validate(), expected);
- prop.link_origin_property_name = "";
- }
- }
- SECTION("rejects non-nullable link properties") {
- Schema schema = {
- {"object", {
- {"link", PropertyType::Object, "target"}
- }},
- {"target", {
- {"value", PropertyType::Int}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.link' of type 'object' must be nullable.");
- }
- SECTION("rejects nullable array properties") {
- Schema schema = {
- {"object", {
- {"array", PropertyType::Array|PropertyType::Object|PropertyType::Nullable, "target"}
- }},
- {"target", {
- {"value", PropertyType::Int}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.array' of type 'array' cannot be nullable.");
- }
- SECTION("rejects nullable linking objects") {
- Schema schema = {
- {"object", {
- {"link", PropertyType::Object|PropertyType::Nullable, "object"},
- }, {
- {"incoming", PropertyType::LinkingObjects|PropertyType::Array|PropertyType::Nullable, "object", "link"}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.incoming' of type 'linking objects' cannot be nullable.");
- }
- SECTION("rejects duplicate primary keys") {
- Schema schema = {
- {"object", {
- {"pk1", PropertyType::Int, Property::IsPrimary{true}},
- {"pk2", PropertyType::Int, Property::IsPrimary{true}},
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Properties 'pk2' and 'pk1' are both marked as the primary key of 'object'.");
- }
- SECTION("rejects invalid primary key types") {
- Schema schema = {
- {"object", {
- {"pk", PropertyType::Float, Property::IsPrimary{true}},
- }}
- };
- schema.begin()->primary_key_property()->type = PropertyType::Any;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'any' cannot be made the primary key.");
- schema.begin()->primary_key_property()->type = PropertyType::Bool;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'bool' cannot be made the primary key.");
- schema.begin()->primary_key_property()->type = PropertyType::Float;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'float' cannot be made the primary key.");
- schema.begin()->primary_key_property()->type = PropertyType::Double;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'double' cannot be made the primary key.");
- schema.begin()->primary_key_property()->type = PropertyType::Object;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'object' cannot be made the primary key.");
- schema.begin()->primary_key_property()->type = PropertyType::LinkingObjects;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'linking objects' cannot be made the primary key.");
- schema.begin()->primary_key_property()->type = PropertyType::Data;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'data' cannot be made the primary key.");
- schema.begin()->primary_key_property()->type = PropertyType::Date;
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Property 'object.pk' of type 'date' cannot be made the primary key.");
- }
- SECTION("allows valid primary key types") {
- Schema schema = {
- {"object", {
- {"pk", PropertyType::Int, Property::IsPrimary{true}},
- }}
- };
- REQUIRE_NOTHROW(schema.validate());
- schema.begin()->primary_key_property()->type = PropertyType::Int|PropertyType::Nullable;
- REQUIRE_NOTHROW(schema.validate());
- schema.begin()->primary_key_property()->type = PropertyType::String;
- REQUIRE_NOTHROW(schema.validate());
- schema.begin()->primary_key_property()->type = PropertyType::String|PropertyType::Nullable;
- REQUIRE_NOTHROW(schema.validate());
- }
- SECTION("rejects nonexistent primary key") {
- Schema schema = {
- {"object", {
- {"value", PropertyType::Int},
- }}
- };
- schema.begin()->primary_key = "nonexistent";
- REQUIRE_THROWS_CONTAINING(schema.validate(), "Specified primary key 'object.nonexistent' does not exist.");
- }
- SECTION("rejects indexes for types that cannot be indexed") {
- Schema schema = {
- {"object", {
- {"float", PropertyType::Float},
- {"double", PropertyType::Double},
- {"data", PropertyType::Data},
- {"object", PropertyType::Object|PropertyType::Nullable, "object"},
- {"array", PropertyType::Array|PropertyType::Object, "object"},
- }}
- };
- for (auto& prop : schema.begin()->persisted_properties) {
- REQUIRE_NOTHROW(schema.validate());
- prop.is_indexed = true;
- auto expected = util::format("Property 'object.%1' of type '%1' cannot be indexed.", prop.name);
- REQUIRE_THROWS_CONTAINING(schema.validate(), expected);
- prop.is_indexed = false;
- }
- }
- SECTION("allows indexing types that can be indexed") {
- Schema schema = {
- {"object", {
- {"int", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- {"bool", PropertyType::Bool, Property::IsPrimary{false}, Property::IsIndexed{true}},
- {"string", PropertyType::String, Property::IsPrimary{false}, Property::IsIndexed{true}},
- {"date", PropertyType::Date, Property::IsPrimary{false}, Property::IsIndexed{true}},
- }}
- };
- REQUIRE_NOTHROW(schema.validate());
- }
- SECTION("rejects duplicate types with the same name") {
- Schema schema = {
- {"object1", {
- {"int", PropertyType::Int},
- }},
- {"object2", {
- {"int", PropertyType::Int},
- }},
- {"object3", {
- {"int", PropertyType::Int},
- }},
- {"object2", {
- {"int", PropertyType::Int},
- }},
- {"object1", {
- {"int", PropertyType::Int},
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(),
- "- Type 'object1' appears more than once in the schema.\n"
- "- Type 'object2' appears more than once in the schema.");
- }
- SECTION("rejects properties with the same name") {
- Schema schema = {
- {"object", {
- {"child", PropertyType::Object|PropertyType::Nullable, "object"},
- {"parent", PropertyType::Int},
- {"field1", PropertyType::Int},
- {"field2", PropertyType::String},
- {"field1", PropertyType::String},
- {"field2", PropertyType::String},
- {"field1", PropertyType::Int},
- }, {
- {"parent", PropertyType::Array|PropertyType::LinkingObjects, "object", "child"}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(),
- "- Property 'field1' appears more than once in the schema for type 'object'.\n"
- "- Property 'field2' appears more than once in the schema for type 'object'.\n"
- "- Property 'parent' appears more than once in the schema for type 'object'.");
- }
- SECTION("rejects schema if all properties have the same name") {
- Schema schema = {
- {"object", {
- {"field", PropertyType::Int},
- {"otherField", PropertyType::Int},
- {"field", PropertyType::Int},
- {"otherField", PropertyType::Int},
- {"field", PropertyType::Int},
- {"otherField", PropertyType::Int},
- {"field", PropertyType::Int},
- {"otherField", PropertyType::Int},
- {"field", PropertyType::Int},
- {"otherField", PropertyType::Int},
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(),
- "- Property 'field' appears more than once in the schema for type 'object'.\n"
- "- Property 'otherField' appears more than once in the schema for type 'object'.");
- }
- SECTION("rejects properties with the same alias") {
- Schema schema = {
- {"object", {
- {"child", PropertyType::Object|PropertyType::Nullable, "object"},
-
- {"parentA", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "_parent"},
-
- {"fieldA", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "_field1"},
- {"fieldB", PropertyType::String, Property::IsPrimary{false}, Property::IsIndexed{false}, "_field2"},
- {"fieldC", PropertyType::String, Property::IsPrimary{false}, Property::IsIndexed{false}, "_field1"},
- {"fieldD", PropertyType::String, Property::IsPrimary{false}, Property::IsIndexed{false}, "_field2"},
- {"fieldE", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "_field1"},
-
- {"fieldF", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "fieldF"},
- }, {
-
- {"parentB", PropertyType::Array|PropertyType::LinkingObjects, "object", "child", "_parent"}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(),
- "- Alias '_field1' appears more than once in the schema for type 'object'.\n"
- "- Alias '_field2' appears more than once in the schema for type 'object'.\n"
- "- Alias '_parent' appears more than once in the schema for type 'object'.");
- }
- SECTION("rejects properties whose name conflicts with an alias for another property") {
- Schema schema = {
- {"object", {
- {"child", PropertyType::Object|PropertyType::Nullable, "object"},
- {"field1", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "field2"},
- {"field2", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{false}, "parent"},
- }, {
- {"parent", PropertyType::Array|PropertyType::LinkingObjects, "object", "child", "field1"}
- }}
- };
- REQUIRE_THROWS_CONTAINING(schema.validate(),
- "- Property 'object.parent' has an alias 'field1' that conflicts with a property of the same name.\n"
- "- Property 'object.field1' has an alias 'field2' that conflicts with a property of the same name.\n"
- "- Property 'object.field2' has an alias 'parent' that conflicts with a property of the same name.");
- }
- }
- SECTION("compare()") {
- using namespace schema_change;
- using vec = std::vector<SchemaChange>;
- SECTION("add table") {
- Schema schema1 = {
- {"object 1", {
- {"int", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object 1", {
- {"int", PropertyType::Int},
- }},
- {"object 2", {
- {"int", PropertyType::Int},
- }}
- };
- auto obj = &*schema2.find("object 2");
- auto expected = vec{AddTable{obj}, AddInitialProperties{obj}};
- REQUIRE(schema1.compare(schema2) == expected);
- }
- SECTION("add property") {
- Schema schema1 = {
- {"object", {
- {"int 1", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"int 1", PropertyType::Int},
- {"int 2", PropertyType::Int},
- }}
- };
- REQUIRE(schema1.compare(schema2) == vec{(AddProperty{&*schema1.find("object"), &schema2.find("object")->persisted_properties[1]})});
- }
- SECTION("remove property") {
- Schema schema1 = {
- {"object", {
- {"int 1", PropertyType::Int},
- {"int 2", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"int 1", PropertyType::Int},
- }}
- };
- REQUIRE(schema1.compare(schema2) == vec{(RemoveProperty{&*schema1.find("object"), &schema1.find("object")->persisted_properties[1]})});
- }
- SECTION("change property type") {
- Schema schema1 = {
- {"object", {
- {"value", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"value", PropertyType::Double},
- }}
- };
- REQUIRE(schema1.compare(schema2) == vec{(ChangePropertyType{
- &*schema1.find("object"),
- &schema1.find("object")->persisted_properties[0],
- &schema2.find("object")->persisted_properties[0]})});
- };
- SECTION("change link target") {
- Schema schema1 = {
- {"object", {
- {"value", PropertyType::Object, "target 1"},
- }},
- {"target 1", {
- {"value", PropertyType::Int},
- }},
- {"target 2", {
- {"value", PropertyType::Int},
- }},
- };
- Schema schema2 = {
- {"object", {
- {"value", PropertyType::Object, "target 2"},
- }},
- {"target 1", {
- {"value", PropertyType::Int},
- }},
- {"target 2", {
- {"value", PropertyType::Int},
- }},
- };
- REQUIRE(schema1.compare(schema2) == vec{(ChangePropertyType{
- &*schema1.find("object"),
- &schema1.find("object")->persisted_properties[0],
- &schema2.find("object")->persisted_properties[0]})});
- }
- SECTION("add index") {
- Schema schema1 = {
- {"object", {
- {"int", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"int", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- }}
- };
- auto object_schema = &*schema1.find("object");
- REQUIRE(schema1.compare(schema2) == vec{(AddIndex{object_schema, &object_schema->persisted_properties[0]})});
- }
- SECTION("remove index") {
- Schema schema1 = {
- {"object", {
- {"int", PropertyType::Int, Property::IsPrimary{false}, Property::IsIndexed{true}},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"int", PropertyType::Int},
- }}
- };
- auto object_schema = &*schema1.find("object");
- REQUIRE(schema1.compare(schema2) == vec{(RemoveIndex{object_schema, &object_schema->persisted_properties[0]})});
- }
- SECTION("add index and make nullable") {
- Schema schema1 = {
- {"object", {
- {"int", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"int", PropertyType::Int|PropertyType::Nullable, Property::IsPrimary{false}, Property::IsIndexed{true}},
- }}
- };
- auto object_schema = &*schema1.find("object");
- REQUIRE(schema1.compare(schema2) == (vec{
- MakePropertyNullable{object_schema, &object_schema->persisted_properties[0]},
- AddIndex{object_schema, &object_schema->persisted_properties[0]}}));
- }
- SECTION("add index and change type") {
- Schema schema1 = {
- {"object", {
- {"value", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"value", PropertyType::Double, Property::IsPrimary{false}, Property::IsIndexed{true}},
- }}
- };
- REQUIRE(schema1.compare(schema2) == vec{(ChangePropertyType{
- &*schema1.find("object"),
- &schema1.find("object")->persisted_properties[0],
- &schema2.find("object")->persisted_properties[0]})});
- }
- SECTION("make nullable and change type") {
- Schema schema1 = {
- {"object", {
- {"value", PropertyType::Int},
- }}
- };
- Schema schema2 = {
- {"object", {
- {"value", PropertyType::Double|PropertyType::Nullable},
- }}
- };
- REQUIRE(schema1.compare(schema2) == vec{(ChangePropertyType{
- &*schema1.find("object"),
- &schema1.find("object")->persisted_properties[0],
- &schema2.find("object")->persisted_properties[0]})});
- }
- }
- }
|