Repository: Redocly/redocly-cli
Pull request #1582 (2024-06-05 07:04:18)

fix: falsy values in no-invalid-media-type-examples, no-invalid-parameter-examples, and no-invalid-schema-examples rules
## What/Why/How?

Fixed a small issue in `no-invalid-media-type-examples`, `no-invalid-parameter-examples`, and `no-invalid-schema-examples` rules which allowed to pass falsy examples for any schema.

## Reference

## Testing

## Screenshots (optional)

## Check yourself

- [ ] Code changed? - Tested with redoc/reference-docs/workflows (internal)
- [ ] All new/updated code is covered with tests
- [ ] New package installed? - Tested in different environments (browser/node)

## Security

- [x] Security impact of change has been considered
- [x] Code follows company security practices and guidelines


changeset-bot[bot]: ###  🦋  Changeset detected

Latest commit: efb6cf42f30c9b6d6874fd3e7db2756a0e4f124e

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 2 packages</summary>

  | Name                  | Type  |
| --------------------- | ----- |
| @redocly/openapi-core | Patch |
| @redocly/cli          | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/Redocly/redocly-cli/new/fix/no-invalid-media-type-exxamples-falsy?filename=.changeset/fast-stingrays-carry.md&value=---%0A%22%40redocly%2Fopenapi-core%22%3A%20patch%0A---%0A%0Afix%3A%20falsy%20values%20in%20no-invalid-media-type-exampls%20rule%0A)


github-actions[bot]: | Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `redocly lint packages/core/src/benchmark/benches/rebilly.yaml` | 975.2 ± 27.3 | 950.8 | 1033.7 | 1.00 |
| `redocly-next lint packages/core/src/benchmark/benches/rebilly.yaml` | 990.3 ± 26.3 | 958.9 | 1038.3 | 1.02 ± 0.04 |

<!-- thollander/actions-comment-pull-request "latest-vs-next-comparison" -->
github-actions[bot]: <!-- jest coverage report action for options with hash 3339ae53a38c44382c0fb4491bdb282d -->

## Coverage report 



| <div title="Status of coverage:&#10; 🟢 - ok&#10; 🟡 - slightly more than threshold&#10; 🔴 - under the threshold">St.<sup>:grey_question:</sup></div> | Category   | Percentage | Covered / Total |
| :----------------------------------------------------------------------------------------------------------------------------------------------------: | :--------- | :--------- | :-------------: |
|                                                                           🟡                                                                           | Statements | 77.26%     |    4485/5805    |
|                                                                           🟡                                                                           | Branches   | 67.57%     |    2479/3669    |
|                                                                           🟡                                                                           | Functions  | 70.94%     |     747/1053    |
|                                                                           🟡                                                                           | Lines      | 77.45%     |    4218/5446    |


## Test suite run success
739 tests passing in 102 suites.

<p align="right">Report generated by <a href="https://github.com/ArtiomTr/jest-coverage-report-action">🧪jest coverage report action</a> from efb6cf42f30c9b6d6874fd3e7db2756a0e4f124e</p>


Patch:
diff --git a/.changeset/strong-crews-change.md b/.changeset/strong-crews-change.md
new file mode 100644
index 00000000..c7503370
--- /dev/null
+++ b/.changeset/strong-crews-change.md
@@ -0,0 +1,6 @@
+---
+"@redocly/openapi-core": patch
+"@redocly/cli": patch
+---
+
+Fixed `no-invalid-media-type-examples`, `no-invalid-parameter-examples`, and `no-invalid-schema-examples` rules which allowed falsy example values to pass for any schema.
diff --git a/packages/core/src/rules/common/no-invalid-parameter-examples.ts b/packages/core/src/rules/common/no-invalid-parameter-examples.ts
index bea8e5bc..0a4806d4 100644
--- a/packages/core/src/rules/common/no-invalid-parameter-examples.ts
+++ b/packages/core/src/rules/common/no-invalid-parameter-examples.ts
@@ -7,7 +7,7 @@ export const NoInvalidParameterExamples: any = (opts: any) => {
   return {
     Parameter: {
       leave(parameter: Oas3Parameter, ctx: UserContext) {
-        if (parameter.example) {
+        if (parameter.example !== undefined) {
           validateExample(
             parameter.example,
             parameter.schema!,
diff --git a/packages/core/src/rules/common/no-invalid-schema-examples.ts b/packages/core/src/rules/common/no-invalid-schema-examples.ts
index a80d5c4c..deebf652 100644
--- a/packages/core/src/rules/common/no-invalid-schema-examples.ts
+++ b/packages/core/src/rules/common/no-invalid-schema-examples.ts
@@ -18,7 +18,7 @@ export const NoInvalidSchemaExamples: any = (opts: any) => {
             );
           }
         }
-        if (schema.example) {
+        if (schema.example !== undefined) {
           validateExample(schema.example, schema, ctx.location.child('example'), ctx, true);
         }
       },
diff --git a/packages/core/src/rules/oas3/no-invalid-media-type-examples.ts b/packages/core/src/rules/oas3/no-invalid-media-type-examples.ts
index 240e753f..ce13441e 100644
--- a/packages/core/src/rules/oas3/no-invalid-media-type-examples.ts
+++ b/packages/core/src/rules/oas3/no-invalid-media-type-examples.ts
@@ -12,7 +12,7 @@ export const ValidContentExamples: Oas3Rule = (opts) => {
       leave(mediaType, ctx: UserContext) {
         const { location, resolve } = ctx;
         if (!mediaType.schema) return;
-        if (mediaType.example) {
+        if (mediaType.example !== undefined) {
           resolveAndValidateExample(mediaType.example, location.child('example'));
         } else if (mediaType.examples) {
           for (const exampleName of Object.keys(mediaType.examples)) {


Test patch:
diff --git a/packages/core/src/rules/common/__tests__/no-invalid-parameter-examples.test.ts b/packages/core/src/rules/common/__tests__/no-invalid-parameter-examples.test.ts
new file mode 100644
index 00000000..df1b05eb
--- /dev/null
+++ b/packages/core/src/rules/common/__tests__/no-invalid-parameter-examples.test.ts
@@ -0,0 +1,53 @@
+import { outdent } from 'outdent';
+import { lintDocument } from '../../../lint';
+import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../../../__tests__/utils';
+import { BaseResolver } from '../../../resolve';
+
+describe('no-invalid-parameter-examples', () => {
+  it('should report on invalid falsy example', async () => {
+    const document = parseYamlToDocument(
+      outdent`
+        openapi: 3.1.0
+        paths:
+          /results:
+            get:
+              parameters:
+                - name: username
+                  in: query
+                  schema:
+                    type: string
+                    maxLength: 15
+                  example: false
+      `,
+      'foobar.yaml'
+    );
+
+    const results = await lintDocument({
+      externalRefResolver: new BaseResolver(),
+      document,
+      config: await makeConfig({ 'no-invalid-parameter-examples': 'error' }),
+    });
+
+    expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
+      [
+        {
+          "from": {
+            "pointer": "#/paths/~1results/get/parameters/0",
+            "source": "foobar.yaml",
+          },
+          "location": [
+            {
+              "pointer": "#/paths/~1results/get/parameters/0/example",
+              "reportOnKey": false,
+              "source": "foobar.yaml",
+            },
+          ],
+          "message": "Example value must conform to the schema: type must be string.",
+          "ruleId": "no-invalid-parameter-examples",
+          "severity": "error",
+          "suggest": [],
+        },
+      ]
+    `);
+  });
+});
diff --git a/packages/core/src/rules/common/__tests__/no-invalid-schema-examples.test.ts b/packages/core/src/rules/common/__tests__/no-invalid-schema-examples.test.ts
new file mode 100644
index 00000000..96830c97
--- /dev/null
+++ b/packages/core/src/rules/common/__tests__/no-invalid-schema-examples.test.ts
@@ -0,0 +1,51 @@
+import { outdent } from 'outdent';
+import { lintDocument } from '../../../lint';
+import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../../../__tests__/utils';
+import { BaseResolver } from '../../../resolve';
+
+describe('no-invalid-schema-examples', () => {
+  it('should report on invalid falsy example', async () => {
+    const document = parseYamlToDocument(
+      outdent`
+        openapi: 3.1.0
+        components:
+          schemas:
+            Car:
+              type: object
+              properties:
+                color:
+                  type: string
+                  example: 0
+      `,
+      'foobar.yaml'
+    );
+
+    const results = await lintDocument({
+      externalRefResolver: new BaseResolver(),
+      document,
+      config: await makeConfig({ 'no-invalid-schema-examples': 'error' }),
+    });
+
+    expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
+      [
+        {
+          "from": {
+            "pointer": "#/components/schemas/Car/properties/color",
+            "source": "foobar.yaml",
+          },
+          "location": [
+            {
+              "pointer": "#/components/schemas/Car/properties/color/example",
+              "reportOnKey": false,
+              "source": "foobar.yaml",
+            },
+          ],
+          "message": "Example value must conform to the schema: type must be string.",
+          "ruleId": "no-invalid-schema-examples",
+          "severity": "error",
+          "suggest": [],
+        },
+      ]
+    `);
+  });
+});
diff --git a/packages/core/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts b/packages/core/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts
index bf4a028e..146e79a7 100644
--- a/packages/core/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts
+++ b/packages/core/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts
@@ -137,6 +137,58 @@ describe('no-invalid-media-type-examples', () => {
     `);
   });
 
+  it('should report on invalid example with a falsy value', async () => {
+    const document = parseYamlToDocument(
+      outdent`
+        openapi: 3.1.0
+        paths:
+          /test:
+            get:
+              responses:
+                '200':
+                  content:
+                    application/json:
+                      schema:
+                        type: string
+                      example: false
+      `,
+      'foobar.yaml'
+    );
+
+    const results = await lintDocument({
+      externalRefResolver: new BaseResolver(),
+      document,
+      config: await makeConfig({
+        'no-invalid-media-type-examples': {
+          severity: 'error',
+          allowAdditionalProperties: false,
+        },
+      }),
+    });
+
+    expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
+      [
+        {
+          "from": {
+            "pointer": "#/paths/~1test/get/responses/200/content/application~1json",
+            "source": "foobar.yaml",
+          },
+          "location": [
+            {
+              "pointer": "#/paths/~1test/get/responses/200/content/application~1json/example",
+              "reportOnKey": false,
+              "source": "foobar.yaml",
+            },
+          ],
+          "message": "Example value must conform to the schema: type must be string.",
+          "ruleId": "no-invalid-media-type-examples",
+          "severity": "error",
+          "suggest": [],
+        },
+      ]
+    `);
+  });
+
   it('should not report on valid example with allowAdditionalProperties', async () => {
     const document = parseYamlToDocument(
       outdent`