mirror of
https://github.com/jlengrand/supabase-testcontainers-kotlin.git
synced 2026-03-10 00:31:18 +00:00
Working
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -39,4 +39,6 @@ bin/
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
|
||||
src/test/resources/supabase
|
||||
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
|
||||
7
.idea/sqldialects.xml
generated
Normal file
7
.idea/sqldialects.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="file://$PROJECT_DIR$/src/test/kotlin/MainKtTest.kt" dialect="GenericSQL" />
|
||||
<file url="PROJECT" dialect="PostgreSQL" />
|
||||
</component>
|
||||
</project>
|
||||
70
README.md
Normal file
70
README.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Supabase Test Container for Kotlin
|
||||
|
||||
This repo demonstrates the use of [Test Containers](https://testcontainers.com/) against a full local [Supabase](https://supabase.com/) instance
|
||||
|
||||
Supabase allows for self-hosting (see [here](https://supabase.com/docs/guides/self-hosting/docker)). We use that to run a local instance of Supabase for testing.
|
||||
|
||||
|
||||
## Manual steps (for now) - do it once
|
||||
|
||||
* You need to have the Supabase repo available locally to play around
|
||||
|
||||
```bash
|
||||
$ git clone --depth 1 https://github.com/supabase/supabase src/test/resources/supabase
|
||||
$ cp src/test/resources/supabase/docker/.env.example src/test/resources/supabase/docker/.env
|
||||
```
|
||||
|
||||
_Note : I've added `src/test/resources/supabase` to the `.gitignore` file, so that it does not get pushed to the repo._
|
||||
|
||||
### Container names fun stuff
|
||||
|
||||
Interestingly, Test Containers don't support the `container_name` property in the `docker-compose.yml` file, [and don't seem to intend to either](https://github.com/testcontainers/testcontainers-java/pull/2741).
|
||||
But it also doesn't get ignored meaning that if you run the tests now, you'll get the following error:
|
||||
|
||||
```
|
||||
java.lang.IllegalStateException: Compose file supabase-testcontainers-kotlin/src/test/resources/supabase/docker/docker-compose.yml has 'container_name' property set for service 'studio' but this property is not supported by Testcontainers, consider removing it
|
||||
at org.testcontainers.containers.ParsedDockerComposeFile.validateNoContainerNameSpecified(ParsedDockerComposeFile.java:113)
|
||||
```
|
||||
|
||||
To avoid that, we'll remove the `container_name` properties from the `docker-compose.yml` file. Do note that it might have an impact if you want to run the containers manually though.
|
||||
|
||||
```bash
|
||||
$ grep -v "container_name" docker/docker-compose.yml > tmpfile && mv tmpfile docker/docker-compose.yml
|
||||
```
|
||||
|
||||
## Updating the Supabase repo to the latest version
|
||||
|
||||
Cloning the repo is great, but we also need to stay in think with sync with the latest version of Supabase.
|
||||
Once in a while, you'll need to update the repo.
|
||||
|
||||
```bash
|
||||
$ cd src/test/resources/supabase
|
||||
$ git pull
|
||||
```
|
||||
|
||||
_Note : Remember, we modified `docker-compose.yml` to remove the `container_name` property. This might lead to conflict at times if the file gets modified_
|
||||
|
||||
## Running the tests
|
||||
|
||||
For now, we'll avoid the creation of database and more, and simply run against existing tables of the public database and see what's up.
|
||||
|
||||
Run the test in `MainTest.kt`.
|
||||
Note that the rests are quite long to run (obviously, I'd say), and that the Supabase compose file is setup so that volumes are saved in between runs.
|
||||
|
||||
```
|
||||
$ ./gradlew test ✔ ╱ 17:38:27
|
||||
|
||||
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
|
||||
|
||||
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
|
||||
|
||||
For more on this, please refer to https://docs.gradle.org/8.2/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
|
||||
|
||||
BUILD SUCCESSFUL in 53s
|
||||
4 actionable tasks: 3 executed, 1 up-to-date
|
||||
|
||||
```
|
||||
|
||||
## Author
|
||||
|
||||
* Julien Lengrand-Lambert (@jlengrand)
|
||||
@@ -33,6 +33,8 @@ dependencies {
|
||||
|
||||
testImplementation("io.mockk:mockk:1.13.7")
|
||||
testImplementation("io.ktor:ktor-client-mock:2.3.5")
|
||||
|
||||
testImplementation("io.github.cdimascio:dotenv-kotlin:6.4.1")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
|
||||
@@ -25,18 +25,7 @@ data class ResultPerson (
|
||||
}
|
||||
|
||||
fun main() {
|
||||
println("Hello World!")
|
||||
// Application goes here
|
||||
val supabaseClient = createSupabaseClient(
|
||||
supabaseUrl = "",
|
||||
supabaseKey = ""
|
||||
) {
|
||||
install(Postgrest)
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
savePerson(listOf(Person("Jan", 30), Person("Jane", 42)), supabaseClient)
|
||||
}
|
||||
// Your application logic
|
||||
}
|
||||
|
||||
suspend fun getPerson(client: SupabaseClient): List<ResultPerson> {
|
||||
|
||||
@@ -1,42 +1,22 @@
|
||||
import io.github.cdimascio.dotenv.dotenv
|
||||
import io.github.jan.supabase.SupabaseClient
|
||||
import io.github.jan.supabase.createSupabaseClient
|
||||
import io.github.jan.supabase.postgrest.Postgrest
|
||||
import io.github.jan.supabase.postgrest.postgrest
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.testcontainers.containers.ComposeContainer
|
||||
import org.testcontainers.junit.jupiter.Container
|
||||
import org.testcontainers.junit.jupiter.Testcontainers
|
||||
import java.io.File
|
||||
import java.sql.DriverManager
|
||||
|
||||
|
||||
@Testcontainers
|
||||
class MainKtTestTestContainers {
|
||||
|
||||
private val jwtToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q"
|
||||
|
||||
private lateinit var supabaseClient: SupabaseClient
|
||||
|
||||
@Container
|
||||
var environment: ComposeContainer =
|
||||
ComposeContainer(File("src/test/resources/supabase/docker/docker-compose.yml"))
|
||||
.withExposedService("kong", 8000)
|
||||
.withExposedService("analytics", 4000)
|
||||
.withExposedService("db", 5432)
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
val fakeSupabaseUrl = environment.getServiceHost("kong", 8000) +
|
||||
":" + environment.getServicePort("kong", 8000)
|
||||
|
||||
supabaseClient = createSupabaseClient(
|
||||
supabaseUrl = "http://$fakeSupabaseUrl",
|
||||
supabaseKey = jwtToken
|
||||
) {
|
||||
install(Postgrest)
|
||||
}
|
||||
}
|
||||
class MainKtTest {
|
||||
|
||||
@Test
|
||||
fun testEmptyPersonTable(){
|
||||
@@ -60,4 +40,83 @@ class MainKtTestTestContainers {
|
||||
assertEquals(randomPersons, fetchResult.map { it.toPerson() })
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val DOCKER_COMPOSE_FILE = "src/test/resources/supabase/docker/docker-compose.yml"
|
||||
private const val ENV_LOCATION = "src/test/resources/supabase/docker/.env" // We grab the JWT token from here
|
||||
|
||||
val dotenv = dotenv{
|
||||
directory = File(ENV_LOCATION).toString()
|
||||
}
|
||||
|
||||
private val jwtToken = dotenv["SERVICE_ROLE_KEY"]
|
||||
private val dbPassword = dotenv["POSTGRES_PASSWORD"]
|
||||
private val db = dotenv["POSTGRES_DB"]
|
||||
|
||||
private lateinit var supabaseClient: SupabaseClient
|
||||
|
||||
@Container
|
||||
var container: ComposeContainer = ComposeContainer(File(DOCKER_COMPOSE_FILE))
|
||||
.withExposedService("kong", 8000)
|
||||
.withExposedService("db", 5432) // Handy but not required
|
||||
|
||||
@JvmStatic
|
||||
@AfterAll
|
||||
fun tearDown() {
|
||||
val dbUrl = container.getServiceHost("db", 5432) + ":" + container.getServicePort("db", 5432)
|
||||
|
||||
val jdbcUrl = "jdbc:postgresql://$dbUrl/$db"
|
||||
val connection = DriverManager.getConnection(jdbcUrl, "postgres", dbPassword)
|
||||
|
||||
try {
|
||||
val query = connection.prepareStatement(
|
||||
"""
|
||||
drop table public.person;
|
||||
"""
|
||||
)
|
||||
|
||||
query.executeQuery()
|
||||
} catch (ex: Exception) {
|
||||
println(ex)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@BeforeAll
|
||||
fun setUp() {
|
||||
val supabaseUrl = container.getServiceHost("kong", 8000) + ":" + container.getServicePort("kong", 8000)
|
||||
val dbUrl = container.getServiceHost("db", 5432) + ":" + container.getServicePort("db", 5432)
|
||||
|
||||
supabaseClient = createSupabaseClient(
|
||||
supabaseUrl = "http://$supabaseUrl",
|
||||
supabaseKey = jwtToken
|
||||
) {
|
||||
install(Postgrest)
|
||||
}
|
||||
|
||||
val jdbcUrl = "jdbc:postgresql://$dbUrl/$db"
|
||||
val connection = DriverManager.getConnection(jdbcUrl, "postgres", dbPassword)
|
||||
|
||||
|
||||
try {
|
||||
val query = connection.prepareStatement(
|
||||
"""
|
||||
create table
|
||||
public.person (
|
||||
id bigint generated by default as identity not null,
|
||||
timestamp timestamp with time zone null default now(),
|
||||
name character varying null,
|
||||
age bigint null
|
||||
) tablespace pg_default;
|
||||
"""
|
||||
)
|
||||
|
||||
query.executeQuery()
|
||||
} catch (ex: Exception) {
|
||||
println("Error is fine here. This should actually run only once")
|
||||
println(ex) // Might be fine, this should actually run only once
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/test/resources/initdb/person.sql
Normal file
7
src/test/resources/initdb/person.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
create table
|
||||
public.person (
|
||||
id bigint generated by default as identity not null,
|
||||
timestamp timestamp with time zone null default now(),
|
||||
name character varying null,
|
||||
age bigint null
|
||||
) tablespace pg_default;
|
||||
Reference in New Issue
Block a user