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

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-7181-4de3605d0784abb6ec9e99187d0a181acc0aee71
in repository https://gitbox.apache.org/repos/asf/texera.git

commit 01071832cd2e64d45b54f46ffd193d1ffa359299
Author: Meng Wang <[email protected]>
AuthorDate: Fri Jul 31 17:21:43 2026 -0700

    test(amber): add unit test coverage for UserConfigResource (#7181)
    
    ### What changes were proposed in this PR?
    
    Adds `UserConfigResourceSpec` for `UserConfigResource` (previously ~0%
    coverage,
    no spec) — the small jOOQ-backed per-user key/value config store. The
    suite runs
    against embedded Postgres via `MockTexeraDB` and seeds users through the
    generated `UserDao`.
    
    11 tests, each starting from a clean slate (so they are
    order-independent),
    cover the full CRUD round-trip through the resource's own API:
    
    - `getEntry` — returns `null` for a key that was never set.
    - `setEntry` — stores a value that `getEntry` then returns; overwriting
    the same key updates the value (and leaves a single entry, not two).
    - `getAllDict` — empty map when the user has no entries; every key/value
    pair after N sets; and it is **scoped to the requesting user** (a second
    user's entries are not returned, and the same key set by two users stays
    independent).
    - `deleteEntry` — removes the key so `getEntry`/`getAllDict` no longer
    show it; a delete of a non-existent key is a no-op (no error).
    - Key validation — `getEntry` / `setEntry` / `deleteEntry` reject a
    blank key with a `BadRequestException`.
    
    No production code was changed.
    
    ### Any related issues, documentation, discussions?
    
    Closes #7176
    
    ### How was this PR tested?
    
    New unit tests, run locally against embedded Postgres:
    
    ```
    sbt "WorkflowExecutionService/testOnly 
org.apache.texera.web.resource.UserConfigResourceSpec"
    # Tests: succeeded 11, failed 0
    sbt "WorkflowExecutionService/Test/scalafmtCheck" 
"WorkflowExecutionService/Test/scalafix --check"   # clean
    ```
    
    The failure path was verified by deliberately breaking an assertion and
    confirming the suite goes red.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Opus 4.8 [1M context])
---
 .../web/resource/UserConfigResourceSpec.scala      | 153 +++++++++++++++++++++
 1 file changed, 153 insertions(+)

diff --git 
a/amber/src/test/scala/org/apache/texera/web/resource/UserConfigResourceSpec.scala
 
b/amber/src/test/scala/org/apache/texera/web/resource/UserConfigResourceSpec.scala
new file mode 100644
index 0000000000..0ee7158077
--- /dev/null
+++ 
b/amber/src/test/scala/org/apache/texera/web/resource/UserConfigResourceSpec.scala
@@ -0,0 +1,153 @@
+/*
+ * 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.texera.web.resource
+
+import org.apache.texera.auth.SessionUser
+import org.apache.texera.dao.MockTexeraDB
+import org.apache.texera.dao.jooq.generated.Tables.{USER, USER_CONFIG}
+import org.apache.texera.dao.jooq.generated.tables.daos.UserDao
+import org.apache.texera.dao.jooq.generated.tables.pojos.User
+import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach}
+import org.scalatest.flatspec.AnyFlatSpec
+
+import javax.ws.rs.BadRequestException
+
+class UserConfigResourceSpec
+    extends AnyFlatSpec
+    with BeforeAndAfterAll
+    with BeforeAndAfterEach
+    with MockTexeraDB {
+
+  private val testUid = 4000 + scala.util.Random.nextInt(1000)
+  private val otherUid = testUid + 1
+
+  private var userDao: UserDao = _
+  private var testUser: User = _
+  private var otherUser: User = _
+  private val resource = new UserConfigResource
+
+  override protected def beforeAll(): Unit = {
+    initializeDBAndReplaceDSLContext()
+    userDao = new UserDao(getDSLContext.configuration())
+  }
+
+  override protected def afterAll(): Unit = shutdownDB()
+
+  // Every test starts from a clean slate for the ids under test, then seeds 
only
+  // the two users it needs, so tests are order-independent.
+  override protected def beforeEach(): Unit = resetFixtures()
+
+  private def resetFixtures(): Unit = {
+    getDSLContext.deleteFrom(USER_CONFIG).where(USER_CONFIG.UID.in(testUid, 
otherUid)).execute()
+    getDSLContext.deleteFrom(USER).where(USER.UID.in(testUid, 
otherUid)).execute()
+
+    testUser = newUser(testUid, "config_user", "[email protected]")
+    otherUser = newUser(otherUid, "other_user", "[email protected]")
+  }
+
+  private def newUser(uid: Int, name: String, email: String): User = {
+    val user = new User
+    user.setUid(uid)
+    user.setName(name)
+    user.setEmail(email)
+    user.setPassword("password")
+    userDao.insert(user)
+    user
+  }
+
+  private def session(user: User): SessionUser = new SessionUser(user)
+
+  // ─── getEntry / setEntry 
───────────────────────────────────────────────────
+
+  "getEntry" should "return null for a key that was never set" in {
+    assert(resource.getEntry("missing", session(testUser)) == null)
+  }
+
+  "setEntry" should "store a value that getEntry then returns" in {
+    resource.setEntry("theme", "dark", session(testUser))
+    assert(resource.getEntry("theme", session(testUser)) == "dark")
+  }
+
+  it should "overwrite the value when the same key is set again" in {
+    resource.setEntry("theme", "dark", session(testUser))
+    resource.setEntry("theme", "light", session(testUser))
+    assert(resource.getEntry("theme", session(testUser)) == "light")
+    // still a single entry, not two
+    assert(resource.getAllDict(session(testUser)).size == 1)
+  }
+
+  // ─── getAllDict 
────────────────────────────────────────────────────────────
+
+  "getAllDict" should "return an empty map when the user has no entries" in {
+    assert(resource.getAllDict(session(testUser)).isEmpty)
+  }
+
+  it should "return every key/value pair the user has set" in {
+    resource.setEntry("a", "1", session(testUser))
+    resource.setEntry("b", "2", session(testUser))
+    resource.setEntry("c", "3", session(testUser))
+
+    val dict = resource.getAllDict(session(testUser))
+    assert(dict == Map("a" -> "1", "b" -> "2", "c" -> "3"))
+  }
+
+  it should "be scoped to the requesting user (another user's entries are not 
returned)" in {
+    resource.setEntry("shared", "mine", session(testUser))
+    resource.setEntry("shared", "theirs", session(otherUser))
+    resource.setEntry("other-only", "x", session(otherUser))
+
+    assert(resource.getAllDict(session(testUser)) == Map("shared" -> "mine"))
+    assert(resource.getEntry("other-only", session(testUser)) == null)
+    assert(
+      resource.getAllDict(session(otherUser)) == Map("shared" -> "theirs", 
"other-only" -> "x")
+    )
+  }
+
+  // ─── deleteEntry 
───────────────────────────────────────────────────────────
+
+  "deleteEntry" should "remove the key so getEntry and getAllDict no longer 
show it" in {
+    resource.setEntry("k1", "v1", session(testUser))
+    resource.setEntry("k2", "v2", session(testUser))
+
+    resource.deleteEntry("k1", session(testUser))
+
+    assert(resource.getEntry("k1", session(testUser)) == null)
+    assert(resource.getAllDict(session(testUser)) == Map("k2" -> "v2"))
+  }
+
+  it should "be a no-op (no error) when the key does not exist" in {
+    resource.deleteEntry("never-set", session(testUser))
+    assert(resource.getAllDict(session(testUser)).isEmpty)
+  }
+
+  // ─── key validation 
────────────────────────────────────────────────────────
+
+  "getEntry" should "reject a blank key with a BadRequestException" in {
+    assertThrows[BadRequestException](resource.getEntry("  ", 
session(testUser)))
+  }
+
+  "setEntry" should "reject a blank key with a BadRequestException" in {
+    assertThrows[BadRequestException](resource.setEntry("", "v", 
session(testUser)))
+  }
+
+  "deleteEntry" should "reject a blank key with a BadRequestException" in {
+    assertThrows[BadRequestException](resource.deleteEntry("", 
session(testUser)))
+  }
+}

Reply via email to