diff --git a/test/test_apidb_backend_changeset_uploads.cpp b/test/test_apidb_backend_changeset_uploads.cpp index 86d11f50..b826833d 100644 --- a/test/test_apidb_backend_changeset_uploads.cpp +++ b/test/test_apidb_backend_changeset_uploads.cpp @@ -1646,6 +1646,126 @@ TEST_CASE_METHOD( DatabaseTestsFixture, "test_single_relations", "[changeset][up } } + SECTION("Deletion relations, multilevel nested relations with dependency cycles") + { + + /* + Test case for https://github.com/zerebubuth/openstreetmap-cgimap/issues/223#issuecomment-617381115 + + In this test case, we're checking that deleting relations -2, -3 and -4 is not possible, because they + are directly (or indirectly) referenced by relation -1 as relation member. + + In addition, relations -2, -3 and -4 have a cyclic dependency. This way, we can test + if the recursive relation member resolution in collect_recursive_relation_rel_member_ids + works as expected. + + +----+ +----+ + | -1 | <-- | -2 | <+ + +----+ +----+ | + | | + | | + v | + +----+ | + | -4 | | + +----+ | + | | + | | + v | + +----+ | + | -3 | -+ + +----+ + + "-1 <---- -2" means: relation -2 is a relation member of relation -1 + + */ + + static api06::OSMChange_Tracking change_tracking_1{}; // for step 1: create relations + static api06::OSMChange_Tracking change_tracking_2{}; // for step 2: modify 1 relation + static api06::OSMChange_Tracking change_tracking_3{}; // for step 3: delete 3 relations + + SECTION("Create multi-level relations") + { + auto upd = tdb.get_data_update(); + auto rel_updater = upd->get_relation_updater(ctx, change_tracking_1); + + // Note: we cannot add Relation -2 as a Relation member of relation -4 during creation, + // because relation -2 is not known at this point yet, and we don't allow forward + // references for Rails compatibility reasons. + rel_updater->add_relation(1, -4, { }, {{"key4", "value4"}}); + rel_updater->add_relation(1, -3, { { "Relation", -4, "role4" }}, {{"key3", "value3"}}); + rel_updater->add_relation(1, -2, { { "Relation", -3, "role3" }}, {{"key2", "value2"}}); + rel_updater->add_relation(1, -1, { { "Relation", -2, "role2" }}, {{"key1", "value1"}}); + REQUIRE_NOTHROW(rel_updater->process_new_relations()); + + upd->commit(); + + REQUIRE(change_tracking_1.created_relation_ids.size() == 4); + for (int i = 0; i < 4; i++) { + REQUIRE(change_tracking_1.created_relation_ids[i].new_version == 1); + REQUIRE(change_tracking_1.created_relation_ids[i].old_id == -4 + i); + REQUIRE(change_tracking_1.created_relation_ids[i].new_id >= 1); + } + } + + SECTION("Change relation -4 by adding -2 as relation member (adds dependency loop)") + { + auto upd = tdb.get_data_update(); + auto rel_updater = upd->get_relation_updater(ctx, change_tracking_2); + + rel_updater->modify_relation(1, + change_tracking_1.created_relation_ids[0].new_id, + change_tracking_1.created_relation_ids[0].new_version, + { + { "Relation", static_cast<osm_nwr_signed_id_t>(change_tracking_1.created_relation_ids[2].new_id), "role2" } + }, + {{"key2", "value2"}}); + + REQUIRE_NOTHROW(rel_updater->process_modify_relations()); + REQUIRE_NOTHROW(upd->commit()); + + REQUIRE(change_tracking_2.modified_relation_ids.size() == 1); + REQUIRE(change_tracking_2.modified_relation_ids[0].new_id == change_tracking_1.created_relation_ids[0].new_id); + REQUIRE(change_tracking_2.modified_relation_ids[0].new_version > change_tracking_1.created_relation_ids[0].new_version); + } + + SECTION("Try to delete relations -2, -3 and -4, if-unused set") + { + auto upd = tdb.get_data_update(); + auto rel_updater = upd->get_relation_updater(ctx, change_tracking_3); + + // Delete relation -4 + rel_updater->delete_relation(1, + change_tracking_2.modified_relation_ids[0].new_id, + change_tracking_2.modified_relation_ids[0].new_version, + true); + + // Delete relation -3 + rel_updater->delete_relation(1, + change_tracking_1.created_relation_ids[1].new_id, + change_tracking_1.created_relation_ids[1].new_version, + true); + + // Delete relation -2 + rel_updater->delete_relation(1, + change_tracking_1.created_relation_ids[2].new_id, + change_tracking_1.created_relation_ids[2].new_version, + true); + + REQUIRE_NOTHROW(rel_updater->process_delete_relations()); + REQUIRE_NOTHROW(upd->commit()); + + REQUIRE(change_tracking_3.deleted_relation_ids.size() == 0); + REQUIRE(change_tracking_3.skip_deleted_relation_ids.size() == 3); + + auto sel = tdb.get_data_selection(); + + // check that there are no changes on the database, all 4 relations are all still visible + for (int i = 0; i < 4; i++) { + REQUIRE(sel->check_relation_visibility(static_cast<osm_nwr_signed_id_t>(change_tracking_1.created_relation_ids[i].new_id)) == data_selection::exists); + } + } + } + SECTION("Testing locking of future relation members") { // this test is checking that locking in ApiDB_Relation_Updater::lock_future_members is working as expected