113 lines
3.9 KiB
TypeScript
113 lines
3.9 KiB
TypeScript
// SPDX-FileCopyrightText: 2025-2026 Lucio Lelii <lucio.lelii@isti.cnr.it> - ISTI-CNR
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
// Attribution term under AGPL-3.0 section 7(b): see LICENSE-ADDENDUM.
|
|
|
|
import { findLoopBackEdgeIds, LoopGraph } from './flow-loops';
|
|
|
|
// The same shapes the server's FlowLoopsTest pins, so the two keep agreeing on which connection
|
|
// leads back.
|
|
describe('findLoopBackEdgeIds', () => {
|
|
const activity = (id: string) => ({ id, routesExclusively: false });
|
|
const router = (id: string) => ({ id, routesExclusively: true });
|
|
const connect = (id: string, sourceId: string, targetId: string, hasLoopSettings = false) =>
|
|
({ id, sourceId, targetId, hasLoopSettings });
|
|
|
|
it('finds the connection a router goes round by', () => {
|
|
const graph: LoopGraph = {
|
|
nodes: [activity('source'), activity('draft'), router('check'), activity('end')],
|
|
connections: [
|
|
connect('in', 'source', 'draft'),
|
|
connect('to-check', 'draft', 'check'),
|
|
connect('back', 'check', 'draft'),
|
|
connect('done', 'check', 'end')
|
|
]
|
|
};
|
|
|
|
expect([...findLoopBackEdgeIds(graph)]).toEqual(['back']);
|
|
});
|
|
|
|
it('finds nothing in a flow without cycles', () => {
|
|
const graph: LoopGraph = {
|
|
nodes: [activity('a'), router('b')],
|
|
connections: [connect('ab', 'a', 'b')]
|
|
};
|
|
|
|
expect(findLoopBackEdgeIds(graph).size).toBe(0);
|
|
});
|
|
|
|
it('finds nothing in a cycle no router closes', () => {
|
|
const graph: LoopGraph = {
|
|
nodes: [activity('a'), activity('b')],
|
|
connections: [connect('ab', 'a', 'b'), connect('ba', 'b', 'a')]
|
|
};
|
|
|
|
expect(findLoopBackEdgeIds(graph).size).toBe(0);
|
|
});
|
|
|
|
it('treats a router connected to itself as a loop of one', () => {
|
|
const graph: LoopGraph = {
|
|
nodes: [activity('source'), router('retry')],
|
|
connections: [connect('in', 'source', 'retry'), connect('again', 'retry', 'retry')]
|
|
};
|
|
|
|
expect([...findLoopBackEdgeIds(graph)]).toEqual(['again']);
|
|
});
|
|
|
|
it('is not confused by a branch inside the loop', () => {
|
|
const graph: LoopGraph = {
|
|
nodes: [activity('source'), activity('draft'), router('triage'), activity('trim'), activity('expand'),
|
|
activity('rejoin'), router('check')],
|
|
connections: [
|
|
connect('in', 'source', 'draft'),
|
|
connect('d-t', 'draft', 'triage'),
|
|
connect('short', 'triage', 'trim'),
|
|
connect('long', 'triage', 'expand'),
|
|
connect('t-r', 'trim', 'rejoin'),
|
|
connect('e-r', 'expand', 'rejoin'),
|
|
connect('r-c', 'rejoin', 'check'),
|
|
connect('back', 'check', 'draft')
|
|
]
|
|
};
|
|
|
|
expect([...findLoopBackEdgeIds(graph)]).toEqual(['back']);
|
|
});
|
|
|
|
it('picks the connection into where the loop is entered when two routers could close it', () => {
|
|
const graph: LoopGraph = {
|
|
nodes: [activity('source'), activity('draft'), router('screen'), router('check')],
|
|
connections: [
|
|
connect('in', 'source', 'draft'),
|
|
connect('d-s', 'draft', 'screen'),
|
|
connect('pass', 'screen', 'check'),
|
|
connect('back', 'check', 'draft')
|
|
]
|
|
};
|
|
|
|
expect([...findLoopBackEdgeIds(graph)]).toEqual(['back']);
|
|
});
|
|
|
|
it('lets the author decide when the shape alone cannot', () => {
|
|
const ring = (marked: boolean): LoopGraph => ({
|
|
nodes: [router('screen'), router('check')],
|
|
connections: [connect('pass', 'screen', 'check'), connect('back', 'check', 'screen', marked)]
|
|
});
|
|
|
|
expect(findLoopBackEdgeIds(ring(false)).size).toBe(0);
|
|
expect([...findLoopBackEdgeIds(ring(true))]).toEqual(['back']);
|
|
});
|
|
|
|
it('finds none where two loops share nodes', () => {
|
|
const graph: LoopGraph = {
|
|
nodes: [activity('draft'), router('first'), router('second')],
|
|
connections: [
|
|
connect('d-f', 'draft', 'first'),
|
|
connect('f-again', 'first', 'draft'),
|
|
connect('f-next', 'first', 'second'),
|
|
connect('s-again', 'second', 'draft')
|
|
]
|
|
};
|
|
|
|
expect(findLoopBackEdgeIds(graph).size).toBe(0);
|
|
});
|
|
});
|