feat: expand graph lifecycle and data platform
This commit is contained in:
1 parent
00af885b1a
commit
15368f2779
50 files changed
+17461
-1040
No files matched your search
@@ -0,0 +1,150 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
buildGenericGraphQueryTemplates,
|
||||
buildSchemaGuidance,
|
||||
GENERIC_GRAPH_DEFAULT_CYPHER,
|
||||
GENERIC_GRAPH_RELATIONS_CYPHER,
|
||||
genericGraphQueryPlaceholder,
|
||||
schemaHasDeclaredTypes,
|
||||
schemaSupportsLegacyGraphProfile,
|
||||
} from "../src/schemaCapabilities.ts";
|
||||
|
||||
const genericSchema = {
|
||||
namespace: "human_flow_e2e_20260804_224229",
|
||||
version: "1.0.0",
|
||||
entity_types: {
|
||||
Place: { label: "地点", fields: { name: { type: "string" } } },
|
||||
Hotel: { label: "酒店", fields: { name: { type: "string" } } },
|
||||
},
|
||||
relation_types: {
|
||||
LOCATED_IN: { from: "Place", to: "Place", properties: {} },
|
||||
NEAR: { from: "Hotel", to: "Place", properties: {} },
|
||||
},
|
||||
};
|
||||
|
||||
test("generic schemas do not show fixed-route guidance", () => {
|
||||
assert.deepEqual(buildSchemaGuidance(genericSchema), []);
|
||||
assert.equal(schemaHasDeclaredTypes(genericSchema), true);
|
||||
assert.equal(schemaSupportsLegacyGraphProfile(genericSchema, "travel"), false);
|
||||
assert.equal(schemaSupportsLegacyGraphProfile(genericSchema, "travel_item"), false);
|
||||
assert.equal(schemaSupportsLegacyGraphProfile(genericSchema, "libo"), false);
|
||||
});
|
||||
|
||||
test("generic default query preserves isolated nodes when the schema has no relations", () => {
|
||||
const zeroRelationSchema = {
|
||||
namespace: "isolated_nodes",
|
||||
entity_types: {
|
||||
Place: { label: "地点", fields: { name: { type: "string" } } },
|
||||
},
|
||||
relation_types: {},
|
||||
};
|
||||
|
||||
assert.equal(schemaHasDeclaredTypes(zeroRelationSchema), true);
|
||||
assert.equal(
|
||||
GENERIC_GRAPH_DEFAULT_CYPHER,
|
||||
"MATCH (n) OPTIONAL MATCH (n)-[r]->(m) RETURN n,r,m LIMIT 2500",
|
||||
);
|
||||
assert.deepEqual(buildGenericGraphQueryTemplates(zeroRelationSchema).slice(0, 2), [
|
||||
{ label: "全部节点", cypher: "MATCH (n) RETURN n LIMIT 200" },
|
||||
{ label: "全部节点关系", cypher: GENERIC_GRAPH_RELATIONS_CYPHER },
|
||||
]);
|
||||
});
|
||||
|
||||
test("Libo shortcuts require the specialized POI model shape", () => {
|
||||
assert.equal(schemaSupportsLegacyGraphProfile({
|
||||
entity_types: {
|
||||
Hotel: {},
|
||||
FoodPlace: {},
|
||||
TransitFacility: {},
|
||||
ScenicSpot: {},
|
||||
},
|
||||
relation_types: {},
|
||||
}, "libo"), true);
|
||||
});
|
||||
|
||||
test("travel guidance only includes capabilities declared by that schema", () => {
|
||||
const travelSchema = {
|
||||
design_principles: ["主干路线固定且有序。"],
|
||||
entity_types: {
|
||||
TourProduct: {},
|
||||
ProductDay: {},
|
||||
RouteStop: {},
|
||||
ScenicAttraction: {},
|
||||
TravelItem: {},
|
||||
ResourcePriceRule: {},
|
||||
},
|
||||
relation_types: {
|
||||
DAY_HAS_STOP: { from: "ProductDay", to: "RouteStop" },
|
||||
ROUTE_STOP_NEXT: { from: "RouteStop", to: "RouteStop" },
|
||||
STOP_VISITS_ATTRACTION: { from: "RouteStop", to: "ScenicAttraction" },
|
||||
STOP_USES_DEFAULT_RESOURCE: { from: "RouteStop", to: "TravelItem" },
|
||||
ATTRACTION_NEARBY_RESOURCE: { from: "ScenicAttraction", to: "Hotel" },
|
||||
},
|
||||
};
|
||||
|
||||
assert.deepEqual(buildSchemaGuidance(travelSchema), [
|
||||
"固定路线不可改",
|
||||
"RouteStop 串联景区",
|
||||
"默认资源挂停靠点",
|
||||
"可替换资源走 nearby",
|
||||
"价格统一用 ResourcePriceRule",
|
||||
]);
|
||||
assert.equal(schemaSupportsLegacyGraphProfile(travelSchema, "travel"), true);
|
||||
assert.equal(schemaSupportsLegacyGraphProfile(travelSchema, "travel_item"), true);
|
||||
});
|
||||
|
||||
test("generic graph shortcuts come from the active schema", () => {
|
||||
const templates = buildGenericGraphQueryTemplates({ schema_jsonb: genericSchema });
|
||||
|
||||
assert.deepEqual(templates, [
|
||||
{ label: "全部节点", cypher: "MATCH (n) RETURN n LIMIT 200" },
|
||||
{ label: "全部节点关系", cypher: "MATCH (n)-[r]->(m) RETURN n,r,m LIMIT 2500" },
|
||||
{ label: "地点(Place)节点", cypher: "MATCH (n:`Place`) RETURN n LIMIT 100" },
|
||||
{ label: "酒店(Hotel)节点", cypher: "MATCH (n:`Hotel`) RETURN n LIMIT 100" },
|
||||
{
|
||||
label: "LOCATED_IN 关系",
|
||||
cypher: "MATCH (a:`Place`)-[r:`LOCATED_IN`]->(b:`Place`) RETURN a,r,b LIMIT 100",
|
||||
},
|
||||
{
|
||||
label: "NEAR 关系",
|
||||
cypher: "MATCH (a:`Hotel`)-[r:`NEAR`]->(b:`Place`) RETURN a,r,b LIMIT 100",
|
||||
},
|
||||
]);
|
||||
assert.equal(JSON.stringify(templates).includes("花溪"), false);
|
||||
assert.equal(JSON.stringify(templates).includes("Area"), false);
|
||||
assert.equal(genericGraphQueryPlaceholder(genericSchema), "输入自然语言或 Cypher,例如:查询 Place、Hotel 节点");
|
||||
});
|
||||
|
||||
test("unsafe schema identifiers are never interpolated into shortcuts", () => {
|
||||
const templates = buildGenericGraphQueryTemplates({
|
||||
entity_types: {
|
||||
"Place`) MATCH (secret": {},
|
||||
SafeType: {},
|
||||
},
|
||||
relation_types: {
|
||||
"REL`]->(secret": { from: "SafeType", to: "SafeType" },
|
||||
SAFE_REL: { from: "SafeType", to: "Bad|Union" },
|
||||
},
|
||||
});
|
||||
const allQueries = templates.map(({ cypher }) => cypher).join("\n");
|
||||
|
||||
assert.equal(allQueries.includes("secret"), false);
|
||||
assert.equal(allQueries.includes("Bad|Union"), false);
|
||||
assert.match(allQueries, /`SafeType`/);
|
||||
assert.match(allQueries, /`SAFE_REL`/);
|
||||
});
|
||||
|
||||
test("graph counts provide safe shortcuts when the schema endpoint is unavailable", () => {
|
||||
assert.deepEqual(buildGenericGraphQueryTemplates({}, {
|
||||
label_counts: [{ label: "Place", count: 2 }, { label: "Hotel", count: 1 }],
|
||||
relationship_type_counts: [{ type: "NEAR", count: 1 }],
|
||||
}), [
|
||||
{ label: "全部节点", cypher: "MATCH (n) RETURN n LIMIT 200" },
|
||||
{ label: "全部节点关系", cypher: "MATCH (n)-[r]->(m) RETURN n,r,m LIMIT 2500" },
|
||||
{ label: "Place 节点", cypher: "MATCH (n:`Place`) RETURN n LIMIT 100" },
|
||||
{ label: "Hotel 节点", cypher: "MATCH (n:`Hotel`) RETURN n LIMIT 100" },
|
||||
{ label: "NEAR 关系", cypher: "MATCH (a)-[r:`NEAR`]->(b) RETURN a,r,b LIMIT 100" },
|
||||
]);
|
||||
});
|
||||
Reference in new issue
Block a user