diff --git a/src/specmap/lib/refs.js b/src/specmap/lib/refs.js index c93d325da..9500728c6 100644 --- a/src/specmap/lib/refs.js +++ b/src/specmap/lib/refs.js @@ -329,11 +329,13 @@ function pointerAlreadyInPath(pointer, basePath, parent, specmap) { // Case 1: direct cycle, e.g. a.b.c.$ref: '/a.b' // Detect by checking that the parent path doesn't start with pointer. - // This only applies if the pointer is purely internal. - if (basePath == null && pointerIsAParent(parentPointer, pointer)) { + // This only applies if the pointer is internal, i.e. basePath === rootPath (could be null) + const rootDoc = specmap.contextTree.get([]).baseDoc + if (basePath === rootDoc && pointerIsAParent(parentPointer, pointer)) { return true } + // Case 2: indirect cycle // ex1: a.$ref: '/b' & b.c.$ref: '/b/c' // ex2: a.$ref: '/b/c' & b.c.$ref: '/b' diff --git a/test/index.js b/test/index.js index 7bb45251c..1c65d494a 100644 --- a/test/index.js +++ b/test/index.js @@ -91,6 +91,43 @@ describe('constructor', () => { expect(apis.me.getMe).toBeA(Function) }) }) + + it('should handle circular $refs when a baseDoc is provided', () => { + // Given + const spec = { + swagger: '2.0', + definitions: { + node: { + required: ['id', 'nodes'], + type: 'object', + properties: { + id: { + type: 'string' + }, + nodes: { + type: 'array', + items: { + $ref: '#/definitions/node' + } + } + } + } + } + } + + // When + return Swagger.resolve({ + spec, + allowMetaPatches: false, + baseDoc: 'http://example.com/swagger.json' + }).then(handleResponse) + + // Then + function handleResponse(obj) { + expect(obj.errors).toEqual([]) + expect(obj.spec).toEqual(spec) + } + }) }) describe('#http', function () {