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

imbajin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hugegraph-toolchain.git

commit f0c5b11d39f628d8910b0d19d8b0b5b8a1b01597
Author: dark <[email protected]>
AuthorDate: Fri Aug 14 12:39:02 2026 +0800

    test(hubble): cover sample and graph API contracts
---
 .../hugegraph/unit/SampleGraphControllerTest.java  | 165 +++++++++++++++++++++
 .../hubble-fe/src/accessible-click-actions.test.js |  23 ++-
 .../hubble-fe/src/api/manage-contract.test.js      |  11 ++
 hugegraph-hubble/hubble-fe/src/api/manage.js       |   4 +-
 4 files changed, 197 insertions(+), 6 deletions(-)

diff --git 
a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/unit/SampleGraphControllerTest.java
 
b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/unit/SampleGraphControllerTest.java
index 12a28d32a..2a58a5725 100644
--- 
a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/unit/SampleGraphControllerTest.java
+++ 
b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/unit/SampleGraphControllerTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.hugegraph.unit;
 
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.Map;
 
 import org.junit.Test;
@@ -28,6 +30,9 @@ import org.apache.hugegraph.api.gremlin.GremlinRequest;
 import org.apache.hugegraph.driver.GremlinManager;
 import org.apache.hugegraph.driver.HugeClient;
 import org.apache.hugegraph.driver.SchemaManager;
+import org.apache.hugegraph.exception.ExternalException;
+import org.apache.hugegraph.structure.schema.EdgeLabel;
+import org.apache.hugegraph.structure.schema.PropertyKey;
 import org.apache.hugegraph.structure.schema.VertexLabel;
 import org.apache.hugegraph.testutil.Assert;
 
@@ -158,6 +163,166 @@ public class SampleGraphControllerTest {
         Assert.assertEquals(15, result.get("edges"));
     }
 
+    @Test
+    public void testRejectsIncompatibleRankSchemaBeforeMutation() {
+        HugeClient client = Mockito.mock(HugeClient.class);
+        GremlinManager gremlin = Mockito.mock(GremlinManager.class);
+        SchemaManager schema = Mockito.mock(SchemaManager.class,
+                                            Mockito.RETURNS_DEEP_STUBS);
+        PropertyKey name = new PropertyKey.BuilderImpl("name", schema)
+                           .asText().build();
+        VertexLabel person = new VertexLabel.BuilderImpl("person", schema)
+                             .properties("name", "age", "city")
+                             .primaryKeys("name")
+                             .nullableKeys("age", "city")
+                             .build();
+        Mockito.when(client.gremlin()).thenReturn(gremlin);
+        Mockito.when(client.schema()).thenReturn(schema);
+        Mockito.when(schema.getPropertyKeys()).thenReturn(
+                Collections.singletonList(name));
+        Mockito.when(schema.getVertexLabels()).thenReturn(
+                Collections.singletonList(person));
+        
Mockito.when(schema.getEdgeLabels()).thenReturn(Collections.emptyList());
+        SampleGraphController controller = new TestController(client);
+
+        ExternalException error = (ExternalException) Assert.assertThrows(
+                ExternalException.class,
+                () -> controller.load("DEFAULT", "hugegraph", "rank"));
+
+        Assert.assertEquals("graph.sample.schema-incompatible",
+                            error.getMessage());
+        Assert.assertEquals("rank", error.args()[0]);
+        Assert.assertEquals("vertex label", error.args()[1]);
+        Assert.assertEquals("person", error.args()[2]);
+        Mockito.verify(schema, Mockito.never())
+               .propertyKey(Mockito.anyString());
+        Mockito.verify(schema, Mockito.never())
+               .vertexLabel(Mockito.anyString());
+        Mockito.verify(schema, Mockito.never())
+               .edgeLabel(Mockito.anyString());
+        Mockito.verify(gremlin, Mockito.never()).gremlin(Mockito.anyString());
+    }
+
+    @Test
+    public void testCompletesCompatiblePartialRankSchema() {
+        HugeClient client = Mockito.mock(HugeClient.class);
+        GremlinManager gremlin = Mockito.mock(GremlinManager.class);
+        SchemaManager schema = Mockito.mock(SchemaManager.class,
+                                            Mockito.RETURNS_DEEP_STUBS);
+        PropertyKey name = new PropertyKey.BuilderImpl("name", schema)
+                           .asText().build();
+        VertexLabel person = new VertexLabel.BuilderImpl("person", schema)
+                             .useCustomizeStringId()
+                             .properties("name")
+                             .build();
+        Mockito.when(client.gremlin()).thenReturn(gremlin);
+        Mockito.when(client.schema()).thenReturn(schema);
+        Mockito.when(schema.getPropertyKeys()).thenReturn(
+                Collections.singletonList(name));
+        Mockito.when(schema.getVertexLabels()).thenReturn(
+                Collections.singletonList(person));
+        
Mockito.when(schema.getEdgeLabels()).thenReturn(Collections.emptyList());
+        Mockito.when(gremlin.gremlin(Mockito.anyString()))
+               .thenAnswer(invocation -> new GremlinRequest.Builder(
+                       invocation.getArgument(0), gremlin));
+        SampleGraphController controller = new TestController(client);
+
+        Map<String, Object> result = controller.load("DEFAULT", "hugegraph",
+                                                     "rank");
+
+        Mockito.verify(schema, Mockito.never()).propertyKey("name");
+        Mockito.verify(schema, Mockito.never()).vertexLabel("person");
+        Mockito.verify(schema).vertexLabel("movie");
+        Mockito.verify(schema).edgeLabel("follow");
+        Mockito.verify(schema).edgeLabel("like");
+        Mockito.verify(schema).edgeLabel("directedBy");
+        Mockito.verify(gremlin).execute(Mockito.any(GremlinRequest.class));
+        Assert.assertEquals("rank", result.get("dataset"));
+        Assert.assertEquals(true, result.get("idempotent"));
+    }
+
+    @Test
+    public void testRejectsIncompatibleEdgeBeforeMutation() {
+        HugeClient client = Mockito.mock(HugeClient.class);
+        GremlinManager gremlin = Mockito.mock(GremlinManager.class);
+        SchemaManager schema = Mockito.mock(SchemaManager.class,
+                                            Mockito.RETURNS_DEEP_STUBS);
+        PropertyKey name = new PropertyKey.BuilderImpl("name", schema)
+                           .asText().build();
+        VertexLabel person = new VertexLabel.BuilderImpl("person", schema)
+                             .useCustomizeStringId()
+                             .properties("name")
+                             .build();
+        VertexLabel movie = new VertexLabel.BuilderImpl("movie", schema)
+                            .useCustomizeStringId()
+                            .properties("name")
+                            .build();
+        EdgeLabel follow = new EdgeLabel.BuilderImpl("follow", schema)
+                           .sourceLabel("person")
+                           .targetLabel("person")
+                           .ttl(1L)
+                           .build();
+        Mockito.when(client.gremlin()).thenReturn(gremlin);
+        Mockito.when(client.schema()).thenReturn(schema);
+        Mockito.when(schema.getPropertyKeys()).thenReturn(
+                Collections.singletonList(name));
+        Mockito.when(schema.getVertexLabels()).thenReturn(
+                Arrays.asList(person, movie));
+        Mockito.when(schema.getEdgeLabels()).thenReturn(
+                Collections.singletonList(follow));
+        SampleGraphController controller = new TestController(client);
+
+        ExternalException error = (ExternalException) Assert.assertThrows(
+                ExternalException.class,
+                () -> controller.load("DEFAULT", "hugegraph", "rank"));
+
+        Assert.assertEquals("edge label", error.args()[1]);
+        Assert.assertEquals("follow", error.args()[2]);
+        Mockito.verify(schema, Mockito.never())
+               .propertyKey(Mockito.anyString());
+        Mockito.verify(schema, Mockito.never())
+               .vertexLabel(Mockito.anyString());
+        Mockito.verify(schema, Mockito.never())
+               .edgeLabel(Mockito.anyString());
+        Mockito.verify(gremlin, Mockito.never()).gremlin(Mockito.anyString());
+    }
+
+    @Test
+    public void testRejectsExtraRequiredVertexPropertyBeforeMutation() {
+        HugeClient client = Mockito.mock(HugeClient.class);
+        GremlinManager gremlin = Mockito.mock(GremlinManager.class);
+        SchemaManager schema = Mockito.mock(SchemaManager.class,
+                                            Mockito.RETURNS_DEEP_STUBS);
+        PropertyKey name = new PropertyKey.BuilderImpl("name", schema)
+                           .asText().build();
+        VertexLabel person = new VertexLabel.BuilderImpl("person", schema)
+                             .useCustomizeStringId()
+                             .properties("name", "tenant")
+                             .build();
+        Mockito.when(client.gremlin()).thenReturn(gremlin);
+        Mockito.when(client.schema()).thenReturn(schema);
+        Mockito.when(schema.getPropertyKeys()).thenReturn(
+                Collections.singletonList(name));
+        Mockito.when(schema.getVertexLabels()).thenReturn(
+                Collections.singletonList(person));
+        
Mockito.when(schema.getEdgeLabels()).thenReturn(Collections.emptyList());
+        SampleGraphController controller = new TestController(client);
+
+        ExternalException error = (ExternalException) Assert.assertThrows(
+                ExternalException.class,
+                () -> controller.load("DEFAULT", "hugegraph", "rank"));
+
+        Assert.assertEquals("vertex label", error.args()[1]);
+        Assert.assertEquals("person", error.args()[2]);
+        Mockito.verify(schema, Mockito.never())
+               .propertyKey(Mockito.anyString());
+        Mockito.verify(schema, Mockito.never())
+               .vertexLabel(Mockito.anyString());
+        Mockito.verify(schema, Mockito.never())
+               .edgeLabel(Mockito.anyString());
+        Mockito.verify(gremlin, Mockito.never()).gremlin(Mockito.anyString());
+    }
+
     private static int occurrences(String value, String token) {
         int count = 0;
         int offset = 0;
diff --git a/hugegraph-hubble/hubble-fe/src/accessible-click-actions.test.js 
b/hugegraph-hubble/hubble-fe/src/accessible-click-actions.test.js
index 5e8b5465a..7e0cf52d5 100644
--- a/hugegraph-hubble/hubble-fe/src/accessible-click-actions.test.js
+++ b/hugegraph-hubble/hubble-fe/src/accessible-click-actions.test.js
@@ -77,8 +77,23 @@ test('task-name columns constrain long values and preserve 
the full name', () =>
         'utf8'
     );
 
-    [taskList, asyncList].forEach(source => {
-        
expect(source).toMatch(/dataIndex:\s*['"]task_name['"][\s\S]{0,200}width:\s*\d+/);
-        expect(source).toMatch(/ellipsis=\{\{tooltip:\s*task_name\}\}/);
-    });
+    const taskWidthConfig = taskList.match(
+        /const TASK_COLUMN_WIDTHS = \{([\s\S]*?)\};/
+    )?.[1] || '';
+    const taskNameWidth = Number(
+        taskWidthConfig.match(/\bname:\s*(\d+)/)?.[1]
+    );
+
+    expect(taskList).toMatch(
+        
/dataIndex:\s*['"]task_name['"][\s\S]{0,200}width:\s*TASK_COLUMN_WIDTHS\.name/
+    );
+    expect(taskNameWidth).toBeGreaterThan(0);
+    expect(taskNameWidth).toBeGreaterThanOrEqual(120);
+    expect(taskNameWidth).toBeLessThanOrEqual(220);
+    expect(taskList).toMatch(/ellipsis=\{\{tooltip:\s*task_name\}\}/);
+
+    expect(asyncList).toMatch(
+        /dataIndex:\s*['"]task_name['"][\s\S]{0,200}width:\s*\d+/
+    );
+    expect(asyncList).toMatch(/ellipsis=\{\{tooltip:\s*task_name\}\}/);
 });
diff --git a/hugegraph-hubble/hubble-fe/src/api/manage-contract.test.js 
b/hugegraph-hubble/hubble-fe/src/api/manage-contract.test.js
index daf0a7c49..75016fec7 100644
--- a/hugegraph-hubble/hubble-fe/src/api/manage-contract.test.js
+++ b/hugegraph-hubble/hubble-fe/src/api/manage-contract.test.js
@@ -100,6 +100,17 @@ test('keeps task-run error ownership controls out of query 
parameters', () => {
     );
 });
 
+test('forwards task-detail error ownership controls', () => {
+    const config = {suppressBusinessErrorToast: true};
+
+    manage.getTaskDetail('42', config);
+
+    expect(request.get).toHaveBeenCalledWith(
+        '/ingest/tasks/42',
+        config
+    );
+});
+
 test('reads the default graph from the canonical route', () => {
     manage.getDefaultGraph('DEFAULT');
     expect(request.get).toHaveBeenCalledWith(
diff --git a/hugegraph-hubble/hubble-fe/src/api/manage.js 
b/hugegraph-hubble/hubble-fe/src/api/manage.js
index 02c144074..f9b06d129 100644
--- a/hugegraph-hubble/hubble-fe/src/api/manage.js
+++ b/hugegraph-hubble/hubble-fe/src/api/manage.js
@@ -331,8 +331,8 @@ const getTaskList = params => {
     return request.get(`${testhost}/tasks/list`, {params});
 };
 
-const getTaskDetail = id => {
-    return request.get(`${testhost}/tasks/${id}`);
+const getTaskDetail = (id, config) => {
+    return request.get(`${testhost}/tasks/${id}`, config);
 };
 
 const deleteTask = id => {

Reply via email to