first
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
groupDrawerActionById,
|
||||
groupDrawerActions,
|
||||
recommendedGroupActionForCommunity,
|
||||
recommendedGroupActionForSelection,
|
||||
resolveSelection,
|
||||
resolveSelectionForCapabilities,
|
||||
toggleNodeInSelection
|
||||
} from "../src/select";
|
||||
import type { GraphData, SelectionActionId } from "../src/types";
|
||||
|
||||
describe("structured graph selection", () => {
|
||||
it("maps a linked single page to page actions", () => {
|
||||
const selection = resolveSelection(multicommGraph(), { kind: "node", id: "a1" });
|
||||
|
||||
assert.deepEqual(selection.nodeIds, ["a1"]);
|
||||
assert.deepEqual(selection.communityIds, ["alpha"]);
|
||||
assert.deepEqual(selection.facts, {
|
||||
pageCount: 1,
|
||||
internalLinkCount: 0,
|
||||
communityCount: 1,
|
||||
isolatedCount: 0
|
||||
});
|
||||
assert.deepEqual(actionIds(selection), ["summarize_page", "find_related_pages", "quote_page"]);
|
||||
assert.equal(actionIds(selection).includes("explore_potential_links"), false);
|
||||
});
|
||||
|
||||
it("maps an isolated single page to page actions plus the link action", () => {
|
||||
const selection = resolveSelection(multicommGraph(), { kind: "node", id: "island" });
|
||||
|
||||
assert.deepEqual(selection.nodeIds, ["island"]);
|
||||
assert.deepEqual(selection.communityIds, ["solo"]);
|
||||
assert.deepEqual(selection.facts, {
|
||||
pageCount: 1,
|
||||
internalLinkCount: 0,
|
||||
communityCount: 1,
|
||||
isolatedCount: 1
|
||||
});
|
||||
assert.deepEqual(actionIds(selection), ["summarize_page", "find_related_pages", "quote_page", "link_island"]);
|
||||
assert.equal(actionIds(selection).includes("explore_potential_links"), false);
|
||||
});
|
||||
|
||||
it("selects a community and offers single-community actions", () => {
|
||||
const selection = resolveSelection(multicommGraph(), { kind: "community", id: "alpha" });
|
||||
|
||||
assert.deepEqual(selection.nodeIds, ["a1", "a2", "a3"]);
|
||||
assert.deepEqual(selection.communityIds, ["alpha"]);
|
||||
assert.deepEqual(selection.facts, {
|
||||
pageCount: 3,
|
||||
internalLinkCount: 2,
|
||||
communityCount: 1,
|
||||
isolatedCount: 0
|
||||
});
|
||||
assert.deepEqual(actionIds(selection), ["summarize_cluster", "find_knowledge_gaps", "create_topic_page"]);
|
||||
});
|
||||
|
||||
it("selects one-hop neighbors and offers two-community bridge actions", () => {
|
||||
const selection = resolveSelection(multicommGraph(), { kind: "neighbors", id: "bridge" });
|
||||
|
||||
assert.deepEqual(selection.nodeIds, ["a3", "bridge", "b1"]);
|
||||
assert.deepEqual(selection.communityIds, ["alpha", "beta"]);
|
||||
assert.deepEqual(selection.facts, {
|
||||
pageCount: 3,
|
||||
internalLinkCount: 2,
|
||||
communityCount: 2,
|
||||
isolatedCount: 0
|
||||
});
|
||||
assert.deepEqual(actionIds(selection), ["why_no_connection", "find_potential_bridges", "compare_communities"]);
|
||||
});
|
||||
|
||||
it("selects shift-style manual nodes and avoids cluster summary for unlinked multi-select", () => {
|
||||
const selection = resolveSelection(multicommGraph(), { kind: "nodes", ids: ["a1", "b2"] });
|
||||
|
||||
assert.deepEqual(selection.nodeIds, ["a1", "b2"]);
|
||||
assert.deepEqual(selection.communityIds, ["alpha", "beta"]);
|
||||
assert.deepEqual(selection.facts, {
|
||||
pageCount: 2,
|
||||
internalLinkCount: 0,
|
||||
communityCount: 2,
|
||||
isolatedCount: 0
|
||||
});
|
||||
assert.deepEqual(actionIds(selection), ["why_no_connection", "find_potential_bridges", "compare_communities"]);
|
||||
assert.equal(actionIds(selection).includes("summarize_cluster"), false);
|
||||
});
|
||||
|
||||
it("maps linked manual multi-select to group actions", () => {
|
||||
const selection = resolveSelection(multicommGraph(), { kind: "nodes", ids: ["a1", "a2"] });
|
||||
|
||||
assert.deepEqual(selection.nodeIds, ["a1", "a2"]);
|
||||
assert.deepEqual(selection.facts, {
|
||||
pageCount: 2,
|
||||
internalLinkCount: 1,
|
||||
communityCount: 1,
|
||||
isolatedCount: 0
|
||||
});
|
||||
assert.deepEqual(actionIds(selection), ["summarize_group", "explore_group_relationships"]);
|
||||
assert.equal(actionIds(selection).includes("summarize_cluster"), false);
|
||||
});
|
||||
|
||||
it("maps unlinked multi-community selections beyond two communities to exploration actions", () => {
|
||||
const selection = resolveSelection(multicommGraph(), { kind: "nodes", ids: ["a1", "b2", "g1"] });
|
||||
|
||||
assert.deepEqual(selection.nodeIds, ["a1", "b2", "g1"]);
|
||||
assert.deepEqual(selection.facts, {
|
||||
pageCount: 3,
|
||||
internalLinkCount: 0,
|
||||
communityCount: 3,
|
||||
isolatedCount: 0
|
||||
});
|
||||
assert.deepEqual(actionIds(selection), ["explore_potential_links", "compare_differences"]);
|
||||
assert.equal(actionIds(selection).includes("summarize_cluster"), false);
|
||||
});
|
||||
|
||||
it("omits ask actions when onAsk capability is absent", () => {
|
||||
const selection = resolveSelectionForCapabilities(multicommGraph(), { kind: "community", id: "alpha" }, { canAsk: false });
|
||||
|
||||
assert.deepEqual(selection.facts, {
|
||||
pageCount: 3,
|
||||
internalLinkCount: 2,
|
||||
communityCount: 1,
|
||||
isolatedCount: 0
|
||||
});
|
||||
assert.deepEqual(actionIds(selection), []);
|
||||
});
|
||||
|
||||
it("toggles Sigma Shift node clicks into stable manual node selections", () => {
|
||||
const data = multicommGraph();
|
||||
|
||||
assert.deepEqual(toggleNodeInSelection(data, null, "a1"), { kind: "node", id: "a1" });
|
||||
assert.deepEqual(toggleNodeInSelection(data, { kind: "node", id: "a1" }, "a2"), { kind: "nodes", ids: ["a1", "a2"] });
|
||||
assert.deepEqual(toggleNodeInSelection(data, { kind: "nodes", ids: ["a1", "a2"] }, "a1"), { kind: "node", id: "a2" });
|
||||
assert.equal(toggleNodeInSelection(data, { kind: "node", id: "a1" }, "a1"), null);
|
||||
});
|
||||
|
||||
it("toggles from a community selection without losing graph order", () => {
|
||||
const data = multicommGraph();
|
||||
|
||||
assert.deepEqual(toggleNodeInSelection(data, { kind: "community", id: "alpha" }, "b1"), {
|
||||
kind: "nodes",
|
||||
ids: ["a1", "a2", "a3", "b1"]
|
||||
});
|
||||
});
|
||||
|
||||
it("exposes unified drawer actions and recommendations from one catalog", () => {
|
||||
assert.deepEqual(groupDrawerActions().map((action) => action.label), [
|
||||
"总结这一簇",
|
||||
"找知识缺口",
|
||||
"生成主题页",
|
||||
"探索潜在关系"
|
||||
]);
|
||||
assert.equal(groupDrawerActionById("find_knowledge_gaps")?.tone, "lint");
|
||||
assert.equal(recommendedGroupActionForCommunity("loose"), "find_knowledge_gaps");
|
||||
assert.equal(recommendedGroupActionForCommunity("ungrouped"), "explore_potential_links");
|
||||
assert.equal(
|
||||
recommendedGroupActionForSelection({ pageCount: 3, internalLinkCount: 0, communityCount: 1, isolatedCount: 3 }),
|
||||
"explore_potential_links"
|
||||
);
|
||||
assert.equal(
|
||||
recommendedGroupActionForSelection({ pageCount: 3, internalLinkCount: 2, communityCount: 1, isolatedCount: 0 }),
|
||||
"summarize_cluster"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function actionIds(selection: { actions?: Array<{ id: SelectionActionId }> }): SelectionActionId[] {
|
||||
return (selection.actions ?? []).map((action) => action.id);
|
||||
}
|
||||
|
||||
function multicommGraph(): GraphData {
|
||||
return {
|
||||
meta: {
|
||||
build_date: "2026-06-12T00:00:00.000Z",
|
||||
wiki_title: "Multicomm Fixture",
|
||||
total_nodes: 8,
|
||||
total_edges: 6
|
||||
},
|
||||
nodes: [
|
||||
{ id: "a1", label: "Alpha 1", type: "topic", community: "alpha", source_path: "wiki/topics/a1.md" },
|
||||
{ id: "a2", label: "Alpha 2", type: "entity", community: "alpha", source_path: "wiki/entities/a2.md" },
|
||||
{ id: "a3", label: "Alpha 3", type: "entity", community: "alpha", source_path: "wiki/entities/a3.md" },
|
||||
{ id: "bridge", label: "Bridge", type: "topic", community: "beta", source_path: "wiki/topics/bridge.md" },
|
||||
{ id: "b1", label: "Beta 1", type: "entity", community: "beta", source_path: "wiki/entities/b1.md" },
|
||||
{ id: "b2", label: "Beta 2", type: "entity", community: "beta", source_path: "wiki/entities/b2.md" },
|
||||
{ id: "g1", label: "Gamma 1", type: "entity", community: "gamma", source_path: "wiki/entities/g1.md" },
|
||||
{ id: "island", label: "Island", type: "source", community: "solo", source_path: "wiki/sources/island.md" }
|
||||
],
|
||||
edges: [
|
||||
{ id: "a1-a2", from: "a1", to: "a2", type: "EXTRACTED", weight: 1 },
|
||||
{ id: "a2-a3", from: "a2", to: "a3", type: "EXTRACTED", weight: 1 },
|
||||
{ id: "a3-bridge", from: "a3", to: "bridge", type: "INFERRED", weight: 0.7 },
|
||||
{ id: "bridge-b1", from: "bridge", to: "b1", type: "EXTRACTED", weight: 1 },
|
||||
{ id: "b1-b2", from: "b1", to: "b2", type: "EXTRACTED", weight: 1 },
|
||||
{ id: "g1-a2", from: "g1", to: "a2", type: "AMBIGUOUS", weight: 0.4 }
|
||||
],
|
||||
learning: {
|
||||
version: 1,
|
||||
entry: { recommended_start_node_id: "a1", recommended_start_reason: "community_hub", default_mode: "global" },
|
||||
views: {
|
||||
path: { enabled: false, start_node_id: null, node_ids: [], degraded: true },
|
||||
community: { enabled: false, community_id: null, label: null, node_ids: [], is_weak: false, degraded: true },
|
||||
global: { enabled: true, node_ids: ["a1", "a2", "a3", "bridge", "b1", "b2", "g1", "island"], degraded: false }
|
||||
},
|
||||
communities: [
|
||||
{ id: "alpha", label: "Alpha", node_count: 3, color_index: 0 },
|
||||
{ id: "beta", label: "Beta", node_count: 3, color_index: 1 },
|
||||
{ id: "gamma", label: "Gamma", node_count: 1, color_index: 2 },
|
||||
{ id: "solo", label: "Solo", node_count: 1, color_index: 3 }
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user