You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@texera.apache.org by gi...@apache.org on 2026/08/08 07:06:47 UTC

(texera) branch gh-readonly-queue/main/pr-7399-a38bc6659d314b5cbbc32a2233934bb3e0d4ce37 created (now c5c2c6f8c2)

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

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


      at c5c2c6f8c2 test(amber): add unit tests cover ProjectSearchQueryBuilder (#7399)

This branch includes the following new commits:

     new c5c2c6f8c2 test(amber): add unit tests cover ProjectSearchQueryBuilder (#7399)

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(texera) 01/01: test(amber): add unit tests cover ProjectSearchQueryBuilder (#7399)

Posted by gi...@apache.org.
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-7399-a38bc6659d314b5cbbc32a2233934bb3e0d4ce37
in repository https://gitbox.apache.org/repos/asf/texera.git

commit c5c2c6f8c2aeb746929a5f56b9d36eaa0373848e
Author: Eugene Gu <eu...@outlook.com>
AuthorDate: Sat Aug 8 00:06:27 2026 -0700

    test(amber): add unit tests cover ProjectSearchQueryBuilder (#7399)
    
    ### What changes were proposed in this PR?
    
    Adds `ProjectSearchQueryBuilderSpec`, a connection-free unit spec for
    the project arm of the unified dashboard search.
    `ProjectSearchQueryBuilder` was the only search query builder without
    tests — `WorkflowSearchQueryBuilderSpec` already exists, and
    `DatasetSearchQueryBuilder` is driven from `DatasetResourceSpec`.
    
    The spec follows the same conventions as the sibling
    `WorkflowSearchQueryBuilderSpec` (in-memory jOOQ records rendered with
    the Postgres dialect, no database connection) and covers the two members
    the object widens from the trait's `protected` to public:
    
    **`toEntryImpl` — the record-to-DTO mapping (4 tests):** copies every
    `PROJECT` column into the `Project` POJO with distinct fixture values so
    a wrong-column read cannot pass; passes NULL description/color through
    (the only two nullable project columns); tags the entry `resourceType ==
    "project"` with the `workflow`/`dataset` payload slots `None` (the
    dashboard dispatch matches on this value with no default branch, so
    drift is a runtime `MatchError`); and produces an identical entry
    regardless of the caller's uid (the project arm computes no ownership
    flag).
    
    **`mappedResourceSchema` — the projection the union and dispatch depend
    on (5 tests):** pins the inline `'project'` resourceType literal; pins
    that `PROJECT.CREATION_TIME` is aliased as both the creation and
    last-modified time — a deliberate alias (the project table has no
    last-modified column) that keeps projects sortable by edit time instead
    of NULL-sinking; and pins the color, pid/ownerId, and name/description
    projection slots.
    
    `constructFromClause`, `constructWhereClause`, and `getGroupByFields`
    stay `override protected` (Scala `protected` grants no same-package
    access), so they are intentionally out of scope.
    
    Every test was mutation-checked: 8 distinct hand-applied mutations of
    the production object (literal changes, column swaps, dropped alias,
    empty-POJO copy, `project = None`) each fail the intended test, with
    production sources restored afterwards. No production file is touched.
    
    ### Any related issues, documentation, discussions?
    
    Closes #7398
    
    ### How was this PR tested?
    
    Nine new tests; the spec is the only change:
    
    ```
    sbt "WorkflowExecutionService/testOnly org.apache.texera.web.resource.dashboard.ProjectSearchQueryBuilderSpec"
    ```
    
    ```
    [info] Tests: succeeded 9, failed 0, canceled 0, ignored 0, pending 0
    [info] All tests passed.
    ```
    
    The file is formatted with the project's scalafmt (no diff on re-run).
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Fable 5)
---
 .../dashboard/ProjectSearchQueryBuilderSpec.scala  | 141 +++++++++++++++++++++
 1 file changed, 141 insertions(+)

diff --git a/amber/src/test/scala/org/apache/texera/web/resource/dashboard/ProjectSearchQueryBuilderSpec.scala b/amber/src/test/scala/org/apache/texera/web/resource/dashboard/ProjectSearchQueryBuilderSpec.scala
new file mode 100644
index 0000000000..0a05846dda
--- /dev/null
+++ b/amber/src/test/scala/org/apache/texera/web/resource/dashboard/ProjectSearchQueryBuilderSpec.scala
@@ -0,0 +1,141 @@
+/*
+ * 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.dashboard
+
+import org.apache.texera.dao.jooq.generated.Tables.PROJECT
+import org.apache.texera.dao.jooq.generated.tables.pojos.Project
+import org.jooq.impl.{DSL => JDSL}
+import org.jooq.{Record, SQLDialect}
+
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.matchers.should.Matchers
+
+import java.sql.Timestamp
+
+class ProjectSearchQueryBuilderSpec extends AnyFlatSpec with Matchers {
+
+  private val ctx = JDSL.using(SQLDialect.POSTGRES)
+
+  private val ownerUid: Integer = Integer.valueOf(42)
+  private val callerUid: Integer = Integer.valueOf(43)
+  private val pid: Integer = Integer.valueOf(7)
+  private val createdAt = new Timestamp(1700000000123L)
+
+  // In-memory record shaped like the one toEntryImpl receives (keyed by the
+  // original PROJECT fields). Values are distinct so a wrong-column read fails.
+  private def translatedRecord(
+      description: String = "proj-description",
+      color: String = "aabbcc"
+  ): Record = {
+    val record = ctx.newRecord(
+      PROJECT.PID,
+      PROJECT.NAME,
+      PROJECT.DESCRIPTION,
+      PROJECT.OWNER_ID,
+      PROJECT.CREATION_TIME,
+      PROJECT.COLOR
+    )
+    record.set(PROJECT.PID, pid)
+    record.set(PROJECT.NAME, "proj-name")
+    record.set(PROJECT.DESCRIPTION, description)
+    record.set(PROJECT.OWNER_ID, ownerUid)
+    record.set(PROJECT.CREATION_TIME, createdAt)
+    record.set(PROJECT.COLOR, color)
+    record
+  }
+
+  private def projectOf(record: Record, uid: Integer): Project =
+    ProjectSearchQueryBuilder.toEntryImpl(uid, record).project.get
+
+  "toEntryImpl" should "copy every project column into the POJO" in {
+    val p = projectOf(translatedRecord(), ownerUid)
+    p.getPid shouldBe pid
+    p.getName shouldBe "proj-name"
+    p.getDescription shouldBe "proj-description"
+    p.getOwnerId shouldBe ownerUid
+    p.getCreationTime shouldBe createdAt
+    p.getColor shouldBe "aabbcc"
+  }
+
+  it should "pass a NULL description and a NULL color through as null" in {
+    // description and color are the only nullable project columns.
+    val p = projectOf(translatedRecord(description = null, color = null), ownerUid)
+    p.getDescription shouldBe null
+    p.getColor shouldBe null
+    p.getPid shouldBe pid
+  }
+
+  it should "tag the entry as a project and leave the other payload slots empty" in {
+    // searchAllResources matches on resourceType with no default branch, so a
+    // wrong tag is a runtime MatchError.
+    val entry = ProjectSearchQueryBuilder.toEntryImpl(ownerUid, translatedRecord())
+    entry.resourceType shouldBe "project"
+    entry.project should not be None
+    entry.workflow shouldBe None
+    entry.dataset shouldBe None
+  }
+
+  it should "produce the same entry regardless of the caller's uid" in {
+    // Unlike the workflow arm there is no ownership flag to compute.
+    val record = translatedRecord()
+    ProjectSearchQueryBuilder.toEntryImpl(ownerUid, record) shouldBe
+      ProjectSearchQueryBuilder.toEntryImpl(callerUid, record)
+  }
+
+  // An aliased field renders as the bare alias on its own, so render a SELECT.
+  private lazy val renderedSchema: String = ctx.renderInlined(
+    JDSL.select(ProjectSearchQueryBuilder.mappedResourceSchema.allFields: _*)
+  )
+
+  "mappedResourceSchema" should "project the literal 'project' as the resourceType column" in {
+    renderedSchema should include("'project' as \"resourceType\"")
+  }
+
+  it should "alias PROJECT.CREATION_TIME as both the creation and last-modified time" in {
+    // Deliberate: the project table has no last-modified column. Without this
+    // alias projects would NULL-sink in every sort by edit time.
+    val creationTime = ctx.renderInlined(PROJECT.CREATION_TIME)
+    renderedSchema should include(s"""$creationTime as "resourceCreationTime"""")
+    renderedSchema should include(s"""$creationTime as "resourceLastModifiedTime"""")
+  }
+
+  it should "project PROJECT.COLOR as the color column" in {
+    // The frontend reads the project colour swatch from this alias.
+    val color = ctx.renderInlined(PROJECT.COLOR)
+    renderedSchema should include(s"""$color as "color"""")
+  }
+
+  it should "project the project id and owner through the shared slots" in {
+    // The owner rides the generic resourceOwnerId slot; the project-specific
+    // "owner_uid" slot stays NULL and is deliberately not pinned here.
+    renderedSchema should include(s"""${ctx.renderInlined(PROJECT.PID)} as "pid"""")
+    renderedSchema should include(
+      s"""${ctx.renderInlined(PROJECT.OWNER_ID)} as "resourceOwnerId""""
+    )
+  }
+
+  it should "project the name and description through the shared full-text slots" in {
+    // Also the two columns the keyword filter targets.
+    renderedSchema should include(s"""${ctx.renderInlined(PROJECT.NAME)} as "resourceName"""")
+    renderedSchema should include(
+      s"""${ctx.renderInlined(PROJECT.DESCRIPTION)} as "resourceDescription""""
+    )
+  }
+}