testcontainers / testcontainers-rs-modules-community

Community maintained modules for Testcontainers for Rust
https://docs.rs/crate/testcontainers-modules/
MIT License
90 stars 43 forks source link

Rerunning tests causes them to fail because the container is not cleaned up. #208

Closed santiwanti closed 2 months ago

santiwanti commented 2 months ago

I'm using the testcontainers-module Postgres container and all tests pass on the first run, but on the second run they fail because when trying to add the rows, the elements already exist causing problems with UNIQUE constraints. Is it expected behavior that the tests make permanent changes to the environment? I thought the whole point of testcontainers was that each test ran in isolation and the environment would be a clean one for each test.

DDtKey commented 2 months ago

No, this isn't the expected behavior. Each new container starts an independent instance (until any customizations are made - like mounting postgres data or reusing the same container)

because the container is not cleaned up

Even if it's not cleaned up (e.g TESTCONTAINERS_COMMAND=keep) - you should use the connection string for new container. But from the description it sounds like your code is connecting to the existing instance .

Could you share the code that causes the issue? How do you connect to postgres?

santiwanti commented 2 months ago

While moving all the code to a single file, I noticed I was ignoring the get_host_port_ipv4 output. I thought there was no way that would be what would fix it, but I tested it and it did. :see_no_evil: So for anyone that thinks you can ignore the port, as I did, because I thought obviously postgres is port 5432, think again. For some reason not specifying the port meant that every container was the same and all changes persisted through tests.

This works:

    let connection_string = &format!(
        "postgres://postgres:postgres@127.0.0.1:{}/postgres",
        node.get_host_port_ipv4(5432)?
    );

This does not work:

    let connection_string = "postgres://postgres:postgres@127.0.0.1/postgres";
DDtKey commented 2 months ago

For some reason not specifying the port meant that every container was the same and all changes persisted through tests.

Might that be that you have a local postgres instance? (i.e you started a container, but connected to the local one on your machine every time)

Because containers are always mapped to the random port (until customized) and that's why you need to use get_host_port_ipv4. You even may have many postgres containers spawned at the same time and it will work.

santiwanti commented 2 months ago

I don't think it was connecting to the local instance because deleting the postgres image manually would fix the tests and allow them to run properly again. I tried deleting all the images and containers so I only had one container and one image running, but the tests would still fail, due to UNIQUE constraints on the second run, when not specifying the port.

DDtKey commented 2 months ago

When you don't specify a port in connection string, in fact (depending on the client, ofc) all that happens is usually the default (5432) is used. So to understand the reasons - you need to understand what was mapped to this port. (Test-)containers do not share state between themselves unless this is explicitly configured and get_host_port_ipv4 doesn't do any magic here, just tells which port to use for a connection to a particular container

santiwanti commented 2 months ago

As you can probably tell I'm not much of an expert with docker and containers.

I used docker image ls to list all images and deleted them all with docker image rm. When rerunning the tests after that it worked the first time and then failed again until I deleted the image again. After using the port that I got back from get_host_port_ipv4 this problem stopped happening. Checking docker image ls again I only have 1 image which is what I would expect and the tests run properly no matter how many times I run them.

I'm not sure why using the port returned by get_host_port_ipv4 fixed the problem. I hadn't thought the port was the reason precisely because of what you mentioned of 5432 being the default already.

Btw, removing the port again from the connection string causes the issue to happen again.

If you want me to run any tests or try out anything let me know I would be happy to help.

DDtKey commented 2 months ago

docker images ls shows a list of images, not containers. You may have many containers using the same image - it's totally fine. You can check the documentation about image if you wish

When rerunning the tests after that it worked the first time and then failed again until I deleted the image again.

However this parts sounds interesting and weird.

Could you try to check what's bound to 5432 for you? For example:

lsof -i tcp:5432

Also, what you can check is docker container ls before and after running tests. If you don't see any containers after test execution - it means they've been cleaned up. To double check - you can disable cleaning by setting export TESTCONTAINERS_COMMAND=keep before running and then try again. You should see that the containers spawned are mapped to some random ports.

santiwanti commented 2 months ago

I ran lsof -i tcp:5432 and didn't see any results and when checking docker container ls I don't see any containers, but as soon as I run it again without specifying the port the tests fail, but now deleting the image does not fix it. I have checked my local postgres databases and the database doesn't exist there either. So now I have no way to fix it so the tests run when not specifying a port.

DDtKey commented 2 months ago

I'm going to close the issue since using get_host_port_ipv4 solves the issue for you (and it's expected to be used)

However I'm not sure about underlying issues and can't reproduce. Please, feel free to re-open if you want to discuss the issue deeper, but it requires checking your case more deeply (it doesn't happen on 2 of my devices & some servers, so hard to help without allocating more time)