Tests and refactoring
This commit is contained in:
parent
1c59227030
commit
536ac398c4
17 changed files with 216 additions and 97 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -3,8 +3,9 @@
|
||||||
|
|
||||||
# Ignore Gradle build output directory
|
# Ignore Gradle build output directory
|
||||||
build
|
build
|
||||||
|
.idea/
|
||||||
|
|
||||||
.env
|
.env
|
||||||
*.tar
|
*.tar
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
releases/
|
releases/
|
||||||
|
|
8
.idea/.gitignore
vendored
8
.idea/.gitignore
vendored
|
@ -1,8 +0,0 @@
|
||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CompilerConfiguration">
|
|
||||||
<bytecodeTargetLevel target="17" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
|
@ -1,17 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
|
||||||
<component name="GradleSettings">
|
|
||||||
<option name="linkedExternalProjectsSettings">
|
|
||||||
<GradleProjectSettings>
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="modules">
|
|
||||||
<set>
|
|
||||||
<option value="$PROJECT_DIR$" />
|
|
||||||
<option value="$PROJECT_DIR$/app" />
|
|
||||||
</set>
|
|
||||||
</option>
|
|
||||||
</GradleProjectSettings>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
|
@ -1,10 +0,0 @@
|
||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
|
||||||
<option name="processCode" value="true" />
|
|
||||||
<option name="processLiterals" value="true" />
|
|
||||||
<option name="processComments" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
|
@ -1,25 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="RemoteRepositoriesConfiguration">
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="central" />
|
|
||||||
<option name="name" value="Maven Central repository" />
|
|
||||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
|
||||||
</remote-repository>
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="jboss.community" />
|
|
||||||
<option name="name" value="JBoss Community repository" />
|
|
||||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
|
||||||
</remote-repository>
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="MavenRepo" />
|
|
||||||
<option name="name" value="MavenRepo" />
|
|
||||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
|
||||||
</remote-repository>
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="maven" />
|
|
||||||
<option name="name" value="maven" />
|
|
||||||
<option name="url" value="https://s01.oss.sonatype.org/content/repositories/snapshots/" />
|
|
||||||
</remote-repository>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="KotlinJpsPluginSettings">
|
|
||||||
<option name="version" value="1.9.20" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
|
||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
|
||||||
<file type="web" url="file://$PROJECT_DIR$" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-21" project-jdk-type="JavaSDK" />
|
|
||||||
</project>
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
|
@ -69,3 +69,7 @@ tasks.named<Test>("test") {
|
||||||
// Use JUnit Platform for unit tests.
|
// Use JUnit Platform for unit tests.
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.withType<Test>().configureEach {
|
||||||
|
jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED")
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@ import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
val config = Config()
|
val config = Config()
|
||||||
val publishers = Publishers(config)
|
val mastodonClientFactory = MastodonClientFactory()
|
||||||
|
val publishers = Publishers(config, mastodonClientFactory)
|
||||||
val releaseRepository = ReleaseRepository()
|
val releaseRepository = ReleaseRepository()
|
||||||
val app = App(config, releaseRepository, OkHttpClient(), publishers)
|
val app = App(config, releaseRepository, OkHttpClient(), publishers)
|
||||||
app.schedule()
|
app.schedule()
|
||||||
|
|
|
@ -49,11 +49,13 @@ class Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
mastodonCredentials = githubRepos.associate {
|
mastodonCredentials = githubRepos.associate {
|
||||||
|
val instanceUrlEnvName = "${it.name.uppercase()}_MASTODON_INSTANCE_URL"
|
||||||
|
val accessTokenEnvName = "${it.name.uppercase()}_MASTODON_ACCESS_TOKEN"
|
||||||
it.name to PublishingCredentials(
|
it.name to PublishingCredentials(
|
||||||
instanceUrlEnvName = "${it.name.uppercase()}_INSTANCE_URL",
|
instanceUrlEnvName = instanceUrlEnvName,
|
||||||
instanceUrl = System.getenv("${it.name.uppercase()}_INSTANCE_URL"),
|
instanceUrl = System.getenv(instanceUrlEnvName),
|
||||||
accessTokenEnvName = "${it.name.uppercase()}_ACCESS_TOKEN",
|
accessTokenEnvName = accessTokenEnvName,
|
||||||
accessToken = System.getenv("${it.name.uppercase()}_ACCESS_TOKEN")
|
accessToken = System.getenv(accessTokenEnvName)
|
||||||
)
|
)
|
||||||
}.onEach { (_, credentials) ->
|
}.onEach { (_, credentials) ->
|
||||||
if (!credentials.valid) {
|
if (!credentials.valid) {
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package de.rpr.githubreleases
|
||||||
|
|
||||||
|
import social.bigbone.MastodonClient
|
||||||
|
|
||||||
|
class MastodonClientFactory {
|
||||||
|
fun getClient(url: String, accessToken: String) = MastodonClient.Builder(url)
|
||||||
|
.accessToken(accessToken).build()
|
||||||
|
}
|
|
@ -37,22 +37,29 @@ class Publisher(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Publishers(private val config: Config) : Iterable<Publisher> {
|
class Publishers(
|
||||||
|
private val config: Config,
|
||||||
|
private val mastodonClientFactory: MastodonClientFactory,
|
||||||
|
) :
|
||||||
|
Iterable<Publisher> {
|
||||||
|
|
||||||
private val instances: List<Publisher>
|
private val instances: List<Publisher>
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val mastodonClients = config.githubRepos.associate {
|
val mastodonClients = config.githubRepos.associate {
|
||||||
val publishingCredentials = config.mastodonCredentials[it.name]!!
|
val publishingCredentials = config.mastodonCredentials[it.name]!!
|
||||||
it.name to MastodonClient.Builder(publishingCredentials.instanceUrl!!)
|
it.name to mastodonClientFactory.getClient(
|
||||||
.accessToken(publishingCredentials.accessToken!!).build()
|
publishingCredentials.instanceUrl!!,
|
||||||
|
publishingCredentials.accessToken!!
|
||||||
|
)
|
||||||
}
|
}
|
||||||
instances = config.githubRepos.map { Publisher(it.name, mastodonClients[it.name]!!) }
|
instances = config.githubRepos.map { Publisher(it.name, mastodonClients[it.name]!!) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun forName(name: String) = instances.first() { it.name == name }
|
fun forName(name: String) = instances.first { it.name == name }
|
||||||
|
|
||||||
override fun iterator(): Iterator<Publisher> {
|
override fun iterator(): Iterator<Publisher> {
|
||||||
return instances.iterator()
|
return instances.iterator()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ data class Release(
|
||||||
"\uD83C\uDF89 Navidrome $title has been published!\n\nRelease notes are available here: $link\n\n#Navidrome"
|
"\uD83C\uDF89 Navidrome $title has been published!\n\nRelease notes are available here: $link\n\n#Navidrome"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Release.asCollection() = listOf(this).asCollection()
|
||||||
fun List<Release>.asCollection() = Releases(this)
|
fun List<Release>.asCollection() = Releases(this)
|
||||||
|
|
||||||
class Releases(releases: List<Release>) : List<Release> {
|
class Releases(releases: List<Release>) : List<Release> {
|
||||||
|
|
49
app/src/test/kotlin/de/rpr/githubreleases/ConfigTest.kt
Normal file
49
app/src/test/kotlin/de/rpr/githubreleases/ConfigTest.kt
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package de.rpr.githubreleases
|
||||||
|
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.containsExactly
|
||||||
|
import assertk.assertions.containsOnly
|
||||||
|
import io.kotest.core.spec.style.DescribeSpec
|
||||||
|
import io.kotest.extensions.system.withEnvironment
|
||||||
|
import org.junit.jupiter.api.assertThrows
|
||||||
|
|
||||||
|
class ConfigTest : DescribeSpec({
|
||||||
|
|
||||||
|
describe("instantiation") {
|
||||||
|
|
||||||
|
withEnvironment(
|
||||||
|
mapOf(
|
||||||
|
"GITHUB_REPOS" to "https://github.com/navidrome/navidrome/",
|
||||||
|
"NAVIDROME_MASTODON_INSTANCE_URL" to "example.com",
|
||||||
|
"NAVIDROME_MASTODON_ACCESS_TOKEN" to "token"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
|
||||||
|
it("should populate instances from config") {
|
||||||
|
val config = Config()
|
||||||
|
assertThat(config.githubRepos).containsExactly(
|
||||||
|
GithubRepo("navidrome", "https://github.com/navidrome/navidrome/")
|
||||||
|
)
|
||||||
|
assertThat(config.mastodonCredentials).containsOnly(
|
||||||
|
"navidrome" to Config.PublishingCredentials(
|
||||||
|
instanceUrl = "example.com",
|
||||||
|
accessToken = "token",
|
||||||
|
instanceUrlEnvName = "NAVIDROME_MASTODON_INSTANCE_URL",
|
||||||
|
accessTokenEnvName = "NAVIDROME_MASTODON_ACCESS_TOKEN"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
withEnvironment(
|
||||||
|
mapOf("GITHUB_REPOS" to "https://github.com/navidrome/navidrome/")
|
||||||
|
) {
|
||||||
|
|
||||||
|
it("should throw error if env variables for repo are not present") {
|
||||||
|
assertThrows<IllegalStateException> {
|
||||||
|
Config()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
132
app/src/test/kotlin/de/rpr/githubreleases/PublishersTest.kt
Normal file
132
app/src/test/kotlin/de/rpr/githubreleases/PublishersTest.kt
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
package de.rpr.githubreleases
|
||||||
|
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isTrue
|
||||||
|
import assertk.assertions.prop
|
||||||
|
import io.kotest.core.spec.style.DescribeSpec
|
||||||
|
import io.kotest.extensions.system.withEnvironment
|
||||||
|
import io.mockk.*
|
||||||
|
import social.bigbone.MastodonClient
|
||||||
|
import social.bigbone.MastodonRequest
|
||||||
|
import social.bigbone.api.entity.Status
|
||||||
|
import social.bigbone.api.entity.data.Visibility
|
||||||
|
import social.bigbone.api.method.StatusMethods
|
||||||
|
|
||||||
|
class PublishersTest : DescribeSpec({
|
||||||
|
|
||||||
|
val mastodonClientFactory = mockk<MastodonClientFactory>()
|
||||||
|
val mastodonClient = mockk<MastodonClient>()
|
||||||
|
|
||||||
|
val statuses = mockk<StatusMethods>(relaxed = true)
|
||||||
|
val mockRequest = mockk<MastodonRequest<Status>>(relaxed = true)
|
||||||
|
|
||||||
|
beforeEach {
|
||||||
|
clearAllMocks()
|
||||||
|
every { mastodonClient.statuses } returns statuses
|
||||||
|
every { mastodonClientFactory.getClient(any(), any()) } returns mastodonClient
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Publishers collection") {
|
||||||
|
|
||||||
|
withEnvironment(
|
||||||
|
mapOf(
|
||||||
|
"GITHUB_REPOS" to "https://github.com/navidrome/navidrome/",
|
||||||
|
"NAVIDROME_MASTODON_INSTANCE_URL" to "example.com",
|
||||||
|
"NAVIDROME_MASTODON_ACCESS_TOKEN" to "token"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
|
||||||
|
it("should populate instances from config") {
|
||||||
|
val publishers = Publishers(Config(), mastodonClientFactory)
|
||||||
|
assertThat(publishers.iterator().hasNext()).isTrue()
|
||||||
|
assertThat(publishers.forName("navidrome"))
|
||||||
|
.prop("name") { it.name }
|
||||||
|
.isEqualTo("navidrome")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Publisher") {
|
||||||
|
|
||||||
|
val publisher = Publisher("example", mastodonClient)
|
||||||
|
|
||||||
|
xit("should send with correct visibility") {
|
||||||
|
|
||||||
|
every {
|
||||||
|
statuses.postStatus(
|
||||||
|
spoilerText = any<String>(),
|
||||||
|
status = any<String>(),
|
||||||
|
language = any<String>(),
|
||||||
|
visibility = any<Visibility>()
|
||||||
|
)
|
||||||
|
} returns mockRequest
|
||||||
|
|
||||||
|
publisher.sendReleases(Releases(testRelease))
|
||||||
|
|
||||||
|
verify {
|
||||||
|
statuses.postStatus(
|
||||||
|
status = any(),
|
||||||
|
mediaIds = any(),
|
||||||
|
visibility = Visibility.PUBLIC,
|
||||||
|
inReplyToId = any(),
|
||||||
|
sensitive = any(),
|
||||||
|
spoilerText = any(),
|
||||||
|
language = "en",
|
||||||
|
addIdempotencyKey = any()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it("should send correct status text") {
|
||||||
|
every {
|
||||||
|
statuses.postStatus(
|
||||||
|
spoilerText = any<String>(),
|
||||||
|
status = any<String>(),
|
||||||
|
language = any<String>(),
|
||||||
|
visibility = any<Visibility>()
|
||||||
|
)
|
||||||
|
} returns mockRequest
|
||||||
|
|
||||||
|
publisher.sendReleases(testRelease.copy(id = "v0.0.0").asCollection())
|
||||||
|
|
||||||
|
val statusSlot = slot<String>()
|
||||||
|
|
||||||
|
verify {
|
||||||
|
statuses.postStatus(
|
||||||
|
status = capture(statusSlot),
|
||||||
|
mediaIds = any(),
|
||||||
|
visibility = any(),
|
||||||
|
inReplyToId = any(),
|
||||||
|
sensitive = any(),
|
||||||
|
spoilerText = any(),
|
||||||
|
language = any(),
|
||||||
|
addIdempotencyKey = any()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(statusSlot.captured).isEqualTo("\uD83C\uDF89 Navidrome v0.0.0 has been published!\n\nRelease notes are available here: https://example.com\n\n#Navidrome")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Dry-run mode enabled") {
|
||||||
|
|
||||||
|
val publisher = Publisher(name = "example", client = mastodonClient, dryRun = true)
|
||||||
|
|
||||||
|
it("should not actually send events") {
|
||||||
|
|
||||||
|
every {
|
||||||
|
statuses.postStatus(
|
||||||
|
spoilerText = any<String>(),
|
||||||
|
status = any<String>(),
|
||||||
|
language = any<String>(),
|
||||||
|
visibility = any<Visibility>()
|
||||||
|
)
|
||||||
|
} returns mockRequest
|
||||||
|
|
||||||
|
publisher.sendReleases(testRelease.asCollection())
|
||||||
|
|
||||||
|
verify(exactly = 0) { mockRequest.execute() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
Loading…
Reference in a new issue