0

I am trying to connect to Postgres database instance of TestContainers but I get the error org.postgresql.util.PSQLException: ERROR: relation "proposal" does not exist in Spring data repository class "ProposalRepository" when running the "HelloRestControllerTest"

The flyway is enabled in the config.

I created a demo project with just the basic stuff.

HelloRestController:

import com.example.demo.entity.Proposal
import com.example.demo.repo.ProposalRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class HelloRestController @Autowired constructor(val proposalRepository: ProposalRepository) {

    @GetMapping("/hello")
    fun hello(): ResponseEntity<Proposal>{
        val result = proposalRepository.findByIdOrNull("123") //error is thrown here.
        return ResponseEntity.ok(result)
    }
}

ProposalRepository:

import com.example.demo.entity.Proposal
import org.springframework.data.repository.CrudRepository

interface ProposalRepository : CrudRepository<Proposal, String>

Entity:

import java.math.BigDecimal
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.Table
import javax.persistence.Id

@Entity
@Table(name = "proposal")
data class Proposal(

  @Id
  @Column(name = "proposal_id")
  val proposalId: String,

  @Column(name = "amount", nullable = true)
  val amount: BigDecimal?,

  @Column(name = "user_id")
  val userId: String? = null
)

Flyway SQL script:

create table proposal (
      proposal_id                           varchar(50),
      amount                                decimal not null ,
      user_id                               varchar(35)
);

application-it-test.yml in src/test/resources folder:

spring:
  datasource:
    #driver-class-name: org.postgresql.Driver
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:tc:postgresql:11.6:///databasename
    hikari:
      max-lifetime: 500000
      connection-timeout: 300000
      idle-timeout: 600000
      maximum-pool-size: 5
      minimum-idle: 1
  flyway:
    enabled: true
    url: ${spring.datasource.url}
    locations: 'classpath:db/migration/postgresql'
    table: FLY_VERSION
  jpa:
    show-sql: true
    database-platform: org.hibernate.dialect.PostgreSQLDialect

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
        <kotlin.version>1.6.21</kotlin.version>
        <spring-cloud.version>2021.0.3</spring-cloud.version>
        <testcontainers.version>1.17.3</testcontainers.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.module</groupId>
            <artifactId>jackson-module-kotlin</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-reflect</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk8</artifactId>
        </dependency>

        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>json-path</artifactId>
            <scope>test</scope>
            <exclusions>
                <!-- excluded because REST assured 4.3.0+ expects Groovy 3+ (https://github.com/rest-assured/rest-assured/issues/1283) -->
                <exclusion>
                    <groupId>org.codehaus.groovy</groupId>
                    <artifactId>groovy-json</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <scope>test</scope>
            <exclusions>
                <!-- excluded because REST assured 4.3.0+ expects Groovy 3+ (https://github.com/rest-assured/rest-assured/issues/1283) -->
                <exclusion>
                    <groupId>org.codehaus.groovy</groupId>
                    <artifactId>groovy</artifactId>
                </exclusion>
                <!-- excluded because REST assured 4.3.0+ expects Groovy 3+ (https://github.com/rest-assured/rest-assured/issues/1283) -->
                <exclusion>
                    <groupId>org.codehaus.groovy</groupId>
                    <artifactId>groovy-xml</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>postgresql</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.testcontainers</groupId>
                <artifactId>testcontainers-bom</artifactId>
                <version>${testcontainers.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
        <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <configuration>
                    <args>
                        <arg>-Xjsr305=strict</arg>
                    </args>
                    <compilerPlugins>
                        <plugin>spring</plugin>
                        <plugin>jpa</plugin>
                    </compilerPlugins>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-maven-allopen</artifactId>
                        <version>${kotlin.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-maven-noarg</artifactId>
                        <version>${kotlin.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

HelloRestControllerTest:

import io.restassured.RestAssured.given
import io.restassured.http.ContentType
import org.junit.jupiter.api.Test
import org.junit.runner.RunWith
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.springframework.test.context.junit4.SpringRunner
import org.testcontainers.containers.PostgreSQLContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("it-test")
@AutoConfigureWireMock(port = 1234)
@RunWith(SpringRunner::class)
@Testcontainers
class HelloRestControllerTest {
    @LocalServerPort
    final val portNumber = 0

    val baseUrl = "http://localhost:$portNumber"


    companion object {
        @Container
        var postgreSQL: PostgreSQLContainer<*> = PostgreSQLContainer("postgres:11.6")

        @DynamicPropertySource
        fun postgreSQLProperties(registry: DynamicPropertyRegistry) {
            registry.add("spring.datasource.username") { postgreSQL.username }
            registry.add("spring.datasource.password") { postgreSQL.password }
        }
    }

    @Test
    fun test() {
        val helloResponse = given()
            .contentType(ContentType.JSON)
            .get("$baseUrl$portNumber/hello")
            .andReturn()
        println(helloResponse.body)
    }
}

The code and configuration looks fine so I am not able to figure out what is the problem. What could be the issue here?


EDIT:

If I add @Sql annotation in HelloRestControllerTest as shown below, I do not see any error. It looks like Flyway is not able to run the scripts to the right database or something. Not sure why?

@Sql(scripts = ["classpath:/db/migration/postgresql/V01__PROPOSAL.sql"])

firstpostcommenter
  • 2,328
  • 4
  • 30
  • 59
  • One suspicion here, because it struck me in the past several times: I cannot find the database name in any of your code / configuration. e.g. in `postgreSQLProperties` or `datasource` configuration. – DrGlitch Sep 05 '22 at 07:30
  • The `testcontainers` use the jdbc url `jdbc:tc:postgresql:11.6:///databasename`. https://www.testcontainers.org/modules/databases/jdbc/#using-postgresql. Not sure if I need to mention some custom database name. The hostname, port and database name will be ignored by testcontainer. – firstpostcommenter Sep 05 '22 at 07:51
  • https://stackoverflow.com/questions/67912066/spring-boot-test-using-testcontainers-postgresql – firstpostcommenter Sep 05 '22 at 10:27
  • https://www.baeldung.com/spring-boot-testcontainers-integration-test, docker system prune -af – firstpostcommenter Sep 05 '22 at 11:28
  • Looks like Flyway is having some issue because `@Sql` annotation is working fine with `Testcontainers` postgres database. Not sure how to fix this – firstpostcommenter Sep 05 '22 at 12:02
  • https://stackoverflow.com/questions/45873514/postgresql-hint-you-will-need-to-rewrite-or-cast-the-expression-column-state – firstpostcommenter Sep 05 '22 at 12:42

1 Answers1

0

change the application-it-test.yml as shown below:

spring:
  datasource:
    #driver-class-name: org.postgresql.Driver
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:tc:postgresql:11.6:///databasename?stringtype=unspecified
    hikari:
      max-lifetime: 500000
      connection-timeout: 300000
      idle-timeout: 600000
      maximum-pool-size: 5
      minimum-idle: 1
  flyway:
    enabled: true
    locations: 'classpath:db/migration/postgresql'
  jpa:
    show-sql: true
    database-platform: org.hibernate.dialect.PostgreSQLDialect

under flyway variable in yml file you only need to mention the enable and locations values. Everything else like url, schemas, table, etc should be removed. Otherwise Flyway does not work together with TestContainers in integration test.

The change needs to be done in both the test yml file and real application's yml file as well.

firstpostcommenter
  • 2,328
  • 4
  • 30
  • 59
  • For Spring Boot (Before version 2.3.0) you need to specify the driver manually spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver – firstpostcommenter Sep 05 '22 at 13:04