Repository: Redocly/openapi-cli
Pull request #162 (2020-07-09 10:01:17)

fix: trailing slash rule wrong location & '/' path


Patch:
diff --git a/src/cli.ts b/src/cli.ts
index c3b4a79e..ec4b6dcd 100644
--- a/src/cli.ts
+++ b/src/cli.ts
@@ -411,7 +411,9 @@ function printLintTotals(totals: Totals, definitionsCount: number) {
     process.stderr.write(
       red(
         `❌ Validation failed with ${totals.errors} ${pluralize('error', totals.errors)}${
-          totals.warnings > 0 ? ` and ${totals.warnings} ${pluralize('warning', totals.warnings)}` : ''
+          totals.warnings > 0
+            ? ` and ${totals.warnings} ${pluralize('warning', totals.warnings)}`
+            : ''
         }.\n${ignored}\n`,
       ),
     );
diff --git a/src/resolve.ts b/src/resolve.ts
index b2603ddf..1870153f 100644
--- a/src/resolve.ts
+++ b/src/resolve.ts
@@ -344,10 +344,11 @@ export async function resolveDocument(opts: {
       resolvedRef.node = target;
 
       const refId = document.source.absoluteRef + '::' + ref.$ref;
-      resolvedRefMap.set(refId, resolvedRef);
+
       if (resolvedRef.document && isRef(target)) {
-        return followRef(resolvedRef.document, target, pushRef(refStack, target));
+        resolvedRef = await followRef(resolvedRef.document, target, pushRef(refStack, target));
       }
+      resolvedRefMap.set(refId, resolvedRef);
 
       return { ...resolvedRef };
     }
diff --git a/src/rules/common/no-path-trailing-slash.ts b/src/rules/common/no-path-trailing-slash.ts
index 137a5c89..af1f569d 100644
--- a/src/rules/common/no-path-trailing-slash.ts
+++ b/src/rules/common/no-path-trailing-slash.ts
@@ -3,10 +3,11 @@ import { UserContext } from '../../walk';
 
 export const NoPathTrailingSlash: Oas3Rule | Oas2Rule = () => {
   return {
-    PathItem(_path: any, { report, key }: UserContext) {
-      if ((key as string).endsWith('/')) {
+    PathItem(_path: any, { report, key, location }: UserContext) {
+      if ((key as string).endsWith('/') && key !== '/') {
         report({
           message: `\`${key}\` should not have a trailing slash.`,
+          location: location.key(),
         });
       }
     },


Test patch:
diff --git a/src/__tests__/resolve.test.ts b/src/__tests__/resolve.test.ts
index 1b4d79bf..4786fed8 100644
--- a/src/__tests__/resolve.test.ts
+++ b/src/__tests__/resolve.test.ts
@@ -78,6 +78,12 @@ describe('collect refs', () => {
           $ref: "#/tmp/info"
         tmp:
           $ref: '#/defs'
+        prop:
+          $ref: '#/propTrans'
+        propTrans:
+          $ref: '#/propDest'
+        propDest:
+          type: string
         defs:
           info:
             contact: {}
@@ -93,14 +99,18 @@ describe('collect refs', () => {
     });
 
     expect(resolvedRefs).toBeDefined();
-    expect(resolvedRefs.size).toEqual(2);
+    expect(resolvedRefs.size).toEqual(4);
     expect(Array.from(resolvedRefs.keys())).toEqual([
       'foobar.yaml::#/defs',
+      'foobar.yaml::#/propDest',
       'foobar.yaml::#/tmp/info',
+      'foobar.yaml::#/propTrans',
     ]);
     expect(Array.from(resolvedRefs.values()).map((info) => info.node)).toEqual([
       { info: { contact: {}, license: {} } },
+      { type: 'string' },
       { contact: {}, license: {} },
+      { type: 'string' },
     ]);
   });
 
diff --git a/src/rules/common/__tests__/no-path-trailing-slash.test.ts b/src/rules/common/__tests__/no-path-trailing-slash.test.ts
new file mode 100644
index 00000000..02112159
--- /dev/null
+++ b/src/rules/common/__tests__/no-path-trailing-slash.test.ts
@@ -0,0 +1,88 @@
+import { outdent } from 'outdent';
+
+import { LintConfig } from '../../../config/config';
+
+import { validateDocument } from '../../../validate';
+import { parseYamlToDocument, replaceSourceWithRef } from '../../../__tests__/utils';
+import { BaseResolver } from '../../../resolve';
+
+describe('no-path-trailing-slash', () => {
+  it('should report on trailing slash in path', async () => {
+    const document = parseYamlToDocument(
+      outdent`
+        openapi: 3.0.0
+        paths:
+          '/bad/':
+            get:
+              summary: List all pets
+        `,
+      'foobar.yaml',
+    );
+
+    const results = await validateDocument({
+      externalRefResolver: new BaseResolver(),
+      document,
+      config: new LintConfig({ extends: [], rules: { 'no-path-trailing-slash': 'error' } }),
+    });
+
+    expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "location": Array [
+            Object {
+              "pointer": "#/paths/~1bad~1",
+              "reportOnKey": true,
+              "source": "foobar.yaml",
+            },
+          ],
+          "message": "\`/bad/\` should not have a trailing slash.",
+          "ruleId": "no-path-trailing-slash",
+          "severity": "error",
+          "suggest": Array [],
+        },
+      ]
+    `);
+  });
+
+  it('should not report on if no trailing slash in path', async () => {
+    const document = parseYamlToDocument(
+      outdent`
+        openapi: 3.0.0
+        paths:
+          '/good':
+            get:
+              summary: List all pets
+        `,
+      'foobar.yaml',
+    );
+
+    const results = await validateDocument({
+      externalRefResolver: new BaseResolver(),
+      document,
+      config: new LintConfig({ extends: [], rules: { 'no-path-trailing-slash': 'error' } }),
+    });
+
+    expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
+  });
+
+  it('should not report on trailing slash in path if the path is root', async () => {
+    const document = parseYamlToDocument(
+      outdent`
+        openapi: 3.0.0
+        paths:
+          '/':
+            get:
+              summary: List all pets
+        `,
+      'foobar.yaml',
+    );
+
+    const results = await validateDocument({
+      externalRefResolver: new BaseResolver(),
+      document,
+      config: new LintConfig({ extends: [], rules: { 'no-path-trailing-slash': 'error' } }),
+    });
+
+    expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
+  });
+});