testcontainers / testcontainers-java

Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.
https://testcontainers.org
MIT License
7.97k stars 1.64k forks source link

[Enhancement]: ScriptUtils::runInitScript should load script also from local directory #7257

Open lukaseder opened 1 year ago

lukaseder commented 1 year ago

Module

Core

Proposal

The ScriptUtils::runInitScript only accepts paths from the classpath. This isn't as powerful as I would have expected given:

Additionally, there are many cases where the script doesn't have to be a file. An inline script in String form would suffice, e.g.

container.initScript("""
  create schema test;
  create table test.test (test int);
""");
eddumelendez commented 1 year ago

Hi @lukaseder, thanks for raising the issue.

Currently, it is recommended to use Flyway or Liquibase for database initialization. Also, most of the database images provide a directory to initialize the database. So, withCopyToContainer and withCopyFileToContainer can be used along with Transferable.of, MountableFile.forClasspathResource and MountableFile.forHostPath. I wonder if those would help to fix your issue.

Additionally, there are many cases where the script doesn't have to be a file. An inline script in String form would suffice

Testcontainers provides Transferable in order to achieve your need.

String schema = """
    create schema test;
    create table test.test (test int);
    """;
...
withCopyToContainer(Transferable.of(schema), "/docker-entrypoint-initdb.d/schema.sql")

See mysql and postgres

ScriptUtils.runInitScript also has some limitations regarding sql syntax. That's the reason why our recommendation on relying to existing mechanism.

lukaseder commented 1 year ago

Currently, it is recommended to use Flyway or Liquibase for database initialization.

Ironically, I was looking into using testcontainers here precisely because of Flyway breaking tons of things in their upgrades, and for the use-case I had, I didn't want to debug through those breakages. The use-case is a simple example that just runs a single script containing 1 table, where Flyway (or Liquibase) would be total overkill.

I resorted to using sql-maven-plugin, because it does the job during the build.

I understand that this init script utility isn't a top priority, because it will always be a very limited tool. Though, being able to load a file with absolute (or relative) path rather than a file from the classpath seems to be relatively simple? A lot of build tools (including Flyway and Liquibase) support both classpaths and actual paths for such things. I think this looks like a low hanging fruit. If the 2 existing Resource lookups fail, just try checking if the path resolves to an actual java.io.File with File::exists, and if so, load that.

Also, most of the database images provide a directory to initialize the database. So, withCopyToContainer and withCopyFileToContainer can be used along with Transferable.of, MountableFile.forClasspathResource and MountableFile.forHostPath. I wonder if those would help to fix your issue.

Appreciate the workarounds, thanks. This will be helpful for others, I'm sure. In my case, I just wanted to avoid complexity given the example is so super simple. I removed Flyway because of complexity (that wasn't essential to the example). I'm sure a lot of users will find the init script very useful for exactly this purpose: Avoiding complexity.