Skip to content
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

Run tests against PostgreSQL, fix compatibility #170

Merged
merged 63 commits into from
Jun 27, 2022
Merged

Conversation

adzialocha
Copy link
Member

@adzialocha adzialocha commented Jun 22, 2022

  • Add CI checking tests against PostgreSQL database 🟢
  • Make all SQL statements compatible with SQLite and PostgreSQL
    • The queries we needed to change are commented in this PR
  • Introduces TestDatabaseRunner which is now used in all tests to make sure the database gets disconnected after every succeeding or failing test. It changes our style of making tests slightly, but not too much:
    • Test fn is not async
    • No #[tokio::test] attribute used, we build our own tokio runtime
    • Test is executed within the new TestDatabaseRunner::with_db_teardown method which also gives you access to the underlying TestDatabase instance
  • Removes MySQL support, its complicated and we're not sure why we need it 🐈
  • Fixes runtime of sqlx by switching it to tokio (it was still set to async_std and probably forgotten after the switch)
  • Fixes a bunch of formatting and refactors some tests which didn't make use of the new fixtures yet

Closing: #31 and #98

📋 Checklist

  • Add tests that cover your changes
  • Add this PR to the Unreleased section in CHANGELOG.md
  • Link this PR to any issues it closes
  • New files contain a SPDX license header

@adzialocha adzialocha linked an issue Jun 22, 2022 that may be closed by this pull request
@adzialocha adzialocha changed the title Make database url configurable for tests Run tests against PostgreSQL and MySQL databases Jun 22, 2022
@codecov
Copy link

codecov bot commented Jun 22, 2022

Codecov Report

Merging #170 (5369d26) into development (5a1a845) will increase coverage by 0.65%.
The diff coverage is 99.31%.

@@               Coverage Diff               @@
##           development     #170      +/-   ##
===============================================
+ Coverage        90.01%   90.66%   +0.65%     
===============================================
  Files               43       43              
  Lines             2633     2807     +174     
===============================================
+ Hits              2370     2545     +175     
+ Misses             263      262       -1     
Impacted Files Coverage Δ
aquadoggo/src/config.rs 6.25% <ø> (ø)
aquadoggo/src/db/mod.rs 83.33% <ø> (-1.29%) ⬇️
aquadoggo/src/http/service.rs 91.30% <92.30%> (-3.94%) ⬇️
aquadoggo/src/db/stores/test_utils.rs 97.65% <93.93%> (-1.33%) ⬇️
aquadoggo/src/db/stores/operation.rs 91.15% <95.23%> (+0.07%) ⬆️
aquadoggo/src/graphql/client/query.rs 95.12% <97.05%> (+1.18%) ⬆️
aquadoggo/src/graphql/client/mutation.rs 98.43% <99.42%> (+0.38%) ⬆️
aquadoggo/src/db/stores/document.rs 94.02% <100.00%> (+0.56%) ⬆️
aquadoggo/src/db/stores/entry.rs 95.95% <100.00%> (+0.06%) ⬆️
aquadoggo/src/db/stores/log.rs 97.36% <100.00%> (-0.54%) ⬇️
... and 11 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5a1a845...5369d26. Read the comment docs.

@@ -21,8 +21,6 @@ pub async fn create_database(url: &str) -> Result<()> {
Any::create_database(url).await?;
}

Any::drop_database(url);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was that? 😱

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was never await-ed, therefore it also never worked, but 😱

@@ -144,7 +144,7 @@ impl OperationStore<VerifiedOperation> for SqlStorage {
.bind(name.to_owned())
.bind(value.field_type().to_string())
.bind(db_value)
.bind(index.to_string())
.bind(index as i32)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👆 PostgreSQL was complaining that this column is actually a number (NUMERIC).

COALESCE(document_view_id, 0)
);
-- @TODO: This fails using PostgreSQL
-- CREATE UNIQUE INDEX ux_tasks ON tasks (
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👆 Upsi, this doesn't seem to work on PostgreSQL 😢

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the migrations fail all tests fail for PostgreSQL and MySQL currently

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed now via:

CREATE UNIQUE INDEX ux_tasks ON tasks (
    name,
    COALESCE(document_id, '0'),
    COALESCE(document_view_id, '0')
);

@@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS operation_fields_v1 (
operation_id TEXT NOT NULL,
name TEXT NOT NULL,
field_type TEXT NOT NULL,
value BLOB NULL,
value TEXT NULL,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👆 BLOB is not known for PostgreSQL?

@adzialocha
Copy link
Member Author

adzialocha commented Jun 23, 2022

Small report

Problems of testing a database

So far this "somewhat" does what it promises: It runs the tests against a PostgreSQL and MySQL database. Problem is, that whenever a test panics it leaves the connection to the database alive, nothing gets disconnected afterwards 😢 Since we drop and re-create the database for every test this leads to ugly errors:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Database(PgDatabaseError { severity: Error, code: "55006", message: "database \"aquadoggo-development\" is being accessed by other users", detail: Some("There are 13 other sessions using the database."), hint: None, position: None, where: None, schema: None, table: None, column: None, data_type: None, constraint: None, file: Some("dbcommands.c"), line: Some(872), routine: Some("dropdb") })'

You can see it here for example: https://github.com/p2panda/aquadoggo/runs/7022667094?check_suite_focus=true The first test fails "correctly" and then all the others fail because of this lock.

I couldn't find a way to disconnect from databases automatically when something fails, except of manually catching every error and doing it manually .. maybe you have an idea?

Also, by testing a "real" database, we can't run the tests concurrently, we can assure this by adding -- --test-threads=1 to cargo test. We could rename the used database every time to something random, this would give us concurrency and also solve the locking problem of failing tests, but then you have like thousands of random databases left in your PostgreSQL? Not sure what to do here ..

I assume as soon as all tests are working again and we manually disconnect from the databases after every test it will go fine without any further changes.

Our SQL queries

Otherwise, we get what we want, which is feedback on how it works with other databases and unluckily there are a few things failing.

I've made some first fixes as commented in this PR, but there is more and I don't know if this is the place to fix that? I'd be fine doing it here.

Here is an example of a failing test:

FatalStorageError("error occurred while decoding column \"previous_operations\": unexpected null; try decoding as an `Option`")', aquadoggo/src/db/stores/document.rs:609:14

Where apparently previous_operations is a NULL column but in the deserialized OperationRow its not an Option. Funny, that SQLite didn't cry about this 😅

.. and wow, what was that? #170 (comment) 😰

@adzialocha adzialocha changed the title Run tests against PostgreSQL and MySQL databases Run tests against PostgreSQL Jun 24, 2022
@adzialocha adzialocha changed the title Run tests against PostgreSQL Run tests against PostgreSQL, fix compatibility Jun 24, 2022
@@ -248,7 +248,7 @@ impl TestDatabaseRunner {
pool.close().await;

// Panic here when test failed to propagate it further
assert!(result.is_ok());
result.unwrap();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this will make #[should_panic] work again

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, DOH!!! Yeh, hopefully, why didn't I think of that...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested it, it works again 👍

@adzialocha adzialocha marked this pull request as ready for review June 24, 2022 11:51
COALESCE(document_id, 0),
COALESCE(document_view_id, 0)
COALESCE(document_id, '0'),
COALESCE(document_view_id, '0')
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be a string

database: UNIQUE constraint failed: entries.author, entries.log_id, entries.seq_num"
)
let result = db.store.insert_entry(duplicate_doggo_entry).await;
assert!(result.is_err());
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returned error strings are different depending on the used database, so we need to just check via is_err

Copy link
Member

@sandreae sandreae left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really happy with this now.

Regarding the point I mentioned on the call earlier, I think seeing as we really are use rstest everywhere then there's no need to add another option now.

}

impl TestDatabaseRunner {
pub fn with_db_teardown<F: AsyncTestFn + Send + Sync + 'static>(&self, test: F) {
Copy link
Member

@cafca cafca Jun 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A docstring here could be friendly for when you encounter this function in some test. Or maybe just a reference to another place to learn more.

// be reached even when the test panicked
pool.close().await;

// Panic here when test failed to propagate it further
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I understand this docstring. Propagate the error further? Wouldn't this get triggered exactly when an error is propagated further and not when it is not? If this enables #[should_panic] that could also be nice to mention here for whoever edits this code next.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I understand your question, but I'll try to explain it from another angle: Panicks in tasks are not propagated further ("unwinded"), they just crash the task. To allow the test runner to understand that something happend we have to take the task handle, unwrap it to inform the "parent" about its failure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function of this is to make the test fail when it should :-D

Copy link
Member

@cafca cafca left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had two wishes for docstrings and added some myself, otherwise it's all good!

@adzialocha adzialocha merged commit ab8d186 into development Jun 27, 2022
adzialocha added a commit that referenced this pull request Jun 27, 2022
* main:
  Update README.md
  UPSERT documents (#173)
  Run tests against PostgreSQL, fix compatibility (#170)
  End-to-end publishEntry tests (#167)
  Reschedule pending tasks during startup (#168)
adzialocha added a commit that referenced this pull request Jun 27, 2022
* main:
  Update README.md
  UPSERT documents (#173)
  Run tests against PostgreSQL, fix compatibility (#170)
  End-to-end publishEntry tests (#167)
adzialocha added a commit that referenced this pull request Jun 27, 2022
* main:
  Update README.md
  UPSERT documents (#173)
  Run tests against PostgreSQL, fix compatibility (#170)
  End-to-end publishEntry tests (#167)
@adzialocha adzialocha deleted the postgres-ci branch June 29, 2022 14:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

CI: Tests should also run against PostgreSQL
3 participants