03a4410272
主要工作是共享同一套“画布内核与通用 UI 源码”,网站和 Tauri 只分别实现宿主适配层。 --------- Co-authored-by: 段舒康 <kdletters@qq.com> Co-authored-by: kdletters <kdletters@qq.com> Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/136 Co-authored-by: menghao <mh18530625731@163.com> Co-committed-by: menghao <mh18530625731@163.com>
303 lines
9.1 KiB
TypeScript
303 lines
9.1 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
normalizeProjectResourceGraph,
|
|
projectResourceGraphNeighbors,
|
|
type ProjectResourceGraphReadModel,
|
|
} from '../src/view/project-development/resourceDependencyGraphModel';
|
|
|
|
function readModel(
|
|
overrides: Partial<ProjectResourceGraphReadModel> = {},
|
|
): ProjectResourceGraphReadModel {
|
|
return {
|
|
resourceIds: [],
|
|
referenceEdges: [],
|
|
taskFlows: [],
|
|
connectionIndex: [],
|
|
producerAssignments: [],
|
|
dependencyDepths: [],
|
|
unresolvedReferenceResourceIds: [],
|
|
cyclicResourceIds: [],
|
|
cyclicTaskIds: [],
|
|
producerMappingTruncated: false,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe('resource dependency graph model', () => {
|
|
it('normalizes the Rust read model and filters stale resource endpoints', () => {
|
|
const graph = normalizeProjectResourceGraph(
|
|
readModel({
|
|
resourceIds: ['asset:source', 'asset:target'],
|
|
referenceEdges: [
|
|
{
|
|
id: 'valid-reference',
|
|
kind: 'asset-reference',
|
|
sourceResourceId: 'asset:source',
|
|
targetResourceId: 'asset:target',
|
|
cyclic: false,
|
|
},
|
|
{
|
|
id: 'ghost-reference',
|
|
kind: 'asset-reference',
|
|
sourceResourceId: 'asset:deleted',
|
|
targetResourceId: 'asset:target',
|
|
cyclic: false,
|
|
},
|
|
],
|
|
taskFlows: [
|
|
{
|
|
id: 'valid-flow',
|
|
kind: 'task-flow',
|
|
sourceTaskId: 'source-task',
|
|
targetTaskId: 'target-task',
|
|
sourceResourceIds: ['asset:source', 'asset:deleted'],
|
|
targetResourceIds: ['asset:target'],
|
|
cyclic: false,
|
|
},
|
|
{
|
|
id: 'ghost-flow',
|
|
kind: 'task-flow',
|
|
sourceTaskId: 'deleted-task',
|
|
targetTaskId: 'target-task',
|
|
sourceResourceIds: ['asset:deleted'],
|
|
targetResourceIds: ['asset:target'],
|
|
cyclic: false,
|
|
},
|
|
],
|
|
cyclicResourceIds: ['asset:source', 'asset:deleted'],
|
|
}),
|
|
);
|
|
|
|
expect(graph.referenceEdges.map((edge) => edge.id)).toEqual([
|
|
'valid-reference',
|
|
]);
|
|
expect(graph.taskFlows).toEqual([
|
|
expect.objectContaining({
|
|
id: 'valid-flow',
|
|
sourceResourceIds: ['asset:source'],
|
|
targetResourceIds: ['asset:target'],
|
|
}),
|
|
]);
|
|
expect(graph.cyclicResourceIds).toEqual(new Set(['asset:source']));
|
|
});
|
|
|
|
it('queries direct reference and aggregated task-flow neighbors from the bounded index', () => {
|
|
const graph = normalizeProjectResourceGraph(
|
|
readModel({
|
|
resourceIds: ['source:a', 'source:b', 'target:a', 'target:b'],
|
|
referenceEdges: [
|
|
{
|
|
id: 'reference:a',
|
|
kind: 'asset-reference',
|
|
sourceResourceId: 'source:a',
|
|
targetResourceId: 'target:a',
|
|
cyclic: false,
|
|
},
|
|
],
|
|
taskFlows: [
|
|
{
|
|
id: 'flow:a-b',
|
|
kind: 'task-flow',
|
|
sourceTaskId: 'task:a',
|
|
targetTaskId: 'task:b',
|
|
sourceResourceIds: ['source:a', 'source:b'],
|
|
targetResourceIds: ['target:a', 'target:b'],
|
|
cyclic: false,
|
|
},
|
|
],
|
|
connectionIndex: [
|
|
{
|
|
resourceId: 'target:a',
|
|
upstreamReferenceResourceIds: ['source:a'],
|
|
downstreamReferenceResourceIds: [],
|
|
referenceEdgeIds: ['reference:a'],
|
|
taskFlowIds: ['flow:a-b'],
|
|
},
|
|
],
|
|
}),
|
|
);
|
|
|
|
const neighbors = projectResourceGraphNeighbors(graph, 'target:a');
|
|
expect(neighbors.upstreamResourceIds).toEqual(
|
|
new Set(['source:a', 'source:b']),
|
|
);
|
|
expect(neighbors.downstreamResourceIds).toEqual(new Set());
|
|
expect(neighbors.connectedEdgeIds).toEqual(
|
|
new Set(['reference:a', 'flow:a-b']),
|
|
);
|
|
});
|
|
|
|
it('keeps a refined asset connected to its source through a real reference edge', () => {
|
|
const graph = normalizeProjectResourceGraph(
|
|
readModel({
|
|
resourceIds: ['asset:source-art', 'asset:refined-art'],
|
|
referenceEdges: [
|
|
{
|
|
id: 'asset-reference:source-refined',
|
|
kind: 'asset-reference',
|
|
sourceResourceId: 'asset:source-art',
|
|
targetResourceId: 'asset:refined-art',
|
|
cyclic: false,
|
|
},
|
|
],
|
|
connectionIndex: [
|
|
{
|
|
resourceId: 'asset:refined-art',
|
|
upstreamReferenceResourceIds: ['asset:source-art'],
|
|
downstreamReferenceResourceIds: [],
|
|
referenceEdgeIds: ['asset-reference:source-refined'],
|
|
taskFlowIds: [],
|
|
},
|
|
],
|
|
dependencyDepths: [
|
|
{ resourceId: 'asset:source-art', dependencyDepth: 0 },
|
|
{ resourceId: 'asset:refined-art', dependencyDepth: 1 },
|
|
],
|
|
}),
|
|
);
|
|
|
|
expect(graph.referenceEdges).toEqual([
|
|
expect.objectContaining({
|
|
sourceResourceId: 'asset:source-art',
|
|
targetResourceId: 'asset:refined-art',
|
|
}),
|
|
]);
|
|
expect(graph.dependencyDepthByResourceId.get('asset:refined-art')).toBe(1);
|
|
expect(
|
|
projectResourceGraphNeighbors(graph, 'asset:refined-art')
|
|
.upstreamResourceIds,
|
|
).toEqual(new Set(['asset:source-art']));
|
|
});
|
|
|
|
it('fails closed only for producer-derived data when the audit tail is truncated', () => {
|
|
const graph = normalizeProjectResourceGraph(
|
|
readModel({
|
|
resourceIds: [
|
|
'asset:spec',
|
|
'asset:ui',
|
|
'asset:derived',
|
|
'asset:negative',
|
|
'asset:fractional',
|
|
'asset:unsafe',
|
|
],
|
|
producerAssignments: [
|
|
{
|
|
resourceId: 'asset:spec',
|
|
taskId: 'art-director',
|
|
},
|
|
{
|
|
resourceId: 'asset:ui',
|
|
taskId: 'design-foundation',
|
|
},
|
|
{
|
|
resourceId: 'asset:deleted',
|
|
taskId: 'task-1',
|
|
},
|
|
],
|
|
dependencyDepths: [
|
|
{
|
|
resourceId: 'asset:spec',
|
|
dependencyDepth: 0,
|
|
},
|
|
{
|
|
resourceId: 'asset:ui',
|
|
dependencyDepth: 1,
|
|
},
|
|
{
|
|
resourceId: 'asset:deleted',
|
|
dependencyDepth: 99,
|
|
},
|
|
{
|
|
resourceId: 'asset:ui',
|
|
dependencyDepth: 0,
|
|
},
|
|
{
|
|
resourceId: 'asset:derived',
|
|
dependencyDepth: 2,
|
|
},
|
|
{
|
|
resourceId: 'asset:negative',
|
|
dependencyDepth: -1,
|
|
},
|
|
{
|
|
resourceId: 'asset:fractional',
|
|
dependencyDepth: 1.5,
|
|
},
|
|
{
|
|
resourceId: 'asset:unsafe',
|
|
dependencyDepth: Number.MAX_SAFE_INTEGER + 1,
|
|
},
|
|
],
|
|
taskFlows: [
|
|
{
|
|
id: 'flow:spec-ui',
|
|
kind: 'task-flow',
|
|
sourceTaskId: 'art-director',
|
|
targetTaskId: 'design-foundation',
|
|
sourceResourceIds: ['asset:spec'],
|
|
targetResourceIds: ['asset:ui'],
|
|
cyclic: false,
|
|
},
|
|
],
|
|
referenceEdges: [
|
|
{
|
|
id: 'reference:spec-ui',
|
|
kind: 'asset-reference',
|
|
sourceResourceId: 'asset:spec',
|
|
targetResourceId: 'asset:ui',
|
|
cyclic: false,
|
|
},
|
|
],
|
|
connectionIndex: [
|
|
{
|
|
resourceId: 'asset:spec',
|
|
upstreamReferenceResourceIds: [],
|
|
downstreamReferenceResourceIds: ['asset:ui'],
|
|
referenceEdgeIds: ['reference:spec-ui'],
|
|
taskFlowIds: ['flow:spec-ui'],
|
|
},
|
|
{
|
|
resourceId: 'asset:ui',
|
|
upstreamReferenceResourceIds: ['asset:spec'],
|
|
downstreamReferenceResourceIds: [],
|
|
referenceEdgeIds: ['reference:spec-ui'],
|
|
taskFlowIds: ['flow:spec-ui'],
|
|
},
|
|
],
|
|
unresolvedReferenceResourceIds: ['external:missing'],
|
|
cyclicResourceIds: ['asset:ui'],
|
|
cyclicTaskIds: ['art-director'],
|
|
producerMappingTruncated: true,
|
|
}),
|
|
);
|
|
|
|
expect(graph.producerTaskIdByResourceId).toEqual(new Map());
|
|
expect(graph.dependencyDepthByResourceId).toEqual(
|
|
new Map([
|
|
['asset:spec', 0],
|
|
['asset:ui', 1],
|
|
['asset:derived', 2],
|
|
]),
|
|
);
|
|
expect(graph.taskFlows).toEqual([]);
|
|
expect(graph.cyclicTaskIds).toEqual(new Set());
|
|
expect(graph.referenceEdges.map((edge) => edge.id)).toEqual([
|
|
'reference:spec-ui',
|
|
]);
|
|
expect(graph.unresolvedReferenceResourceIds).toEqual(['external:missing']);
|
|
expect(graph.cyclicResourceIds).toEqual(new Set(['asset:ui']));
|
|
expect(graph.producerMappingTruncated).toBe(true);
|
|
expect(projectResourceGraphNeighbors(graph, 'asset:ui')).toEqual({
|
|
upstreamResourceIds: new Set(['asset:spec']),
|
|
downstreamResourceIds: new Set(),
|
|
connectedEdgeIds: new Set(['reference:spec-ui']),
|
|
});
|
|
expect(projectResourceGraphNeighbors(graph, 'asset:deleted')).toEqual({
|
|
upstreamResourceIds: new Set(),
|
|
downstreamResourceIds: new Set(),
|
|
connectedEdgeIds: new Set(),
|
|
});
|
|
});
|
|
});
|