This is an automated email from the ASF dual-hosted git repository.

pjfanning pushed a commit to branch 1.3.x
in repository https://gitbox.apache.org/repos/asf/pekko-persistence-jdbc.git


The following commit(s) were added to refs/heads/1.3.x by this push:
     new e79cba6f fix incorrect impl of Durable State deleteObject(id, 
revision) (#482) (#505)
e79cba6f is described below

commit e79cba6f875890b585945a7f356dcf18325d604c
Author: PJ Fanning <[email protected]>
AuthorDate: Fri May 29 10:46:39 2026 +0100

    fix incorrect impl of Durable State deleteObject(id, revision) (#482) (#505)
    
    * test durable state store tck (#482)
    
    * Copy DurableStateStore TCK base classes from apache/pekko#2833 and add 
integration tests for all supported databases
    
    Agent-Logs-Url: 
https://github.com/pjfanning/incubator-pekko-persistence-jdbc/sessions/c3116ae6-042e-496b-b5bc-f480e95f7a01
    
    Co-authored-by: pjfanning <[email protected]>
    
    * Fix: remove sealed CapabilityFlags inheritance from 
DurableStateStoreCapabilityFlags
    
    Agent-Logs-Url: 
https://github.com/pjfanning/incubator-pekko-persistence-jdbc/sessions/dcab8f13-0bc4-403e-afbd-d60bdaa2b03a
    
    Co-authored-by: pjfanning <[email protected]>
    
    * Fix Oracle TCK failures: use non-empty tag in DurableStateStoreSpec tests
    
    Agent-Logs-Url: 
https://github.com/pjfanning/incubator-pekko-persistence-jdbc/sessions/491c7f9d-e7f5-446b-8cac-cc96c7c17767
    
    Co-authored-by: pjfanning <[email protected]>
    
    * Bring over latest TCK changes from apache/pekko#2917
    
    Agent-Logs-Url: 
https://github.com/pjfanning/incubator-pekko-persistence-jdbc/sessions/b8ecac9c-455d-49ad-b6c1-7397780899da
    
    Co-authored-by: pjfanning <[email protected]>
    
    * use tck files from 2.0.0-M2 release
    
    * Fix deleteObject revision semantics to match pekko 2.0.0-M2 TCK
    
    Agent-Logs-Url: 
https://github.com/pjfanning/incubator-pekko-persistence-jdbc/sessions/146149b4-03f1-4102-b987-773bc5eda38e
    
    Co-authored-by: pjfanning <[email protected]>
    
    * remove use of DeleteRevisionException
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] 
<[email protected]>
    Co-authored-by: pjfanning <[email protected]>
    
    * add tck spec
    
    * Update DurableStateStoreSpec.scala
    
    * Update JdbcDurableStateStoreTCKSpec.scala
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] 
<[email protected]>
    Co-authored-by: pjfanning <[email protected]>
---
 .../jdbc/state/DurableStateQueries.scala           |   8 +-
 .../scaladsl/DurableStateExceptionSupport.scala    |  48 -----
 .../state/scaladsl/JdbcDurableStateStore.scala     |  25 ++-
 .../state/scaladsl/DurableStateStoreSpec.scala     | 214 +++++++++++++++++++++
 .../jdbc/state/scaladsl/JdbcDurableStateSpec.scala |  28 ++-
 .../scaladsl/JdbcDurableStateStoreTCKSpec.scala    |  55 ++++++
 .../integration/DurableStateStoreTCKSpec.scala     |  52 +++++
 7 files changed, 370 insertions(+), 60 deletions(-)

diff --git 
a/core/src/main/scala/org/apache/pekko/persistence/jdbc/state/DurableStateQueries.scala
 
b/core/src/main/scala/org/apache/pekko/persistence/jdbc/state/DurableStateQueries.scala
index b4346100..86bfabad 100644
--- 
a/core/src/main/scala/org/apache/pekko/persistence/jdbc/state/DurableStateQueries.scala
+++ 
b/core/src/main/scala/org/apache/pekko/persistence/jdbc/state/DurableStateQueries.scala
@@ -115,13 +115,15 @@ import slick.jdbc.{
   }
 
   /**
-   * Deletes a particular revision of an object based on its persistenceId.
-   * This revision may no longer exist and if so, no delete will occur.
+   * Deletes the row for a given persistenceId whose revision equals `revision 
- 1`.
+   * Following the same "next-revision" convention as `upsertObject`, the 
caller passes
+   * the tombstone revision (= current stored revision + 1), so we delete the 
row
+   * where `storedRevision = passedRevision - 1`.
    *
    * @since 1.1.0
    */
   private[jdbc] def deleteBasedOnPersistenceIdAndRevision(persistenceId: 
String, revision: Long) = {
-    selectFromDbByPersistenceId(persistenceId).filter(_.revision === 
revision).delete
+    selectFromDbByPersistenceId(persistenceId).filter(_.revision === revision 
- 1L).delete
   }
 
   def deleteAllFromDb() = {
diff --git 
a/core/src/main/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/DurableStateExceptionSupport.scala
 
b/core/src/main/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/DurableStateExceptionSupport.scala
deleted file mode 100644
index 88e19316..00000000
--- 
a/core/src/main/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/DurableStateExceptionSupport.scala
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.pekko.persistence.jdbc.state.scaladsl
-
-import java.lang.invoke.{ MethodHandles, MethodType }
-
-import scala.util.Try
-
-/**
- * INTERNAL API
- *
- * Support for creating a `DeleteRevisionException`if the class is
- * available on the classpath. Pekko 1.0 does not have this class, but
- * it is added in Pekko 1.1.
- */
-private[scaladsl] object DurableStateExceptionSupport {
-  val DeleteRevisionExceptionClass =
-    "org.apache.pekko.persistence.state.exception.DeleteRevisionException"
-
-  private def exceptionClassOpt: Option[Class[_]] =
-    Try(Class.forName(DeleteRevisionExceptionClass)).toOption
-
-  private val constructorOpt = exceptionClassOpt.map { clz =>
-    val mt = MethodType.methodType(classOf[Unit], classOf[String])
-    MethodHandles.publicLookup().findConstructor(clz, mt)
-  }
-
-  def createDeleteRevisionExceptionIfSupported(message: String): 
Option[Exception] =
-    constructorOpt.map { constructor =>
-      constructor.invoke(message).asInstanceOf[Exception]
-    }
-
-}
diff --git 
a/core/src/main/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/JdbcDurableStateStore.scala
 
b/core/src/main/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/JdbcDurableStateStore.scala
index ca05b084..3f5585db 100644
--- 
a/core/src/main/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/JdbcDurableStateStore.scala
+++ 
b/core/src/main/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/JdbcDurableStateStore.scala
@@ -102,7 +102,24 @@ class JdbcDurableStateStore[A](
     Future
       .fromTry(row)
       .flatMap { r =>
-        val action = if (revision == 1) insertDurableState(r) else 
updateDurableState(r)
+        val action = if (revision == 1) insertDurableState(r)
+        else {
+          // Try UPDATE first (entity exists at revision - 1). If 0 rows are 
affected, the
+          // entity may have been deleted (no row at all) rather than having a 
stale revision.
+          // In that case fall back to INSERT so that re-creation after 
deletion works.
+          for {
+            s <- queries.getSequenceNextValueExpr()
+            u <- queries.updateDbWithDurableState(r, s.head)
+            result <-
+              if (u > 0) DBIO.successful(u)
+              else {
+                
queries.selectFromDbByPersistenceId(persistenceId).exists.result.flatMap { 
exists =>
+                  if (!exists) queries.insertDbWithDurableState(r, s.head)
+                  else DBIO.successful(0) // entity exists with wrong revision 
-> propagate 0 to throw below
+                }
+              }
+          } yield result
+        }
         db.run(action)
       }
       .map { rowsAffected =>
@@ -119,16 +136,12 @@ class JdbcDurableStateStore[A](
   override def deleteObject(persistenceId: String, revision: Long): 
Future[Done] =
     db.run(queries.deleteBasedOnPersistenceIdAndRevision(persistenceId, 
revision)).map { count =>
       if (count != 1) {
-        // if you run this code with Pekko 1.0.x, no exception will be thrown 
here
-        // this matches the behavior of pekko-connectors-jdbc 1.0.x
-        // if you run this code with Pekko 1.1.x, a DeleteRevisionException 
will be thrown here
         val msg = if (count == 0) {
           s"Failed to delete object with persistenceId [$persistenceId] and 
revision [$revision]"
         } else {
           s"Delete object succeeded for persistenceId [$persistenceId] and 
revision [$revision] but more than one row was affected ($count rows)"
         }
-        
DurableStateExceptionSupport.createDeleteRevisionExceptionIfSupported(msg)
-          .foreach(throw _)
+        throw new IllegalStateException(msg)
       }
       Done
     }(ExecutionContexts.parasitic)
diff --git 
a/core/src/test/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/DurableStateStoreSpec.scala
 
b/core/src/test/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/DurableStateStoreSpec.scala
new file mode 100644
index 00000000..557c6a6b
--- /dev/null
+++ 
b/core/src/test/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/DurableStateStoreSpec.scala
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * license agreements; and to You under the Apache License, version 2.0:
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is part of the Apache Pekko project, which was derived from Akka.
+ */
+
+/*
+ * Copyright (C) 2009-2022 Lightbend Inc. <https://www.lightbend.com>
+ */
+
+package org.apache.pekko.persistence.jdbc.state.scaladsl
+
+import scala.annotation.nowarn
+import scala.concurrent.Await
+import scala.concurrent.duration._
+
+import org.apache.pekko
+import pekko.actor.ActorSystem
+import pekko.persistence._
+import pekko.persistence.scalatest.{ MayVerb, OptionalTests }
+import pekko.persistence.state.DurableStateStoreRegistry
+import pekko.persistence.state.scaladsl.DurableStateUpdateStore
+import pekko.testkit.TestProbe
+
+import com.typesafe.config.Config
+import com.typesafe.config.ConfigFactory
+
+object DurableStateStoreSpec {
+  val config: Config = ConfigFactory.parseString(s"""
+    pekko.actor {
+      serializers {
+        durable-state-tck-test = "${classOf[TestSerializer].getName}"
+      }
+      serialization-bindings {
+        "${classOf[TestPayload].getName}" = durable-state-tck-test
+      }
+    }
+    """)
+}
+
+/**
+ * This spec aims to verify custom pekko-persistence [[DurableStateStore]] 
implementations.
+ * Plugin authors are highly encouraged to include it in their plugin's test 
suites.
+ *
+ * In case your durable state store plugin needs some kind of setup or 
teardown, override the `beforeAll`
+ * or `afterAll` methods (don't forget to call `super` in your overridden 
methods).
+ *
+ * This is a copy of the TCK spec added in Pekko 2.0.0.
+ * 
https://github.com/apache/pekko/blob/4f20b4736980a5d57bc58e1356fea4dede87a7cd/persistence-tck/src/main/scala/org/apache/pekko/persistence/state/DurableStateStoreSpec.scala
+ */
+abstract class DurableStateStoreSpec(config: Config)
+    extends PluginSpec(config)
+    with MayVerb
+    with OptionalTests {
+
+  implicit lazy val system: ActorSystem =
+    ActorSystem("DurableStateStoreSpec", 
config.withFallback(DurableStateStoreSpec.config))
+
+  protected def supportsDeleteWithRevisionCheck: CapabilityFlag = 
CapabilityFlag.off()
+
+  protected def supportsUpsertWithRevisionCheck: CapabilityFlag = 
CapabilityFlag.off()
+
+  protected def supportsSerialization: CapabilityFlag = CapabilityFlag.on()
+
+  protected def supportsSoftDelete: CapabilityFlag = CapabilityFlag.off()
+
+  override protected def beforeEach(): Unit = {
+    super.beforeEach()
+    preparePersistenceId(pid)
+  }
+
+  /**
+   * Overridable hook that is called before each test case.
+   * `pid` is the `persistenceId` that will be used in the test.
+   * This method may be needed to clean any pre-existing state from the store,
+   * for example when running against a shared external database.
+   */
+  def preparePersistenceId(@nowarn("msg=never used") pid: String): Unit = ()
+
+  /**
+   * Returns the `DurableStateUpdateStore` under test. By default, this uses 
the plugin
+   * configured under `pekko.persistence.state.plugin` in the provided config.
+   */
+  def durableStateStore(): DurableStateUpdateStore[Any] =
+    
DurableStateStoreRegistry(system).durableStateStoreFor[DurableStateUpdateStore[Any]]("")
+
+  protected val timeout: FiniteDuration = 5.seconds
+
+  "A durable state store" must {
+    "not find a non-existing object" in {
+      val result = Await.result(durableStateStore().getObject(pid), timeout)
+      result.value shouldBe None
+    }
+
+    "persist a state and retrieve it" in {
+      val value = s"state-${pid}"
+      Await.result(durableStateStore().upsertObject(pid, 1L, value, 
"test-tag"), timeout)
+      val result = Await.result(durableStateStore().getObject(pid), timeout)
+      result.value shouldBe Some(value)
+      result.revision shouldBe 1L
+    }
+
+    "update a state" in {
+      val store = durableStateStore()
+      val value1 = s"state-1-${pid}"
+      val value2 = s"state-2-${pid}"
+      Await.result(store.upsertObject(pid, 1L, value1, "test-tag"), timeout)
+      Await.result(store.upsertObject(pid, 2L, value2, "test-tag"), timeout)
+      val result = Await.result(store.getObject(pid), timeout)
+      result.value shouldBe Some(value2)
+      result.revision shouldBe 2L
+    }
+
+    "delete a state" in {
+      val store = durableStateStore()
+      val value = s"state-${pid}"
+      Await.result(store.upsertObject(pid, 1L, value, "test-tag"), timeout)
+      Await.result(store.deleteObject(pid, 2L), timeout)
+      val result = Await.result(store.getObject(pid), timeout)
+      result.value shouldBe None
+    }
+
+    "handle different persistence IDs independently" in {
+      val store = durableStateStore()
+      val pid2 = pid + "-2"
+      val value1 = s"state-${pid}"
+      val value2 = s"state-${pid2}"
+      Await.result(store.upsertObject(pid, 1L, value1, "test-tag"), timeout)
+      Await.result(store.upsertObject(pid2, 1L, value2, "test-tag"), timeout)
+
+      val result1 = Await.result(store.getObject(pid), timeout)
+      val result2 = Await.result(store.getObject(pid2), timeout)
+
+      result1.value shouldBe Some(value1)
+      result2.value shouldBe Some(value2)
+    }
+
+    "upsert again after a deletion" in {
+      val store = durableStateStore()
+      val original = s"state-${pid}"
+      val recreated = s"state-${pid}-v2"
+      Await.result(store.upsertObject(pid, 1L, original, "test-tag"), timeout)
+      Await.result(store.deleteObject(pid, 2L), timeout)
+      Await.result(store.upsertObject(pid, 3L, recreated, "test-tag"), timeout)
+      val result = Await.result(store.getObject(pid), timeout)
+      result.value shouldBe Some(recreated)
+      result.revision shouldBe 3L
+    }
+  }
+
+  "A durable state store optionally".may {
+    optional(flag = supportsDeleteWithRevisionCheck) {
+      "fail to delete a state when the revision does not match" in {
+        val store = durableStateStore()
+        val value = s"state-${pid}"
+        Await.result(store.upsertObject(pid, 1L, value, "test-tag"), timeout)
+        val deleteResult = store.deleteObject(pid, 99L)
+        intercept[Exception] {
+          Await.result(deleteResult, timeout)
+        }
+        // The original state should still be accessible
+        val result = Await.result(store.getObject(pid), timeout)
+        result.value shouldBe Some(value)
+        result.revision shouldBe 1L
+      }
+    }
+
+    optional(flag = supportsUpsertWithRevisionCheck) {
+      "fail to upsert a state when the revision is stale" in {
+        val store = durableStateStore()
+        val original = s"state-${pid}"
+        val stale = s"state-${pid}-stale"
+        Await.result(store.upsertObject(pid, 1L, original, "test-tag"), 
timeout)
+        // Re-using revision 1 should be rejected; the next valid revision is 
2.
+        val staleUpsert = store.upsertObject(pid, 1L, stale, "test-tag")
+        intercept[Exception] {
+          Await.result(staleUpsert, timeout)
+        }
+        // The original state should still be accessible
+        val result = Await.result(store.getObject(pid), timeout)
+        result.value shouldBe Some(original)
+        result.revision shouldBe 1L
+      }
+    }
+
+    optional(flag = supportsSerialization) {
+      "serialize and deserialize values via the configured serializer" in {
+        val store = durableStateStore()
+        val probe = TestProbe()
+        val value = TestPayload(probe.ref)
+        Await.result(store.upsertObject(pid, 1L, value, "test-tag"), timeout)
+        val result = Await.result(store.getObject(pid), timeout)
+        result.value shouldBe Some(value)
+        result.revision shouldBe 1L
+      }
+    }
+
+    optional(flag = supportsSoftDelete) {
+      "delete a state via the deprecated deleteObject overload" in {
+        val store = durableStateStore()
+        val value = s"state-${pid}"
+        Await.result(store.upsertObject(pid, 1L, value, "test-tag"), timeout)
+        @nowarn("cat=deprecation")
+        val deleteResult = store.deleteObject(pid)
+        Await.result(deleteResult, timeout)
+        val result = Await.result(store.getObject(pid), timeout)
+        result.value shouldBe None
+      }
+    }
+  }
+}
diff --git 
a/core/src/test/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/JdbcDurableStateSpec.scala
 
b/core/src/test/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/JdbcDurableStateSpec.scala
index 119b1e84..06387528 100644
--- 
a/core/src/test/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/JdbcDurableStateSpec.scala
+++ 
b/core/src/test/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/JdbcDurableStateSpec.scala
@@ -121,7 +121,7 @@ abstract class JdbcDurableStateSpec(config: Config, 
schemaType: SchemaType) exte
         }
       }
     }
-    "fail to delete old object revision" in {
+    "fail to delete with old object revision" in {
       val f = for {
         n <- stateStoreString.upsertObject("p987", 1, "a valid string", "t123")
         _ = n shouldBe pekko.Done
@@ -137,7 +137,7 @@ abstract class JdbcDurableStateSpec(config: Config, 
schemaType: SchemaType) exte
         }
       } else {
         whenReady(f.failed) { e =>
-          e.getClass.getName shouldEqual 
DurableStateExceptionSupport.DeleteRevisionExceptionClass
+          e.getClass shouldEqual classOf[IllegalStateException]
           e.getMessage should include("Failed to delete object with 
persistenceId [p987] and revision [1]")
         }
       }
@@ -152,7 +152,9 @@ abstract class JdbcDurableStateSpec(config: Config, 
schemaType: SchemaType) exte
           _ = g.value shouldBe Some("a valid string")
           u <- stateStoreString.upsertObject("p9876", 2, "updated valid 
string", "t123")
           _ = u shouldBe pekko.Done
-          d <- stateStoreString.deleteObject("p9876", 2)
+          // revision 3 = current stored revision (2) + 1, following the same 
tombstone-revision
+          // convention used by upsertObject and the pekko-persistence TCK
+          d <- stateStoreString.deleteObject("p9876", 3)
           _ = d shouldBe pekko.Done
           h <- stateStoreString.getObject("p9876")
 
@@ -162,6 +164,26 @@ abstract class JdbcDurableStateSpec(config: Config, 
schemaType: SchemaType) exte
         v.value shouldBe None
       }
     }
+    "upsert again after deletion" in {
+      whenReady {
+        for {
+          n <- stateStoreString.upsertObject("p11111", 1, "original string", 
"t123")
+          _ = n shouldBe pekko.Done
+          // tombstone revision = current stored revision (1) + 1 = 2
+          d <- stateStoreString.deleteObject("p11111", 2)
+          _ = d shouldBe pekko.Done
+          g <- stateStoreString.getObject("p11111")
+          _ = g.value shouldBe None
+          // re-insert: revision = tombstone revision (2) + 1 = 3
+          u <- stateStoreString.upsertObject("p11111", 3, "recreated string", 
"t123")
+          _ = u shouldBe pekko.Done
+          h <- stateStoreString.getObject("p11111")
+        } yield h
+      } { v =>
+        v.value shouldBe Some("recreated string")
+        v.revision shouldBe 3L
+      }
+    }
   }
 
   "A durable state store with payload that needs custom serializer" must 
withActorSystem { implicit system =>
diff --git 
a/core/src/test/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/JdbcDurableStateStoreTCKSpec.scala
 
b/core/src/test/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/JdbcDurableStateStoreTCKSpec.scala
new file mode 100644
index 00000000..090394f7
--- /dev/null
+++ 
b/core/src/test/scala/org/apache/pekko/persistence/jdbc/state/scaladsl/JdbcDurableStateStoreTCKSpec.scala
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * license agreements; and to You under the Apache License, version 2.0:
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is part of the Apache Pekko project, which was derived from Akka.
+ */
+
+package org.apache.pekko.persistence.jdbc.state.scaladsl
+
+import com.typesafe.config.Config
+import org.apache.pekko
+import pekko.persistence.CapabilityFlag
+import pekko.persistence.jdbc.config.SlickConfiguration
+import pekko.persistence.jdbc.db.SlickDatabase
+import pekko.persistence.jdbc.testkit.internal.SchemaType
+import pekko.persistence.jdbc.util.DropCreate
+import org.scalatest.BeforeAndAfterAll
+
+abstract class JdbcDurableStateStoreTCKSpec(config: Config, schemaType: 
SchemaType)
+    extends DurableStateStoreSpec(config)
+    with BeforeAndAfterAll
+    with DropCreate {
+
+  override protected def supportsDeleteWithRevisionCheck: CapabilityFlag = 
CapabilityFlag.on()
+
+  override protected def supportsUpsertWithRevisionCheck: CapabilityFlag = 
CapabilityFlag.on()
+
+  override protected def supportsSerialization: CapabilityFlag = 
CapabilityFlag.on()
+
+  override protected def supportsSoftDelete: CapabilityFlag = 
CapabilityFlag.on()
+
+  lazy val db = {
+    val cfg = config.getConfig("jdbc-durable-state-store")
+    if (cfg.hasPath("slick.profile")) {
+      SlickDatabase.database(cfg, new 
SlickConfiguration(cfg.getConfig("slick")), "slick.db")
+    } else {
+      SlickDatabase.database(
+        config,
+        new 
SlickConfiguration(config.getConfig("pekko-persistence-jdbc.shared-databases.slick")),
+        "pekko-persistence-jdbc.shared-databases.slick.db")
+    }
+  }
+
+  override def beforeAll(): Unit = {
+    dropAndCreate(schemaType)
+    super.beforeAll()
+  }
+
+  override def afterAll(): Unit = {
+    super.afterAll()
+    db.close()
+  }
+}
diff --git 
a/integration-test/src/test/scala/org/apache/pekko/persistence/jdbc/integration/DurableStateStoreTCKSpec.scala
 
b/integration-test/src/test/scala/org/apache/pekko/persistence/jdbc/integration/DurableStateStoreTCKSpec.scala
new file mode 100644
index 00000000..e113e634
--- /dev/null
+++ 
b/integration-test/src/test/scala/org/apache/pekko/persistence/jdbc/integration/DurableStateStoreTCKSpec.scala
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * license agreements; and to You under the Apache License, version 2.0:
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is part of the Apache Pekko project, which was derived from Akka.
+ */
+
+package org.apache.pekko.persistence.jdbc.integration
+
+import com.typesafe.config.ConfigFactory
+import 
org.apache.pekko.persistence.jdbc.state.scaladsl.JdbcDurableStateStoreTCKSpec
+import org.apache.pekko.persistence.jdbc.testkit.internal.{ MariaDB, MySQL, 
Oracle, Postgres, SqlServer }
+
+// postgres-application.conf already includes pekko.persistence.state.plugin = 
"jdbc-durable-state-store"
+class PostgresDurableStateStoreTCKSpec
+    extends 
JdbcDurableStateStoreTCKSpec(ConfigFactory.load("postgres-application.conf"), 
Postgres)
+
+// mariadb-application.conf already includes pekko.persistence.state.plugin = 
"jdbc-durable-state-store"
+class MariaDBDurableStateStoreTCKSpec
+    extends 
JdbcDurableStateStoreTCKSpec(ConfigFactory.load("mariadb-application.conf"), 
MariaDB)
+
+object MySQLDurableStateStoreTCKSpec {
+  // mysql-application.conf does not configure the durable state plugin, so 
add it here
+  val config = ConfigFactory
+    .parseString("""pekko.persistence.state.plugin = 
"jdbc-durable-state-store"""")
+    .withFallback(ConfigFactory.load("mysql-application.conf"))
+}
+
+class MySQLDurableStateStoreTCKSpec
+    extends JdbcDurableStateStoreTCKSpec(MySQLDurableStateStoreTCKSpec.config, 
MySQL)
+
+object OracleDurableStateStoreTCKSpec {
+  // oracle-application.conf does not configure the durable state plugin, so 
add it here
+  val config = ConfigFactory
+    .parseString("""pekko.persistence.state.plugin = 
"jdbc-durable-state-store"""")
+    .withFallback(ConfigFactory.load("oracle-application.conf"))
+}
+
+class OracleDurableStateStoreTCKSpec
+    extends 
JdbcDurableStateStoreTCKSpec(OracleDurableStateStoreTCKSpec.config, Oracle)
+
+object SqlServerDurableStateStoreTCKSpec {
+  // sqlserver-application.conf does not configure the durable state plugin, 
so add it here
+  val config = ConfigFactory
+    .parseString("""pekko.persistence.state.plugin = 
"jdbc-durable-state-store"""")
+    .withFallback(ConfigFactory.load("sqlserver-application.conf"))
+}
+
+class SqlServerDurableStateStoreTCKSpec
+    extends 
JdbcDurableStateStoreTCKSpec(SqlServerDurableStateStoreTCKSpec.config, 
SqlServer)


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to