From cac2d6e6a9f9a489fc0026ac4eafe706c112c9f9 Mon Sep 17 00:00:00 2001 From: Adam Jordens Date: Tue, 21 May 2019 12:53:09 -0700 Subject: [PATCH] fix(sql): Some additional tests and fixed for `front50-sql` --- front50-sql/front50-sql.gradle | 7 + .../front50/model/SqlStorageService.kt | 32 +++-- .../front50/model/SqlStorageServiceTests.kt | 129 +++++++++++++++++- 3 files changed, 152 insertions(+), 16 deletions(-) diff --git a/front50-sql/front50-sql.gradle b/front50-sql/front50-sql.gradle index d70fd54ab..6ac301ca4 100644 --- a/front50-sql/front50-sql.gradle +++ b/front50-sql/front50-sql.gradle @@ -18,8 +18,15 @@ dependencies { implementation "org.mariadb.jdbc:mariadb-java-client:2.2.3" // runtimeOnly "mysql:mysql-connector-java" + testImplementation "mysql:mysql-connector-java" testImplementation "dev.minutest:minutest" testImplementation "org.testcontainers:mysql" testImplementation "org.junit.jupiter:junit-jupiter-api" testImplementation "com.netflix.spinnaker.kork:kork-sql-test" } + +test { + useJUnitPlatform { + includeEngines "junit-vintage", "junit-jupiter" + } +} diff --git a/front50-sql/src/main/kotlin/com/netflix/spinnaker/front50/model/SqlStorageService.kt b/front50-sql/src/main/kotlin/com/netflix/spinnaker/front50/model/SqlStorageService.kt index 3184bf947..998dafbd8 100644 --- a/front50-sql/src/main/kotlin/com/netflix/spinnaker/front50/model/SqlStorageService.kt +++ b/front50-sql/src/main/kotlin/com/netflix/spinnaker/front50/model/SqlStorageService.kt @@ -70,7 +70,11 @@ class SqlStorageService( ctx .select(field("body", String::class.java)) .from(definitionsByType[objectType]!!.tableName) - .where(field("id", String::class.java).eq(objectKey)) + .where( + field("id", String::class.java).eq(objectKey).and( + DSL.field("is_deleted", Boolean::class.java).eq(false) + ) + ) .fetchOne() } ?: throw NotFoundException("Object not found (key: $objectKey)") @@ -97,9 +101,6 @@ class SqlStorageService( override fun storeObject(objectType: ObjectType, objectKey: String, item: T) { item.lastModifiedBy = AuthenticatedRequest.getSpinnakerUser().orElse("anonymous") - // TODO-AJ handle overriding the `lastModified` during a migration so that not everything is `now()` - item.lastModified = clock.millis() - try { jooq.transactional(sqlRetryProperties.transactions) { ctx -> val insert = ctx.insertInto( @@ -173,12 +174,23 @@ class SqlStorageService( objectKey: String, maxResults: Int): List { val bodies = jooq.withRetry(sqlRetryProperties.reads) { ctx -> - ctx - .select(field("body", String::class.java)) - .from(definitionsByType[objectType]!!.tableName) - .where(DSL.field("id", String::class.java).eq(objectKey)) - .fetch() - .getValues(field("body", String::class.java)) + + if (definitionsByType[objectType]!!.supportsHistory) { + ctx + .select(field("body", String::class.java)) + .from(definitionsByType[objectType]!!.historyTableName) + .where(DSL.field("id", String::class.java).eq(objectKey)) + .orderBy(DSL.field("recorded_at").desc()) + .fetch() + .getValues(field("body", String::class.java)) + } else { + ctx + .select(field("body", String::class.java)) + .from(definitionsByType[objectType]!!.tableName) + .where(DSL.field("id", String::class.java).eq(objectKey)) + .fetch() + .getValues(field("body", String::class.java)) + } } return bodies.map { diff --git a/front50-sql/src/test/kotlin/com/netflix/spinnaker/front50/model/SqlStorageServiceTests.kt b/front50-sql/src/test/kotlin/com/netflix/spinnaker/front50/model/SqlStorageServiceTests.kt index c108604ca..f82d732d2 100644 --- a/front50-sql/src/test/kotlin/com/netflix/spinnaker/front50/model/SqlStorageServiceTests.kt +++ b/front50-sql/src/test/kotlin/com/netflix/spinnaker/front50/model/SqlStorageServiceTests.kt @@ -20,6 +20,8 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.netflix.spectator.api.NoopRegistry import com.netflix.spinnaker.front50.exception.NotFoundException import com.netflix.spinnaker.front50.model.application.Application +import com.netflix.spinnaker.front50.model.pipeline.Pipeline +import com.netflix.spinnaker.front50.model.tag.EntityTags import com.netflix.spinnaker.kork.sql.config.SqlRetryProperties import dev.minutest.junit.JUnit5Minutests import dev.minutest.rootContext @@ -49,29 +51,51 @@ internal object SqlStorageServiceTests : JUnit5Minutests { jooq.flushAll() } - context("Application CRUD") { + context("Application") { test("throws NotFoundException when application does not exist") { expectThrows { sqlStorageService.loadObject(ObjectType.APPLICATION, "application001") } } - test("creates an application") { + test("create, update and delete an application") { + // verify that an application can be created sqlStorageService.storeObject( ObjectType.APPLICATION, "application001", Application().apply { name = "application001" description = "my first application!" - updateTs = "100" + lastModified = 100 } ) - val application = sqlStorageService.loadObject(ObjectType.APPLICATION, "application001") + var application = sqlStorageService.loadObject(ObjectType.APPLICATION, "application001") expectThat(application.description).isEqualTo("my first application!") - } - test("deletes an application") { + // verify that an application can be updated + sqlStorageService.storeObject( + ObjectType.APPLICATION, + "application001", + Application().apply { + name = "application001" + description = "my updated application!" + lastModified = 200 + } + ) + + application = sqlStorageService.loadObject(ObjectType.APPLICATION, "application001") + expectThat(application.description).isEqualTo("my updated application!") + + // verify that history can be retrieved + val applicationVersions = sqlStorageService.listObjectVersions( + ObjectType.APPLICATION, "application001", 5 + ) + expectThat(applicationVersions.size).isEqualTo(2) + expectThat(applicationVersions.get(0).description).isEqualTo("my updated application!") + expectThat(applicationVersions.get(1).description).isEqualTo("my first application!") + + // delete the application sqlStorageService.deleteObject(ObjectType.APPLICATION, "application001") expectThrows { @@ -79,6 +103,99 @@ internal object SqlStorageServiceTests : JUnit5Minutests { } } } + + context("Pipeline") { + test("create, update and delete a pipeline") { + // verify that a pipeline can be created + sqlStorageService.storeObject( + ObjectType.PIPELINE, + "id-pipeline001", + Pipeline().apply { + name = "pipeline001" + lastModified = 100 + + put("application", "application001") + } + ) + + var pipeline = sqlStorageService.loadObject(ObjectType.PIPELINE, "id-pipeline001") + expectThat(pipeline.name).isEqualTo("pipeline001") + + // verify that a pipeline can be updated + sqlStorageService.storeObject( + ObjectType.PIPELINE, + "id-pipeline001", + Pipeline().apply { + name = "pipeline001_updated" + lastModified = 200 + + put("application", "application001") + } + ) + + pipeline = sqlStorageService.loadObject(ObjectType.PIPELINE, "id-pipeline001") + expectThat(pipeline.name).isEqualTo("pipeline001_updated") + + // delete the pipeline + sqlStorageService.deleteObject(ObjectType.PIPELINE, "id-pipeline001") + + expectThrows { + sqlStorageService.loadObject(ObjectType.PIPELINE, "id-pipeline001") + } + } + } + + context("Entity Tags") { + test("create, update and delete an entity tag") { + // verify that entity tags can be created + sqlStorageService.storeObject( + ObjectType.ENTITY_TAGS, + "id-entitytags001", + EntityTags().apply { + id = "id-entitytags001" + lastModified = 100 + + entityRef = EntityTags.EntityRef().apply { + entityId = "application001" + } + } + ) + + var entityTags = sqlStorageService.loadObject(ObjectType.ENTITY_TAGS, "id-entitytags001") + expectThat(entityTags.entityRef.entityId).isEqualTo("application001") + + // verify that entity tags can be updated + sqlStorageService.storeObject( + ObjectType.ENTITY_TAGS, + "id-entitytags001", + EntityTags().apply { + id = "id-entitytags001" + lastModified = 200 + + entityRef = EntityTags.EntityRef().apply { + entityId = "application002" + } + } + ) + + entityTags = sqlStorageService.loadObject(ObjectType.ENTITY_TAGS, "id-entitytags001") + expectThat(entityTags.entityRef.entityId).isEqualTo("application002") + + // entity tags are _not_ versioned so only the most recent should be returned + val entityTagsVersions = sqlStorageService.listObjectVersions( + ObjectType.ENTITY_TAGS, "id-entitytags001", 5 + ) + expectThat(entityTagsVersions.size).isEqualTo(1) + expectThat(entityTagsVersions.get(0).entityRef.entityId).isEqualTo("application002") + + // delete the entity tags + sqlStorageService.deleteObject(ObjectType.ENTITY_TAGS, "id-entitytags001") + + expectThrows { + sqlStorageService.loadObject(ObjectType.ENTITY_TAGS, "id-entitytags001") + } + } + } } @JvmStatic