test for circular references

This commit is contained in:
Marc-Roig 2023-01-03 12:21:40 +01:00
parent 2528ae5637
commit 26fba9f4be
2 changed files with 63 additions and 36 deletions

View File

@ -45,4 +45,20 @@ describe('sortConnectArray', () => {
'There was a problem connecting relation with id 1 at position {"after":2}. The relation with id 2 needs to be connected first.' 'There was a problem connecting relation with id 1 at position {"after":2}. The relation with id 2 needs to be connected first.'
); );
}); });
test('error with circular references', () => {
const sortConnect = () =>
sortConnectArray(
[
{ id: 2, position: { after: 1 } },
{ id: 3, position: { after: 1 } },
{ id: 1, position: { after: 3 } },
],
[]
);
expect(sortConnect).toThrowError(
'A circular reference was found in the connect array. One relation is trying to connect before/after another one that is trying to connect before/after it'
);
});
}); });

View File

@ -44,47 +44,58 @@ const sortConnectArray = (connectArr, initialArr = [], strictSort = true) => {
if (!needsSorting) return connectArr; if (!needsSorting) return connectArr;
// Iterate over connectArr and populate sortedConnect // Iterate over connectArr and populate sortedConnect
connectArr.forEach((rel, idx) => { try {
const pushRelation = (rel) => { connectArr.forEach((rel, idx) => {
sortedConnect.push(rel); const pushRelation = (rel) => {
relInArray[rel.id] = true; sortedConnect.push(rel);
}; relInArray[rel.id] = true;
};
const computeRelation = (rel) => { const computeRelation = (rel) => {
const adjacentRelId = rel.position?.before || rel.position?.after; const adjacentRelId = rel.position?.before || rel.position?.after;
// This connect has already been computed // This connect has already been computed
if (idx in computedIdx) return; if (idx in computedIdx) return;
if (!adjacentRelId || relInArray[adjacentRelId]) { if (!adjacentRelId || relInArray[adjacentRelId]) {
return pushRelation(rel); return pushRelation(rel);
} }
// Look if id is referenced elsewhere in the array // Look if id is referenced elsewhere in the array
const adjacentRelIdx = firstSeen[adjacentRelId]; const adjacentRelIdx = firstSeen[adjacentRelId];
if (adjacentRelIdx) { if (adjacentRelIdx) {
const adjacentRel = connectArr[adjacentRelIdx]; const adjacentRel = connectArr[adjacentRelIdx];
// Mark adjacent relation idx as computed, // Mark adjacent relation idx as computed,
// so it is not computed again later in the loop // so it is not computed again later in the loop
computedIdx[adjacentRelIdx] = true; computedIdx[adjacentRelIdx] = true;
computeRelation(adjacentRel); computeRelation(adjacentRel);
pushRelation(rel); pushRelation(rel);
} else if (strictSort) { } else if (strictSort) {
// If we reach this point, it means that the adjacent relation is not in the connect array // If we reach this point, it means that the adjacent relation is not in the connect array
// and it is not in the database. This should not happen. // and it is not in the database. This should not happen.
throw new InvalidRelationError( throw new InvalidRelationError(
`There was a problem connecting relation with id ${rel.id} at position ${JSON.stringify( `There was a problem connecting relation with id ${rel.id} at position ${JSON.stringify(
rel.position rel.position
)}. The relation with id ${adjacentRelId} needs to be connected first.` )}. The relation with id ${adjacentRelId} needs to be connected first.`
); );
} else { } else {
// We are in non-strict mode so we can push the relation. // We are in non-strict mode so we can push the relation.
pushRelation({ id: rel.id, position: { end: true } }); pushRelation({ id: rel.id, position: { end: true } });
} }
}; };
computeRelation(rel); computeRelation(rel);
}); });
} catch (err) {
// If it is a RangeError, there is a circular dependency in the connect array.
if (err instanceof RangeError)
throw new InvalidRelationError(
'A circular reference was found in the connect array. ' +
'One relation is trying to connect before/after another one that is trying to connect before/after it'
);
throw err;
}
return sortedConnect; return sortedConnect;
}; };