-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ForgeInstallationRepository implementation
- Add Slick-based ForgeInstallationRepository implementation - Add unit and integration tests for ForgeInstallationRepository implementation - Update evolutions script to configure new table, indices and foreign key column fixes #34
- Loading branch information
Showing
18 changed files
with
720 additions
and
96 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
.idea | ||
.bsp | ||
target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<configuration> | ||
|
||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | ||
<!-- encoders are assigned the type | ||
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> | ||
<encoder> | ||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> | ||
</encoder> | ||
</appender> | ||
|
||
<root level="info"> | ||
<appender-ref ref="STDOUT" /> | ||
</root> | ||
</configuration> |
44 changes: 44 additions & 0 deletions
44
src/it/scala/io/toolsplus/atlassian/connect/play/slick/PostgresContainerTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package io.toolsplus.atlassian.connect.play.slick | ||
|
||
import com.dimafeng.testcontainers.PostgreSQLContainer | ||
import com.dimafeng.testcontainers.scalatest.TestContainerForAll | ||
import org.scalatest.{TestData, TestSuite} | ||
import org.scalatestplus.play.guice.GuiceOneAppPerTest | ||
import org.testcontainers.utility.DockerImageName | ||
import play.api.Application | ||
import play.api.db.DBApi | ||
import play.api.db.evolutions.{ClassLoaderEvolutionsReader, Evolutions} | ||
import play.api.inject.guice.GuiceApplicationBuilder | ||
|
||
trait PostgresContainerTest | ||
extends GuiceOneAppPerTest | ||
with TestContainerForAll { self: TestSuite => | ||
|
||
val postgresVersion = "15.5" | ||
|
||
override val containerDef: PostgreSQLContainer.Def = | ||
PostgreSQLContainer.Def( | ||
DockerImageName.parse(s"postgres:$postgresVersion"), | ||
databaseName = "intercom", | ||
username = "test", | ||
password = "test", | ||
) | ||
|
||
override def newAppForTest(td: TestData): Application = withContainers { | ||
container => | ||
GuiceApplicationBuilder() | ||
.configure(ContainerDbConfiguration.configuration(container)) | ||
.build() | ||
} | ||
|
||
def dbApi(implicit app: Application): DBApi = | ||
Application.instanceCache[DBApi].apply(app) | ||
|
||
def withEvolutions[T](block: => T): T = | ||
Evolutions.withEvolutions( | ||
dbApi.database("default"), | ||
ClassLoaderEvolutionsReader.forPrefix("evolutions/")) { | ||
block | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
229 changes: 229 additions & 0 deletions
229
.../scala/io/toolsplus/atlassian/connect/play/slick/SlickForgeInstallationRepositoryIt.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
package io.toolsplus.atlassian.connect.play.slick | ||
|
||
import io.toolsplus.atlassian.connect.play.api.models.{ | ||
DefaultAtlassianHost, | ||
DefaultForgeInstallation | ||
} | ||
import io.toolsplus.atlassian.connect.play.slick.generators.AtlassianHostGen | ||
import org.scalatest.DoNotDiscover | ||
import org.scalatest.concurrent.Eventually | ||
import org.scalatestplus.play.PlaySpec | ||
import play.api.Application | ||
import play.api.test.{DefaultAwaitTimeout, FutureAwaits} | ||
|
||
import scala.collection.immutable | ||
import scala.concurrent.ExecutionContext.Implicits.global | ||
import scala.concurrent.Future | ||
|
||
@DoNotDiscover | ||
class SlickForgeInstallationRepositoryIt | ||
extends PlaySpec | ||
with PostgresContainerTest | ||
with FutureAwaits | ||
with Eventually | ||
with DefaultAwaitTimeout | ||
with AtlassianHostGen { | ||
|
||
val hostA: DefaultAtlassianHost = | ||
atlassianHostGen | ||
.retryUntil(_ => true) | ||
.sample | ||
.get | ||
.copy(installationId = Some("ari-installation-id-a")) | ||
val hostB: DefaultAtlassianHost = | ||
atlassianHostGen | ||
.retryUntil(_ => true) | ||
.sample | ||
.get | ||
.copy(installationId = Some("ari-installation-id-b")) | ||
|
||
val fakeInstallationHostA: DefaultForgeInstallation = | ||
DefaultForgeInstallation("fake-installation-id", hostA.clientKey) | ||
|
||
val fakeInstallationsHostB: Seq[DefaultForgeInstallation] = Seq( | ||
"ari-installation-id-1", | ||
"ari-installation-id-2", | ||
"ari-installation-id-3", | ||
"ari-installation-id-4", | ||
"ari-installation-id-5" | ||
).map(DefaultForgeInstallation(_, hostB.clientKey)) | ||
|
||
def hostRepo(implicit app: Application): SlickAtlassianHostRepository = | ||
Application.instanceCache[SlickAtlassianHostRepository].apply(app) | ||
|
||
def forgeInstallationRepo( | ||
implicit app: Application): SlickForgeInstallationRepository = | ||
Application.instanceCache[SlickForgeInstallationRepository].apply(app) | ||
|
||
"Using a Slick Forge installation repository" when { | ||
|
||
"repository is empty" should { | ||
|
||
"not find any hosts when fetching all" in { | ||
await { | ||
forgeInstallationRepo.all() | ||
} mustEqual immutable.Seq.empty | ||
} | ||
|
||
"return None when trying to find a non existent installation by installation id" in { | ||
await { | ||
forgeInstallationRepo.findByInstallationId("fake-installation-id") | ||
} mustBe None | ||
} | ||
|
||
"return empty Seq when trying to find a installations by client key" in { | ||
await { | ||
forgeInstallationRepo.findByClientKey("fake-client-key") | ||
} mustBe immutable.Seq.empty | ||
} | ||
|
||
} | ||
|
||
"saving a Forge installation to the repository" should { | ||
|
||
"successfully save the installation" in { | ||
withEvolutions { | ||
await(hostRepo.save(hostA)) | ||
await { | ||
forgeInstallationRepo.save(fakeInstallationHostA) | ||
} mustEqual fakeInstallationHostA | ||
|
||
await { | ||
forgeInstallationRepo.all() | ||
} mustEqual Seq(fakeInstallationHostA) | ||
} | ||
} | ||
|
||
"find the inserted installation by installation id" in { | ||
withEvolutions { | ||
await(hostRepo.save(hostA)) | ||
await { | ||
forgeInstallationRepo.save(fakeInstallationHostA) | ||
} | ||
|
||
await { | ||
forgeInstallationRepo.findByInstallationId( | ||
fakeInstallationHostA.installationId) | ||
} mustBe Some(fakeInstallationHostA) | ||
} | ||
} | ||
|
||
"find the inserted installation by client key" in { | ||
withEvolutions { | ||
await(hostRepo.save(hostA)) | ||
await { | ||
forgeInstallationRepo.save(fakeInstallationHostA) | ||
} | ||
|
||
await { | ||
forgeInstallationRepo.findByClientKey( | ||
fakeInstallationHostA.clientKey) | ||
} mustBe Seq(fakeInstallationHostA) | ||
} | ||
} | ||
|
||
} | ||
|
||
"saving different Forge installation with the same client key" should { | ||
|
||
"save all installations" in { | ||
withEvolutions { | ||
await(hostRepo.save(hostB)) | ||
await { | ||
Future.sequence( | ||
fakeInstallationsHostB.map(forgeInstallationRepo.save)) | ||
} | ||
|
||
await { | ||
forgeInstallationRepo.all() | ||
} mustBe fakeInstallationsHostB | ||
} | ||
} | ||
|
||
} | ||
|
||
"saving the same Forge installation twice" should { | ||
|
||
"not duplicate the installation" in { | ||
await(hostRepo.save(hostA)) | ||
withEvolutions { | ||
await { | ||
forgeInstallationRepo.save(fakeInstallationHostA) | ||
} mustBe fakeInstallationHostA | ||
|
||
await { | ||
forgeInstallationRepo.save(fakeInstallationHostA) | ||
} mustBe fakeInstallationHostA | ||
|
||
await { | ||
forgeInstallationRepo.all() | ||
} mustBe Seq(fakeInstallationHostA) | ||
} | ||
} | ||
|
||
} | ||
|
||
"updating a Forge installation" should { | ||
|
||
/** | ||
* This scenario appears if there is site import (migration) | ||
* from on Atlassian instance to another. The installation id | ||
* remains the same but the client key changes. | ||
*/ | ||
"successfully store the updated version" in { | ||
withEvolutions { | ||
await(hostRepo.save(hostA)) | ||
await(hostRepo.save(hostB)) | ||
val updated = | ||
fakeInstallationHostA.copy(clientKey = hostB.clientKey) | ||
await { | ||
forgeInstallationRepo.save(fakeInstallationHostA) | ||
} mustBe fakeInstallationHostA | ||
|
||
await { | ||
forgeInstallationRepo.save(updated) | ||
} mustBe updated | ||
|
||
await { | ||
forgeInstallationRepo.all() | ||
} mustBe Seq(updated) | ||
} | ||
} | ||
|
||
} | ||
|
||
"deleting a Forge installation by client key" should { | ||
|
||
"successfully delete all installations" in { | ||
withEvolutions { | ||
await(hostRepo.save(hostA)) | ||
await(hostRepo.save(hostB)) | ||
await { | ||
Future.sequence( | ||
fakeInstallationsHostB.map(forgeInstallationRepo.save)) | ||
} | ||
|
||
await { | ||
forgeInstallationRepo.save(fakeInstallationHostA) | ||
} mustBe fakeInstallationHostA | ||
|
||
await { | ||
forgeInstallationRepo.all() | ||
}.size mustBe (fakeInstallationsHostB ++ Seq(fakeInstallationHostA)).size | ||
|
||
await { | ||
forgeInstallationRepo.deleteByClientKey( | ||
fakeInstallationsHostB.head.clientKey) | ||
} mustBe fakeInstallationsHostB.size | ||
|
||
await { | ||
forgeInstallationRepo.all() | ||
} mustBe Seq(fakeInstallationHostA) | ||
} | ||
} | ||
|
||
} | ||
|
||
} | ||
|
||
} |
Oops, something went wrong.