diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml index 35022882..531b8096 100644 --- a/.github/workflows/backend-ci.yml +++ b/.github/workflows/backend-ci.yml @@ -40,11 +40,11 @@ jobs: postgres: image: postgres:latest env: - POSTGRES_DB: kryptokrona_api_dev + POSTGRES_DB: kryptokrona_api_test POSTGRES_USER: postgres POSTGRES_PASSWORD: test1234 ports: - - 5432:5432 + - 5433:5432 options: >- --health-cmd pg_isready --health-interval 10s @@ -61,7 +61,7 @@ jobs: cache: 'gradle' - name: Run Liquibase Migrations - run: ./gradlew update -PdbEnv=dev + run: ./gradlew update -PdbEnv=test - name: Run Unit Test run: ./gradlew test diff --git a/.github/workflows/backend-pr-ci.yml b/.github/workflows/backend-pr-ci.yml index dacaff1b..4e3e16fd 100644 --- a/.github/workflows/backend-pr-ci.yml +++ b/.github/workflows/backend-pr-ci.yml @@ -7,43 +7,34 @@ on: jobs: ansible-syntax-check: name: Ansible Syntax Check - # The type of runner that the job will run on runs-on: ubuntu-latest - # Steps represent a sequence of tasks that will be executed as part of the job steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v3 - # Install python3 pip and ansible latest - name: Install python and ansible run: sudo apt update && sudo apt install -y python3-pip && sudo pip3 install ansible - # If `roles` folder exists, create a playbook_repo.yml playbook to perform the syntax check - name: Creating playbook_repo.yml from roles folder (if exists) run: test -d "kryptokrona-api/ansible/roles" || exit 0 && echo $LINE1 > playbook_repo.yml && echo $LINE2 >> playbook_repo.yml && printf " roles:\n" >> playbook_repo.yml && for i in `ls kryptokrona-api/ansible/roles`; do echo " - $i" >> playbook_repo.yml; done env: LINE1: "---" LINE2: "- hosts: localhost" - # Check ansible syntax if roles folder exists (repo with playbooks and roles) - name: Ansible Syntax Check of all roles of the playbook run: test -f "playbook_repo.yml" || exit 0 && ansible-playbook $GITHUB_WORKSPACE/playbook_repo.yml --syntax-check env: ANSIBLE_ACTION_WARNINGS: False - # If `roles` folder does not exist, assume we are in a repository containing just the role - name: Creating role_repo.yml from roles folder (if exists) run: test -d "tasks" || exit 0 && echo $LINE1 > /tmp/role_repo.yml && echo $LINE2 >> /tmp/role_repo.yml && printf " roles:\n" >> /tmp/role_repo.yml && printf " - `echo $GITHUB_REPOSITORY | tr "/" "\n" | tail -1`\n" >> /tmp/role_repo.yml env: LINE1: "---" LINE2: "- hosts: localhost" - # Copy the repo content for syntax check - name: Copy the repo content to /tmp/ for syntax check run: test -f "/tmp/role_repo.yml" || exit 0 && mkdir -p /tmp/roles/ && cd .. && cp -a `echo $GITHUB_REPOSITORY | tr "/" "\n" | tail -1` /tmp/roles/ - # Check ansible syntax if tasks folder exists (repo with just the role) - name: Ansible Syntax Check of role run: test -f "/tmp/role_repo.yml" || exit 0 && ANSIBLE_ROLES_PATH=/tmp/roles ansible-playbook /tmp/role_repo.yml --syntax-check env: @@ -81,11 +72,11 @@ jobs: postgres: image: postgres:latest env: - POSTGRES_DB: kryptokrona_api_dev + POSTGRES_DB: kryptokrona_api_test POSTGRES_USER: postgres POSTGRES_PASSWORD: test1234 ports: - - 5432:5432 + - 5433:5432 options: >- --health-cmd pg_isready --health-interval 10s @@ -102,7 +93,7 @@ jobs: cache: 'gradle' - name: Run Liquibase Migrations - run: ./gradlew update -PdbEnv=dev + run: ./gradlew update -PdbEnv=test - name: Run Unit Test run: ./gradlew test diff --git a/.run/migrate.run.xml b/.run/migrate-dev.run.xml similarity index 54% rename from .run/migrate.run.xml rename to .run/migrate-dev.run.xml index 699f745a..2a4a797d 100644 --- a/.run/migrate.run.xml +++ b/.run/migrate-dev.run.xml @@ -1,5 +1,5 @@ - + true true + + + + false diff --git a/.run/migrate-test.run.xml b/.run/migrate-test.run.xml new file mode 100644 index 00000000..8f60ee7b --- /dev/null +++ b/.run/migrate-test.run.xml @@ -0,0 +1,35 @@ + + + + + + + true + true + + + + + false + + + \ No newline at end of file diff --git a/.run/tests.run.xml b/.run/tests.run.xml index b9833019..416d6139 100644 --- a/.run/tests.run.xml +++ b/.run/tests.run.xml @@ -13,10 +13,22 @@ - diff --git a/build.gradle.kts b/build.gradle.kts index 084f9194..5cd7050f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -154,6 +154,21 @@ liquibase { ) } + activities.register("test") { + val url = properties.getProperty("liquibase.test.url") ?: System.getenv("LIQUIBASE_TEST_URL") + val user = properties.getProperty("liquibase.test.user") ?: System.getenv("LIQUIBASE_TEST_USER") + val password = properties.getProperty("liquibase.test.password") ?: System.getenv("LIQUIBASE_TEST_PASSWORD") + + this.arguments = mapOf( + "logLevel" to "info", + "changeLogFile" to "src/main/resources/db/changelog/master.xml", + "url" to url, + "username" to user, + "password" to password, + "classpath" to "src/main/resources/" + ) + } + activities.register("prod") { val url = System.getenv("LIQUIBASE_URL") val user = System.getenv("LIQUIBASE_USER") diff --git a/docker-compose.yml b/docker-compose.yml index 04a0ad55..6d623c1a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,6 +16,21 @@ services: networks: - kryptokrona_api_net + db-test: + image: postgres + container_name: kryptokrona_api_db_test + restart: always + environment: + POSTGRES_DB: kryptokrona_api_test + POSTGRES_USER: postgres + POSTGRES_PASSWORD: test1234 + volumes: + - db:/data/postgres + ports: + - "5433:5432" + networks: + - kryptokrona_api_net + liquibase: image: liquibase/liquibase container_name: kryptokrona_api_liquibase diff --git a/local.properties b/local.properties index 9b8ddbf4..686a52a3 100644 --- a/local.properties +++ b/local.properties @@ -1,3 +1,7 @@ liquibase.dev.url=jdbc:postgresql://localhost:5432/kryptokrona_api_dev liquibase.dev.user=postgres -liquibase.dev.password=test1234 \ No newline at end of file +liquibase.dev.password=test1234 + +liquibase.test.url=jdbc:postgresql://localhost:5433/kryptokrona_api_test +liquibase.test.user=postgres +liquibase.test.password=test1234 \ No newline at end of file diff --git a/src/main/kotlin/org/kryptokrona/api/models/Block.kt b/src/main/kotlin/org/kryptokrona/api/models/Block.kt index a8933ac8..93edd1c4 100644 --- a/src/main/kotlin/org/kryptokrona/api/models/Block.kt +++ b/src/main/kotlin/org/kryptokrona/api/models/Block.kt @@ -40,7 +40,7 @@ import java.time.LocalDateTime interface Block : Entity { companion object : Entity.Factory() - val id: Long + var id: Long var hash: String var time: Long var difficulty: Float diff --git a/src/main/kotlin/org/kryptokrona/api/syncers/HuginSyncer.kt b/src/main/kotlin/org/kryptokrona/api/syncers/HuginSyncer.kt index 951ada21..248d6e5b 100644 --- a/src/main/kotlin/org/kryptokrona/api/syncers/HuginSyncer.kt +++ b/src/main/kotlin/org/kryptokrona/api/syncers/HuginSyncer.kt @@ -30,6 +30,7 @@ package org.kryptokrona.api.syncers +import io.ktor.client.plugins.* import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.isActive @@ -74,25 +75,30 @@ class HuginSyncer { launch { while(isActive) { logger.debug("Syncing Hugin...") - val retrievedData = poolChangesClient.getPoolChangesLite() - val transactions = retrievedData?.addedTxs - - transactions?.let { - for (tx in it) { - val extra = tx.transactionPrefix.extra - val transactionHash = tx.transactionHash - - if (extra.length > 200 && transactionHash !in knownPoolTxsList) { - logger.debug("Incoming transaction $transactionHash") - knownPoolTxsList += transactionHash - val extraData = trimExtra(extra) - - if ("box" in extraData) savePostEncrypted(extraData, tx) - else if ("sb" in extraData) savePostEncryptedGroup(extraData, tx) - } else { - logger.debug("Extra is less than 200 in length, skipping...") + + try { + val retrievedData = poolChangesClient.getPoolChangesLite() + val transactions = retrievedData?.addedTxs + + transactions?.let { + for (tx in it) { + val extra = tx.transactionPrefix.extra + val transactionHash = tx.transactionHash + + if (extra.length > 200 && transactionHash !in knownPoolTxsList) { + logger.debug("Incoming transaction $transactionHash") + knownPoolTxsList += transactionHash + val extraData = trimExtra(extra) + + if ("box" in extraData) savePostEncrypted(extraData, tx) + else if ("sb" in extraData) savePostEncryptedGroup(extraData, tx) + } else { + logger.debug("Extra is less than 200 in length, skipping...") + } } } + } catch (e: HttpRequestTimeoutException) { + logger.error("Error syncing Hugin: ${e.message}") } delay(HuginConfig.SYNC_INTERVAL) diff --git a/src/test/kotlin/org/kryptokrona/api/routes/BlockRoutesTest.kt b/src/test/kotlin/org/kryptokrona/api/routes/BlockRoutesTest.kt index f2e5c31c..eeb0c986 100644 --- a/src/test/kotlin/org/kryptokrona/api/routes/BlockRoutesTest.kt +++ b/src/test/kotlin/org/kryptokrona/api/routes/BlockRoutesTest.kt @@ -1,18 +1,63 @@ package org.kryptokrona.api.routes; +import io.ktor.client.call.* import io.ktor.client.request.* import io.ktor.http.* import io.ktor.server.testing.* +import org.junit.Before +import org.kryptokrona.api.models.Block +import org.kryptokrona.api.services.block.BlockServiceImpl +import org.kryptokrona.api.utils.jsonObjectMapper +import org.kryptokrona.sdk.http.model.block.Blocks +import org.ktorm.entity.Entity +import org.slf4j.LoggerFactory +import java.time.LocalDateTime +import java.time.LocalDateTime.now +import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertTrue class BlockRoutesTest { + private val logger = LoggerFactory.getLogger("BlockRoutesTest") + + private val blockService: BlockServiceImpl = BlockServiceImpl() + + private val baseEndpoint: String = "/api/v1/blocks" + + @Before + fun beforeEach() = testApplication { + val block = Block { + id = 1 + hash = "hash" + time = 10000 + difficulty = 1.0F + reward = 1.0F + createdAt = now() + } + blockService.getById(block.id).let { + if (it == null) { + blockService.save(block) + logger.info("Block with id: ${block.id} saved") + } else { + logger.info("Block with id: ${it.id} already exists") + } + } + } + @Test - fun blockRouteTest() = testApplication { - client.get("/api/v1/blocks").apply { + fun `can get all blocks`() = testApplication { + client.get(baseEndpoint).apply { assertEquals(HttpStatusCode.OK, status) } } + @Test + fun `can get a block by id`() = testApplication { + val blockId = 1 + val response = client.get("$baseEndpoint/$blockId").body() + + assertTrue { blockId.toLong() == response.id } + } } \ No newline at end of file diff --git a/src/test/kotlin/org/kryptokrona/api/routes/HashrateRoutesTest.kt b/src/test/kotlin/org/kryptokrona/api/routes/HashrateRoutesTest.kt new file mode 100644 index 00000000..66e392a2 --- /dev/null +++ b/src/test/kotlin/org/kryptokrona/api/routes/HashrateRoutesTest.kt @@ -0,0 +1,18 @@ +package org.kryptokrona.api.routes + +import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.server.testing.* +import org.junit.Test +import kotlin.test.assertEquals + +class HashrateRoutesTest { + + @Test + fun `can get all hashrates`() = testApplication { + client.get("/api/v1/hashrates").apply { + assertEquals(HttpStatusCode.OK, status) + } + } + +} \ No newline at end of file diff --git a/src/test/kotlin/org/kryptokrona/api/routes/InfoRoutesTest.kt b/src/test/kotlin/org/kryptokrona/api/routes/InfoRoutesTest.kt new file mode 100644 index 00000000..ce96ff20 --- /dev/null +++ b/src/test/kotlin/org/kryptokrona/api/routes/InfoRoutesTest.kt @@ -0,0 +1,4 @@ +package org.kryptokrona.api.routes + +class InfoRoutesTest { +} \ No newline at end of file diff --git a/src/test/kotlin/org/kryptokrona/api/routes/NodeRoutesTest.kt b/src/test/kotlin/org/kryptokrona/api/routes/NodeRoutesTest.kt new file mode 100644 index 00000000..4568265a --- /dev/null +++ b/src/test/kotlin/org/kryptokrona/api/routes/NodeRoutesTest.kt @@ -0,0 +1,18 @@ +package org.kryptokrona.api.routes + +import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.server.testing.* +import org.junit.Test +import kotlin.test.assertEquals + +class NodeRoutesTest { + + @Test + fun `can get all nodes`() = testApplication { + client.get("/api/v1/nodes").apply { + assertEquals(HttpStatusCode.OK, status) + } + } + +} \ No newline at end of file diff --git a/src/test/kotlin/org/kryptokrona/api/routes/OutputRoutesTest.kt b/src/test/kotlin/org/kryptokrona/api/routes/OutputRoutesTest.kt new file mode 100644 index 00000000..fafa6fc7 --- /dev/null +++ b/src/test/kotlin/org/kryptokrona/api/routes/OutputRoutesTest.kt @@ -0,0 +1,18 @@ +package org.kryptokrona.api.routes + +import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.server.testing.* +import org.junit.Test +import kotlin.test.assertEquals + +class OutputRoutesTest { + + @Test + fun `can get all outputs`() = testApplication { + client.get("/api/v1/nodes").apply { + assertEquals(HttpStatusCode.OK, status) + } + } + +} \ No newline at end of file diff --git a/src/test/kotlin/org/kryptokrona/api/routes/PoolRoutesTest.kt b/src/test/kotlin/org/kryptokrona/api/routes/PoolRoutesTest.kt new file mode 100644 index 00000000..ee9534df --- /dev/null +++ b/src/test/kotlin/org/kryptokrona/api/routes/PoolRoutesTest.kt @@ -0,0 +1,18 @@ +package org.kryptokrona.api.routes + +import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.server.testing.* +import org.junit.Test +import kotlin.test.assertEquals + +class PoolRoutesTest { + + @Test + fun `can get all pools`() = testApplication { + client.get("/api/v1/nodes").apply { + assertEquals(HttpStatusCode.OK, status) + } + } + +} \ No newline at end of file diff --git a/src/test/kotlin/org/kryptokrona/api/routes/PostEncryptedGroupRoutesTest.kt b/src/test/kotlin/org/kryptokrona/api/routes/PostEncryptedGroupRoutesTest.kt new file mode 100644 index 00000000..26ac9aa2 --- /dev/null +++ b/src/test/kotlin/org/kryptokrona/api/routes/PostEncryptedGroupRoutesTest.kt @@ -0,0 +1,18 @@ +package org.kryptokrona.api.routes + +import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.server.testing.* +import org.junit.Test +import kotlin.test.assertEquals + +class PostEncryptedGroupRoutesTest { + + @Test + fun `can get all encrypted group posts`() = testApplication { + client.get("/api/v1/posts-encrypted-group").apply { + assertEquals(HttpStatusCode.OK, status) + } + } + +} \ No newline at end of file diff --git a/src/test/kotlin/org/kryptokrona/api/routes/PostEncryptedRoutesTest.kt b/src/test/kotlin/org/kryptokrona/api/routes/PostEncryptedRoutesTest.kt new file mode 100644 index 00000000..e19b51a0 --- /dev/null +++ b/src/test/kotlin/org/kryptokrona/api/routes/PostEncryptedRoutesTest.kt @@ -0,0 +1,18 @@ +package org.kryptokrona.api.routes + +import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.server.testing.* +import org.junit.Test +import kotlin.test.assertEquals + +class PostEncryptedRoutesTest { + + @Test + fun `can get all encrypted posts`() = testApplication { + client.get("/api/v1/posts-encrypted").apply { + assertEquals(HttpStatusCode.OK, status) + } + } + +} \ No newline at end of file diff --git a/src/test/kotlin/org/kryptokrona/api/routes/SupplyRoutesTest.kt b/src/test/kotlin/org/kryptokrona/api/routes/SupplyRoutesTest.kt new file mode 100644 index 00000000..d24466b5 --- /dev/null +++ b/src/test/kotlin/org/kryptokrona/api/routes/SupplyRoutesTest.kt @@ -0,0 +1,18 @@ +package org.kryptokrona.api.routes + +import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.server.testing.* +import org.junit.Test +import kotlin.test.assertEquals + +class SupplyRoutesTest { + + @Test + fun `can get all supplies`() = testApplication { + client.get("/api/v1/supplies").apply { + assertEquals(HttpStatusCode.OK, status) + } + } + +} \ No newline at end of file diff --git a/src/test/kotlin/org/kryptokrona/api/routes/TransactionRoutesTest.kt b/src/test/kotlin/org/kryptokrona/api/routes/TransactionRoutesTest.kt new file mode 100644 index 00000000..80194dd1 --- /dev/null +++ b/src/test/kotlin/org/kryptokrona/api/routes/TransactionRoutesTest.kt @@ -0,0 +1,18 @@ +package org.kryptokrona.api.routes + +import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.server.testing.* +import org.junit.Test +import kotlin.test.assertEquals + +class TransactionRoutesTest { + + @Test + fun `can get all transactions`() = testApplication { + client.get("/api/v1/transactions").apply { + assertEquals(HttpStatusCode.OK, status) + } + } + +} \ No newline at end of file diff --git a/src/test/resources/application.conf b/src/test/resources/application.conf new file mode 100644 index 00000000..a60a76f2 --- /dev/null +++ b/src/test/resources/application.conf @@ -0,0 +1,16 @@ +ktor { + application { + modules = [ org.kryptokrona.api.ApplicationKt.module ] + } +} + +db { + url = "jdbc:postgresql://127.0.0.1:5433/kryptokrona_api_test" + url = ${?DB_URL} + user = postgres + user = ${?DB_USER} + password = test1234 + password = ${?DB_PASSWORD} + driver = "org.postgresql.Driver" + driver = ${?DB_DRIVER} +} \ No newline at end of file