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-7305-ad638ae035579025f48697089ad9bbc3a50d2c08
in repository https://gitbox.apache.org/repos/asf/texera.git

commit ae1d1d88e6d6a3f3559eb6512a679959da152421
Author: Xinyuan Lin <[email protected]>
AuthorDate: Tue Aug 4 17:43:54 2026 -0700

    test(amber): cover ProjectResource's colour and per-project workflow APIs 
(#7305)
    
    ### What changes were proposed in this PR?
    
    `ProjectResourceSpec` exercised the create / rename / describe / add /
    remove / delete paths, but three of the resource's methods were never
    called by it. Six tests for those:
    
    **`updateProjectColor`** — the only one with real validation:
    
    ```scala
    if (colorHex == null || colorHex.length != 6 && colorHex.length != 3 || 
!colorHex.matches(...))
    ```
    
    The guard checks length **and** hex-ness, so the rejection test covers
    both kinds of bad input: a length-only check would let `"GGGGGG"`
    through, and a regex-only check would let `""` through. It also asserts
    a rejected update leaves the previously stored colour intact, and that
    the null check precedes the length read — without it a null colour is an
    NPE rather than a 400. Both the 3- and 6-digit forms are accepted, and
    the value is stored verbatim rather than expanded.
    
    **`deleteProjectColor`** — clears the value.
    
    **`listProjectWorkflows`** — filters by project id. The test seeds
    **two** projects each holding one workflow, so a filter that ignored the
    pid would return both and fail; plus a project holding none returns
    empty.
    
    These are additions to the spec's existing `MockTexeraDB` fixture, not
    new infrastructure.
    
    For transparency on scope: `WorkflowVersionResource` (12 missed, 88.4%)
    was looked at as a companion for this PR and deliberately left out.
    Every one of its methods is already exercised by its spec, so the
    residue is branch-level and did not look reachable without padding.
    
    No production file is touched.
    
    ### Any related issues, documentation, discussions?
    
    Closes #7302
    
    ### How was this PR tested?
    
    Six new tests, run as the whole spec so the pre-existing cases are
    proven intact — 16 tests, Java 17:
    
    ```
    sbt "WorkflowExecutionService/testOnly 
org.apache.texera.web.resource.dashboard.user.project.ProjectResourceSpec"
    ```
    
    ```
    [info] Tests: succeeded 16, failed 0, canceled 0, ignored 0, pending 0
    [info] All tests passed.
    ```
    
    `Test/scalafmtCheck` and `Test/scalafix --check` both `[success]`.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Opus 5)
    
    ---------
    
    Signed-off-by: Xinyuan Lin <[email protected]>
    Co-authored-by: Copilot Autofix powered by AI 
<[email protected]>
---
 .../user/project/ProjectResourceSpec.scala         | 69 ++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git 
a/amber/src/test/scala/org/apache/texera/web/resource/dashboard/user/project/ProjectResourceSpec.scala
 
b/amber/src/test/scala/org/apache/texera/web/resource/dashboard/user/project/ProjectResourceSpec.scala
index e05a6fb63d..f97d3d4519 100644
--- 
a/amber/src/test/scala/org/apache/texera/web/resource/dashboard/user/project/ProjectResourceSpec.scala
+++ 
b/amber/src/test/scala/org/apache/texera/web/resource/dashboard/user/project/ProjectResourceSpec.scala
@@ -254,6 +254,75 @@ class ProjectResourceSpec
     workflowOfProjectCount(wid, pid) shouldBe 0
   }
 
+  it should "accept both 3- and 6-digit hex colours and persist the last one" 
in {
+    val pid = resource.createProject(session(owner), "p").getPid
+
+    resource.updateProjectColor(pid, "AABBCC", session(owner))
+    resource.getProject(pid).getColor shouldBe "AABBCC"
+
+    // The shorthand form is legal too, and the value is stored verbatim 
rather than expanded.
+    resource.updateProjectColor(pid, "f0a", session(owner))
+    resource.getProject(pid).getColor shouldBe "f0a"
+  }
+
+  it should "reject colours that are not 3 or 6 hex digits, leaving the stored 
one intact" in {
+    val pid = resource.createProject(session(owner), "p").getPid
+    resource.updateProjectColor(pid, "123456", session(owner))
+
+    // Wrong length, and a right-length value with a non-hex digit: this 
exercises both the
+    // length and hex-digit validation branches in updateProjectColor.
+    Seq("12345", "1234567", "GGGGGG", "12G", "").foreach { bad =>
+      withClue(s"colour '$bad': ") {
+        assertThrows[BadRequestException] {
+          resource.updateProjectColor(pid, bad, session(owner))
+        }
+      }
+    }
+
+    resource.getProject(pid).getColor shouldBe "123456"
+  }
+
+  it should "reject a null colour before dereferencing it" in {
+    val pid = resource.createProject(session(owner), "p").getPid
+
+    // The null check has to come first; without it the length read is an NPE 
rather than a 400.
+    assertThrows[BadRequestException] {
+      resource.updateProjectColor(pid, null, session(owner))
+    }
+  }
+
+  it should "clear a project's colour" in {
+    val pid = resource.createProject(session(owner), "p").getPid
+    resource.updateProjectColor(pid, "ABCDEF", session(owner))
+
+    resource.deleteProjectColor(pid)
+
+    resource.getProject(pid).getColor shouldBe null
+  }
+
+  it should "list only the workflows belonging to the given project" in {
+    val pid = resource.createProject(session(owner), "p").getPid
+    val other = resource.createProject(session(owner), "other").getPid
+    val inProject = seedWorkflow(ownerUid)
+    val elsewhere = seedWorkflow(ownerUid)
+    resource.addWorkflowToProject(pid, inProject, session(owner))
+    resource.addWorkflowToProject(other, elsewhere, session(owner))
+
+    // Two projects each holding one workflow, so a filter that ignored the 
pid would return both.
+    resource.listProjectWorkflows(pid, session(owner)).map(_.workflow.getWid) 
shouldBe List(
+      inProject
+    )
+    resource.listProjectWorkflows(other, 
session(owner)).map(_.workflow.getWid) shouldBe List(
+      elsewhere
+    )
+  }
+
+  it should "return no workflows for a project that holds none" in {
+    val pid = resource.createProject(session(owner), "empty").getPid
+
+    resource.listProjectWorkflows(pid, session(owner)) shouldBe empty
+  }
+
   it should "delete a project" in {
     val pid = resource.createProject(session(owner), "doomed").getPid
     resource.getProject(pid) should not be null

Reply via email to