Search code examples
javaoracle-databasespring-bootautomated-teststestcontainers

testcontainers oracle database container starts before database user is created


I want to have automated tests for testing import commands from datasource A (oracle) to datasource B (postgres). Both datasources should be created with testcontainers.

The basic setup is done but the problem is:

testcontainers oracleContainer starts before the entrypoint defined in the Dokerfile has a change to finish up and create the user needed for the tests.

After the container is running, some sql scripts are executed creating tables and filling them with data. Those scripts fail because the user mentioned in the script is not created yet.

testcontainers is not waiting for the complete setup.


I've setup the container like this:

        OracleContainer oracleContainer = new OracleContainer("webdizz/oracle-xe-11g-sa:latest")
            .withStartupTimeoutSeconds(10000)
            .withEnv("DATABASES", "xyz");

The database is definitely created, but after a rather long time (tested by running the test forever and checking the database with docker exec)

I tried to set a wait strategy for the container multiple ways, for example like this:

        oracleContainer.setWaitStrategy(new LogMessageWaitStrategy().withRegEx("*.Enjoy!*"));

but it didn't result in anything.


By checking the logs with:

Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER);
    oracleContainer.followOutput(logConsumer);

I can see that testcontainers never waits for it to finish.

All I found online regarding this "bug" or "problem" is this BugReport on github: https://github.com/testcontainers/testcontainers-java/issues/1292

and it seems like the user is even using the same container as me. However the solution mentioned in the issue didn't work for me.


Solution

  • I solved this problem by creating a script (actually two, but can be put into one) that checks if the database user "XYZ" exists and returns 0 if it does.

    wait_check.sh:

    #!/usr/bin/env bash
    while :
    do
        echo "waiting for user to exist"
        if $(/bin/bash /check_db_user.sh | grep -q XYZ); then
            echo "user XYZ exists"
            exit 0
        fi
    done
    

    and check_db_user.sh:

    #!/usr/bin/env bash
    sqlplus -s /nolog <<EOF
    connect username/password
    select username from dba_users where username = 'XYZ';
    quit
    EOF
    

    and then I run the wait_check.sh with:

    oracleContainer.copyFileToContainer(
            MountableFile.forClasspathResource("/helper/wait_check.sh"),
            "/wait_check.sh");
    oracleContainer.copyFileToContainer(
            MountableFile.forClasspathResource("/helper/check_db_user.sh"),
            "/check_db_user.sh");
    
    try {
        Container.ExecResult result = oracleContainer.execInContainer("./wait_check.sh");
        log.debug(result.getStdout());
    } catch (IOException | InterruptedException e) {
        log.error(e.getMessage());
    }
    

    This works because oracleContainer.execInContainer doesn't time out on default. It just waits until whatever you do finishes. I discovered this while debugging and the solution works for me.