-
Notifications
You must be signed in to change notification settings - Fork 22
Add tests for rebase with database-defined constraints #230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add tests for rebase with database-defined constraints #230
Conversation
|
@wonder-sk - it may be useful to have a call to discuss this, particularly how the new scenarios where there are genuine UNIQUE or FK constraint violations. |
|
Hi, I'm working on fixing #210 (see PR #232). Your tests have been helpful in checking that my patches actually work and I think it's a good idea to get them into Geodiff to guard against regressions. I have a few things that I'd like to see addressed, though:
These shouldn't be too much trouble and I hope we'll soon have this issue fixed and tested. |
8413c83 to
6909abf
Compare
|
Hi, thank you for the work so far on issue #210 (PR #232). We have cherry-picked the commit onto this branch and can confirm that four of the tests are now XPASSing (with a renamed test file): Those tests have now been unmarked. Of the four that are still expected to fail, two are failing due to your final point above: The final two are failing due to foreign key violations: |
|
Thanks for this @dvdkon. Some comments from when @ximenesuk and I ran the code today.
|
|
Another point regarding the |
All the issues checked by this function are now handled.
Co-authored-by: David Koňařík <dvdkon@konarici.cz>
|
@volcan01010 Thanks for making the changes, I've slightly modified your tests to fit the current Geodiff behaviour and cherry-picked them into my PR (#232). Also thanks for reminding me of I've set the SQLite driver to always enable foreign key constraints, since users couldn't enable them on their own otherwise. Sadly we can't report conflicts caused by DB constraints in the conflicts file, because they are only discovered at the very end, after the diffs are written and when they're being applied to the database. The problematic changes are at least written as warnings to the log (which goes to stdout when |
|
Thanks. I'll close this merge request and continue discussion on #232. |
|
I've re-opened, as we can't push code to the branch in the other pull request and may have other suggested edits for the tests. |
|
This is great news. I'm happy that foreign keys will be checked now, because they are important to us.
It would be valuable to get error information back in a more direct way, and at the moment the function is called. Two possible ways are:
My preference would be for adding more information to the I expect that our users will get UNIQUE constraint errors from time to time. Our field data capture plugin uses QGIS expressions to autogenerate locality IDs, which have a unique constraint, based on the user login name. If a user works on two separate devices without syncing as they change between them, they will collect localities with matching IDs. This will result in a genuine unique constraint error when they rebase. I know that this happens, because I have done it myself! It would be nice to be able to explain this when the rebase fails. |
|
Hi @dvdkon, I have merged your changes back onto this branch and pushed up a few more commits.
|
|
Looking at pygeodiff source code, I see that it doesn't get any text argument back from C++ library, only a return code. This would make it hard to give much debugging information in the error. As a minimum, just knowing whether it was a UNIQUE or FOREIGN KEY error would be valuable. Can you add extra return codes and error types? Then class GeoDiffLibConstraintError(GeoDiffLibError):
pass
# keep in sync with c-library
SUCCESS = 0
ERROR = 1
CONFLICT = 2
UNSUPPORTED_CHANGE = 3
FAILED_UNIQUE_CONSTRAINT = 4
FAILED_FOREIGN_KEY_CONSTRAINT = 5
def _parse_return_code(rc, msg):
if rc == SUCCESS:
return
elif rc == ERROR:
raise GeoDiffLibError(msg)
elif rc == CONFLICT:
raise GeoDiffLibConflictError(msg)
elif rc == UNSUPPORTED_CHANGE:
raise GeoDiffLibUnsupportedChangeError(msg)
elif rc == FAILED_UNIQUE_CONSTRAINT:
raise GeoDiffLibConstraintError(f"{msg} (UNIQUE constraint failed)")
elif rc == FAILED_FOREIGN_KEY_CONSTRAINT:
raise GeoDiffLibConstraintError(f"{msg} (FOREIGN KEY constraint failed)")
else:
raise GeoDiffLibVersionError(
"Internal error (enum " + str(rc) + " not handled)"
)Or you could have separate |
|
I've just pushed a commit that uses a custom LoggingGeoDiff class to check the log messages. This is helpful for the test, but wouldn't help with the stock GeoDiff, where it would be useful to know what kind of error was thrown. Below is output from the constration violation tests. $ GEODIFFLIB=$(pwd)/geodiff/build/libgeodiff.so GEODIFFCLI=$(pwd)/geodiff/build/geodiff pytest pygeodiff/tests/test_rebase_db_constraint.py -k violation -vvvs
=================================================================== test session starts ====================================================================
platform linux -- Python 3.13.8, pytest-8.4.2, pluggy-1.6.0 -- /home/user/.virtualenvs/geodiff/bin/python3.13
cachedir: .pytest_cache
rootdir: /home/user/github/MerginMaps/geodiff
configfile: pyproject.toml
collected 18 items / 14 deselected / 4 selected
pygeodiff/tests/test_rebase_db_constraint.py::test_geodiff_rebase_unique_constraint_violation[user_a_data_first] DEBUG: rebase info (base2their / old)
TABLE species
inserted 4,
deleted --none --
updated --none --
DEBUG: mapping
species
4->5,
DEBUG: No conflicts present
WARNING: CONFLICT: unresolvable_conflict:
{
"changes": [
{
"column": 0,
"new": 5
},
{
"column": 1,
"new": "BCH"
},
{
"column": 2,
"new": "Beech"
}
],
"table": "species",
"type": "insert"
}
ERROR: Could not resolve dependencies in constraint conflicts.
ERROR: Unable to perform GEODIFF_applyChangeset modified2final
PASSED
pygeodiff/tests/test_rebase_db_constraint.py::test_geodiff_rebase_unique_constraint_violation[user_b_data_first] DEBUG: rebase info (base2their / old)
TABLE species
inserted 4,
deleted --none --
updated --none --
DEBUG: mapping
species
4->5,
DEBUG: No conflicts present
WARNING: CONFLICT: unresolvable_conflict:
{
"changes": [
{
"column": 0,
"new": 5
},
{
"column": 1,
"new": "BCH"
},
{
"column": 2,
"new": "Birch"
}
],
"table": "species",
"type": "insert"
}
ERROR: Could not resolve dependencies in constraint conflicts.
ERROR: Unable to perform GEODIFF_applyChangeset modified2final
PASSED
pygeodiff/tests/test_rebase_db_constraint.py::test_geodiff_rebase_fkey_constraint_violation[user_a_data_first] DEBUG: rebase info (base2their / old)
TABLE species
inserted --none --
deleted 3,
updated --none --
TABLE trees
inserted --none --
deleted 3,
updated --none --
DEBUG: mapping
trees
--none --
DEBUG: No conflicts present
ERROR: Failed to release savepoint (SQLITE3 error [787]: FOREIGN KEY constraint failed)
ERROR: Unable to perform GEODIFF_applyChangeset modified2final
PASSED
pygeodiff/tests/test_rebase_db_constraint.py::test_geodiff_rebase_fkey_constraint_violation[user_b_data_first] DEBUG: rebase info (base2their / old)
TABLE trees
inserted 4,
deleted --none --
updated --none --
DEBUG: mapping
--none --
DEBUG: No conflicts present
ERROR: Failed to release savepoint (SQLITE3 error [787]: FOREIGN KEY constraint failed)
ERROR: Unable to perform GEODIFF_applyChangeset modified2final
PASSED |
I agree that GeoDiff could (and should) give more detailed errors to the caller, without hacks like reading the log. I made some changes and pushed them to a dev branch, I'll make a PR later, once the base change gets merged. I "hijacked" the |
|
Amazing! Thank you. I've pushed up a couple more commits. They disable LoggingGeodiff and add a couple of other tests. The first checks when no changes have been made. That is just to prevent regressions. The second adds another failure scenario, where the failure is caused by an error raised by a trigger script. The test layout matches the ones in your development branch. It will be interesting to see if the error message propagates through in the same way. |
82cea53 to
be0b1f3
Compare
be0b1f3 to
437220a
Compare
Description
This pull request adds a set of tests for the behaviour of
geodiff.rebase()within pygeodiff.They are intended as an aid to addressing issue #210, where the presence of UNIQUE constraints in a database causes geodiff to throw an error.
The first group of tests covers situations where there are no conflicts or constraint violations and geodiff is expected to give the same results with or without the database constraints. The current behaviour is that these raise an error in databases with a UNIQUE constraint.
The second group of tests covers situations where edits by users have resulted in a conflicting UPDATE, a violation of a UNIQUE constraint or the violation of a FOREIGN KEY constraint. The latter two are new scenarios, and I wasn't sure what the correct behavior of geodiff should be. I have followed the pattern set by the conflicting updates.
Tests that fail with the current geodiff implementation are marked with pytest's
xfailflag. This allows this code to be merged without it failing the CI pipeline. Once #210 is solved, the tests will pass and the flag can be removed.Note that these tests only address a SQLite database. SQLite databases will only enforce a FOREIGN KEY constraint if explicitly configured to via a PRAGMA command. I have not run the tests against a PostGIS database.
Example output
This pull request supersedes pull request #229