diff --git a/.github/workflows/docker-onpush.yml b/.github/workflows/test-lunchmoney-api.yml similarity index 57% rename from .github/workflows/docker-onpush.yml rename to .github/workflows/test-lunchmoney-api.yml index 61d95f4..f883eab 100644 --- a/.github/workflows/docker-onpush.yml +++ b/.github/workflows/test-lunchmoney-api.yml @@ -1,10 +1,9 @@ -name: Push to DockerHub +name: Test against real Lunchmoney API on: [ push ] env: - DOCKER_USER: ${{secrets.DOCKER_USER}} - DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}} + LUNCHMONEY_TEST_TOKEN: ${{secrets.LUNCHMONEY_TEST_TOKEN}} jobs: - push-to-docker-hub: + test-against-lunchmoney-api: runs-on: ubuntu-latest if: ${{ contains(github.event.head_commit.message, '#test') }} steps: @@ -14,5 +13,5 @@ jobs: distribution: temurin java-version: 11 cache: gradle - - name: Code build - run: ./gradlew build --no-daemon -x test -x check + - name: Test against real Lunchmoney API + run: ./gradlew test --no-daemon diff --git a/README.md b/README.md index 579b4bb..56c84a3 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ repositories { } dependencies { - implementation("io.github.smaugfm:lunchmoney:0.0.2") + implementation("io.github.smaugfm:lunchmoney:1.0.0") } ``` diff --git a/build.gradle.kts b/build.gradle.kts index 93af366..c1de43c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,6 @@ import io.gitlab.arturbosch.detekt.Detekt import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask +import org.gradle.api.tasks.testing.logging.TestLogEvent import org.jetbrains.kotlin.gradle.dsl.KotlinCommonCompilerOptions import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask import org.jlleitschuh.gradle.ktlint.KtlintExtension @@ -17,7 +18,7 @@ plugins { } group = "io.github.smaugfm" -version = "0.0.2" +version = "1.0.0" val isReleaseVersion = !version.toString().endsWith("SNAPSHOT") repositories { @@ -66,6 +67,9 @@ detekt { tasks { test { + testLogging { + events = setOf(TestLogEvent.PASSED, TestLogEvent.FAILED, TestLogEvent.SKIPPED) + } useJUnitPlatform() } withType { diff --git a/detekt-baseline.xml b/detekt-baseline.xml index 66c9e3e..e3e2e21 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -2,11 +2,11 @@ LongParameterList:LunchmoneyApi.kt$LunchmoneyApi$( assetId: Long, typeName: LunchmoneyAssetType? = null, subtypeName: String? = null, name: String? = null, displayName: String? = null, balance: BigDecimal? = null, balanceAsOf: Instant? = null, currency: Currency? = null, institutionName: String? = null, closedOn: LocalDate? = null, excludeTransactions: Boolean? = null ) - LongParameterList:LunchmoneyApi.kt$LunchmoneyApi$( categoryId: Long, name: String? = null, description: String? = null, isIncome: Boolean? = null, excludeFromBudget: Boolean? = null, excludeFromTotals: Boolean? = null, categoryIds: List<Long>? = null, groupId: Long? = null ) + LongParameterList:LunchmoneyApi.kt$LunchmoneyApi$( categoryId: Long, isIncome: Boolean, excludeFromBudget: Boolean, excludeFromTotals: Boolean, name: String? = null, description: String? = null, categoryIds: List<Long>? = null, groupId: Long? = null ) LongParameterList:LunchmoneyApi.kt$LunchmoneyApi$( cryptoAssetId: Long, name: String? = null, displayName: String? = null, institutionName: String? = null, currency: String? = null, balance: BigDecimal? = null ) LongParameterList:LunchmoneyApi.kt$LunchmoneyApi$( date: LocalDate, payee: String, transactions: List<Long>, categoryId: Long? = null, notes: String? = null, tags: List<LunchmoneyTransactionTag>? = null ) - LongParameterList:LunchmoneyApi.kt$LunchmoneyApi$( name: String, description: String? = null, isIncome: Boolean? = null, excludeFromBudget: Boolean? = null, excludeFromTotals: Boolean? = null, categoryIds: List<Long>? = null, groupId: Long? = null ) LongParameterList:LunchmoneyApi.kt$LunchmoneyApi$( name: String, description: String? = null, isIncome: Boolean? = null, excludeFromBudget: Boolean? = null, excludeFromTotals: Boolean? = null, categoryIds: List<Long>? = null, newCategories: List<String>? = null ) + LongParameterList:LunchmoneyApi.kt$LunchmoneyApi$( name: String, isIncome: Boolean, excludeFromBudget: Boolean, excludeFromTotals: Boolean, description: String? = null, categoryIds: List<Long>? = null, groupId: Long? = null ) LongParameterList:LunchmoneyApi.kt$LunchmoneyApi$( name: String, typeName: LunchmoneyAssetType, balance: BigDecimal, subtypeName: String? = null, displayName: String? = null, balanceAsOf: Instant? = null, currency: Currency? = null, institutionName: String? = null, closedOn: LocalDate? = null, excludeTransactions: Boolean? = null ) LongParameterList:LunchmoneyApi.kt$LunchmoneyApi$( tagId: Long? = null, recurringId: Long? = null, plaidAccountId: Long? = null, categoryId: Long? = null, assetId: Long? = null, groupId: Long? = null, isGroup: Boolean? = null, status: LunchmoneyTransactionStatus? = null, offset: Long? = null, limit: Long? = null, startDate: LocalDate? = null, endDate: LocalDate? = null, debitAsNegative: Boolean? = null, pending: Boolean? = null ) LongParameterList:LunchmoneyApi.kt$LunchmoneyApi$( transactions: List<LunchmoneyInsertTransaction>, applyRules: Boolean? = null, skipDuplicates: Boolean? = null, checkForRecurring: Boolean? = null, debitAsNegative: Boolean? = null, skipBalanceUpdate: Boolean? = null ) diff --git a/src/main/kotlin/io/github/smaugfm/lunchmoney/model/LunchmoneyBudget.kt b/src/main/kotlin/io/github/smaugfm/lunchmoney/model/LunchmoneyBudget.kt index 4f0e172..4aba98e 100644 --- a/src/main/kotlin/io/github/smaugfm/lunchmoney/model/LunchmoneyBudget.kt +++ b/src/main/kotlin/io/github/smaugfm/lunchmoney/model/LunchmoneyBudget.kt @@ -13,7 +13,7 @@ data class LunchmoneyBudget( val categoryId: Long? = null, val categoryGroupName: String? = null, val groupId: Long? = null, - val isGroup: Boolean, + val isGroup: Boolean? = null, val isIncome: Boolean, val excludeFromBudget: Boolean, val excludeFromTotals: Boolean, diff --git a/src/main/kotlin/io/github/smaugfm/lunchmoney/model/LunchmoneyBudgetData.kt b/src/main/kotlin/io/github/smaugfm/lunchmoney/model/LunchmoneyBudgetData.kt index 422d41c..4f8bc72 100644 --- a/src/main/kotlin/io/github/smaugfm/lunchmoney/model/LunchmoneyBudgetData.kt +++ b/src/main/kotlin/io/github/smaugfm/lunchmoney/model/LunchmoneyBudgetData.kt @@ -10,8 +10,8 @@ import java.util.Currency @Serializable data class LunchmoneyBudgetData( - val numTransactions: Long, - val spendingToBase: Double, + val numTransactions: Long? = null, + val spendingToBase: Double? = null, val budgetToBase: Double? = null, val budgetAmount: Double? = null, val budgetCurrency: Currency? = null, diff --git a/src/main/kotlin/io/github/smaugfm/lunchmoney/model/LunchmoneyRecurringExpense.kt b/src/main/kotlin/io/github/smaugfm/lunchmoney/model/LunchmoneyRecurringExpense.kt index 7ffc2f5..c99ccfd 100644 --- a/src/main/kotlin/io/github/smaugfm/lunchmoney/model/LunchmoneyRecurringExpense.kt +++ b/src/main/kotlin/io/github/smaugfm/lunchmoney/model/LunchmoneyRecurringExpense.kt @@ -27,7 +27,7 @@ import java.util.Currency @Serializable data class LunchmoneyRecurringExpense( val id: Long, - val startDate: LocalDate, + val startDate: LocalDate? = null, val endDate: LocalDate? = null, val cadence: String, val payee: String, diff --git a/src/test/kotlin/io/github/smaugfm/lunchmoney/JsonSchemaUpToDateTest.kt b/src/test/kotlin/io/github/smaugfm/lunchmoney/JsonSchemaUpToDateTest.kt index 4275aca..b6ee2ac 100644 --- a/src/test/kotlin/io/github/smaugfm/lunchmoney/JsonSchemaUpToDateTest.kt +++ b/src/test/kotlin/io/github/smaugfm/lunchmoney/JsonSchemaUpToDateTest.kt @@ -2,16 +2,25 @@ package io.github.smaugfm.lunchmoney import assertk.assertThat import assertk.assertions.hasSize +import assertk.assertions.isBetween import assertk.assertions.isEqualTo +import assertk.assertions.isGreaterThan +import assertk.assertions.isNull import assertk.assertions.isTrue import assertk.assertions.prop +import assertk.assertions.size import io.github.smaugfm.lunchmoney.api.LunchmoneyApi import io.github.smaugfm.lunchmoney.model.LunchmoneyCategorySingle +import io.github.smaugfm.lunchmoney.model.LunchmoneyInsertTransaction +import io.github.smaugfm.lunchmoney.model.LunchmoneyTransaction +import io.github.smaugfm.lunchmoney.model.LunchmoneyUpdateTransaction import io.github.smaugfm.lunchmoney.model.LunchmoneyUser import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable import reactor.tools.agent.ReactorDebugAgent +import java.math.BigDecimal +import java.time.LocalDate @EnabledIfEnvironmentVariable(named = "LUNCHMONEY_TEST_TOKEN", matches = "\\w+") class JsonSchemaUpToDateTest { @@ -43,22 +52,40 @@ class JsonSchemaUpToDateTest { fun getAllCategoriesTest() { val categories = api.getAllCategories().block()!! assertThat(categories) - .hasSize(11) + .size() + .isBetween(11, 12) } @Test - fun getSingleCategoryTest() { - val cat = api.getSingleCategory(489281).block()!! - assertThat(cat) - .prop(LunchmoneyCategorySingle::id) - .isEqualTo(489281) + fun crudCategoryTest() { + val catName = "test-category" + val catName2 = "test-category2" + val id = api.createCategory(catName, false, true, true).block()!! + try { + var cat = api.getSingleCategory(id).block()!! + assertThat(cat) + .prop(LunchmoneyCategorySingle::id) + .isEqualTo(id) + assertThat(cat) + .prop(LunchmoneyCategorySingle::name) + .isEqualTo(catName) + + assertThat(api.updateCategory(id, true, false, false, catName2).block()!!).isTrue() + cat = api.getSingleCategory(id).block()!! + assertThat(cat) + .prop(LunchmoneyCategorySingle::name) + .isEqualTo(catName2) + + } finally { + assertThat(api.forceDeleteCategory(id).block()!!).isTrue() + } } @Test - fun crudCategoryTest() { + fun crudCategoryGroupTest() { val catName = "test-category" val catName2 = "test-category2" - val id = api.createCategory(catName, false, true, true).block()!! + val id = api.createCategoryGroup(catName).block()!! try { var cat = api.getSingleCategory(id).block()!! assertThat(cat) @@ -78,4 +105,107 @@ class JsonSchemaUpToDateTest { assertThat(api.forceDeleteCategory(id).block()!!).isTrue() } } + + @Test + fun getAllTagsTest() { + val tags = api.getAllTags().block()!! + assertThat(tags) + .hasSize(5) + } + + @Test + fun getAllTransactionsTest() { + val transactions = api.getAllTransactions().block()!! + assertThat(transactions) + .size() + .isGreaterThan(23) + } + + @Test + fun crudTransactionTest() { + val ids = api.insertTransactions( + listOf( + LunchmoneyInsertTransaction(LocalDate.now(), BigDecimal.ONE) + ) + ).block()!! + assertThat(ids) + .hasSize(1) + val transaction = api.getSingleTransaction(ids[0]).block()!! + assertThat(transaction) + .prop(LunchmoneyTransaction::id) + .isEqualTo(ids[0]) + + assertThat( + api.updateTransaction(ids[0], LunchmoneyUpdateTransaction(LocalDate.now())) + .block()!!.updated + ).isTrue() + + } + + @Test + fun crudTransactionGroupTest() { + val transactions = api.getAllTransactions().block()!! + .filter { it.recurringId == null && it.groupId == null && !it.isGroup } + .subList(0, 2) + val id = api.createTransactionGroup( + LocalDate.now(), + "vasa", + transactions.map { it.id } + ).block()!! + try { + val transaction = api.getSingleTransaction(id).block()!! + assertThat(transaction) + .prop(LunchmoneyTransaction::id) + .isEqualTo(id) + + } finally { + assertThat(api.deleteTransactionGroup(id).block()!!.toSet()) + .isEqualTo(transactions.map { it.id }.toSet()) + } + } + + @Test + fun getRecurringExpensesTest() { + val from = LocalDate.of(2023, 6, 1) + val exp = api.getRecurringExpenses(from).block()!! + assertThat(exp) + .hasSize(6) + } + + @Test + fun getBudgetTest() { + val from = LocalDate.of(2023, 6, 1) + val to = LocalDate.of(2023, 6, 30) + val budgets = api.getBudgetSummary(from, to).block()!! + assertThat(budgets) + .size() + .isBetween(11, 12) + } + + @Test + fun budgetCrudTest() { + val catId = 489282L + val from = LocalDate.of(2023, 6, 1) + val amount = 123.123 + + assertThat(api.upsertBudget(from, catId, amount).block()).isNull() + assertThat(api.removeBudget(from, catId).block()!!).isTrue() + } + + @Test + fun getAllAssetsTest() { + val assets = api.getAllAssets().block()!! + assertThat(assets) + .hasSize(4) + } + + @Test + fun getAllPlaidAccounts() { + assertThat(api.getAllPlaidAccounts().block()!!).hasSize(0) + } + + @Test + fun getAllCryptoAccounts() { + assertThat(api.getAllCrypto().block()!!).hasSize(0) + } } diff --git a/src/test/kotlin/io/github/smaugfm/lunchmoney/request/category/CreateCategoryGroupRequestTest.kt b/src/test/kotlin/io/github/smaugfm/lunchmoney/request/category/CreateCategoryGroupRequestTest.kt index e8a8ebd..92c55e4 100644 --- a/src/test/kotlin/io/github/smaugfm/lunchmoney/request/category/CreateCategoryGroupRequestTest.kt +++ b/src/test/kotlin/io/github/smaugfm/lunchmoney/request/category/CreateCategoryGroupRequestTest.kt @@ -12,7 +12,6 @@ import io.github.smaugfm.lunchmoney.TestMockServerBase import io.github.smaugfm.lunchmoney.Util.getResourceAsString import io.github.smaugfm.lunchmoney.exception.LunchmoneyApiResponseException import io.github.smaugfm.lunchmoney.request.category.params.CreateCategoryGroupRequestParams -import io.github.smaugfm.lunchmoney.response.ApiErrorResponse import io.github.smaugfm.lunchmoney.response.CreateCategoryResponse import org.junit.jupiter.api.Test import org.mockserver.model.HttpRequest.request @@ -64,6 +63,6 @@ internal class CreateCategoryGroupRequestTest : TestMockServerBase() { .isNotNull() .isInstanceOf(LunchmoneyApiResponseException::class) .prop(LunchmoneyApiResponseException::message) - .isEqualTo(listOf("A category with the same name (vasa) already exists.")) + .isEqualTo("A category with the same name (vasa) already exists.") } }