From 63987725957114ea92dab430b7795ecc649ff107 Mon Sep 17 00:00:00 2001 From: Mark Wubben Date: Fri, 16 Jun 2023 18:23:54 +0200 Subject: [PATCH] Fix circular selector detection in t.like() The previous implementation tracked each object, even if not circular. Update to use a stack. Fixes #3205. --- lib/like-selector.js | 22 ++++++++++++---------- test-tap/assert.js | 5 +++++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/like-selector.js b/lib/like-selector.js index 8f9bfb64c..112eaf149 100644 --- a/lib/like-selector.js +++ b/lib/like-selector.js @@ -16,13 +16,7 @@ export function isLikeSelector(selector) { export const CIRCULAR_SELECTOR = new Error('Encountered a circular selector'); -export function selectComparable(actual, selector, circular = new Set()) { - if (circular.has(selector)) { - throw CIRCULAR_SELECTOR; - } - - circular.add(selector); - +export function selectComparable(actual, selector, circular = [selector]) { if (isPrimitive(actual)) { return actual; } @@ -31,9 +25,17 @@ export function selectComparable(actual, selector, circular = new Set()) { const enumerableKeys = Reflect.ownKeys(selector).filter(key => Reflect.getOwnPropertyDescriptor(selector, key).enumerable); for (const key of enumerableKeys) { const subselector = Reflect.get(selector, key); - comparable[key] = isLikeSelector(subselector) - ? selectComparable(Reflect.get(actual, key), subselector, circular) - : Reflect.get(actual, key); + if (isLikeSelector(subselector)) { + if (circular.includes(subselector)) { + throw CIRCULAR_SELECTOR; + } + + circular.push(subselector); + comparable[key] = selectComparable(Reflect.get(actual, key), subselector, circular); + circular.pop(); + } else { + comparable[key] = Reflect.get(actual, key); + } } return comparable; diff --git a/test-tap/assert.js b/test-tap/assert.js index cc62ca1a1..6f30773aa 100644 --- a/test-tap/assert.js +++ b/test-tap/assert.js @@ -741,6 +741,11 @@ test('.like()', t => { return assertions.like(specimen, selector); }); + passes(t, () => { + const array = ['c1', 'c2']; + return assertions.like({a: 'a', b: 'b', c: ['c1', 'c2'], d: ['c1', 'c2']}, {b: 'b', d: array, c: array}); + }); + failsWith(t, () => { const likePattern = { a: 'a',