Repository: stoplightio/spectral (ts)

Issue:
`operation-2xx-response` validation does not respect '2XX' response code
**Describe the bug**
`operation-2xx-response` does not respect '2XX' response code. According to OAI3 '2XX' is a valid range response code definition.
Validation result: 
```
OpenAPI 3.x detected

/tmp/build/test.yaml
 13:17  warning  operation-2xx-response  Operation must have at least one `2xx` response.  paths./test.post.responses

✖ 1 problem (0 errors, 1 warning, 0 infos, 0 hints)
```

**To Reproduce**

```
openapi: "3.0.0"
info:
  version: test
  title: test
  description: test

servers:
  - url: /

paths:
  '/test':
    post:
      responses:
        '2XX':
          description: Success!
        default:
          description: default
```

**Expected behavior**
No warnings or errors after validation

**Environment (remove any that are not applicable):**
 - Library version: 5.9.1 (via docker image)

**Additional context**
We use OAI to describe an API that our customers would have to implement in order to integrate with our system. Any 2xx would be accepted as successful response.


Pull request description:
fix(rulesets): operation-success-response rule does not respect 2XX and 3XX

Fixes #1727 

**Checklist**

- [x] Tests added / updated
- [x] Docs added / updated

**Does this PR introduce a breaking change?**

- [ ] Yes
- [x] No



Patch:
diff --git a/packages/rulesets/src/oas/functions/oasOpSuccessResponse.ts b/packages/rulesets/src/oas/functions/oasOpSuccessResponse.ts
index d1a22d4e..19ecc4ac 100644
--- a/packages/rulesets/src/oas/functions/oasOpSuccessResponse.ts
+++ b/packages/rulesets/src/oas/functions/oasOpSuccessResponse.ts
@@ -1,22 +1,32 @@
-import type { IFunction } from '@stoplight/spectral-core';
-import { isObject } from './utils/isObject';
+import { createRulesetFunction } from '@stoplight/spectral-core';
+import { oas3 } from '@stoplight/spectral-formats';
 
-export const oasOpSuccessResponse: IFunction = targetVal => {
-  if (!isObject(targetVal)) {
-    return;
-  }
+export const oasOpSuccessResponse = createRulesetFunction<Record<string, unknown>, null>(
+  {
+    input: {
+      type: 'object',
+    },
+    options: null,
+  },
+  (input, opts, context) => {
+    const isOAS3X = context.document.formats?.has(oas3) === true;
+
+    for (const response of Object.keys(input)) {
+      if (isOAS3X && (response === '2XX' || response === '3XX')) {
+        return;
+      }
 
-  for (const response of Object.keys(targetVal)) {
-    if (Number(response) >= 200 && Number(response) < 400) {
-      return;
+      if (Number(response) >= 200 && Number(response) < 400) {
+        return;
+      }
     }
-  }
 
-  return [
-    {
-      message: 'Operation must define at least a single 2xx or 3xx response',
-    },
-  ];
-};
+    return [
+      {
+        message: 'Operation must define at least a single 2xx or 3xx response',
+      },
+    ];
+  },
+);
 
 export default oasOpSuccessResponse;


Test patch:
diff --git a/packages/rulesets/src/oas/__tests__/operation-success-response.test.ts b/packages/rulesets/src/oas/__tests__/operation-success-response.test.ts
index a684023e..488e4350 100644
--- a/packages/rulesets/src/oas/__tests__/operation-success-response.test.ts
+++ b/packages/rulesets/src/oas/__tests__/operation-success-response.test.ts
@@ -131,4 +131,81 @@ testRule('operation-success-response', [
     },
     errors: [],
   },
+
+  {
+    name: 'oas2 document has 2XX and 3XX responses set',
+    document: {
+      swagger: '2.0',
+      paths: {
+        '/path': {
+          get: {
+            responses: {
+              '3XX': {},
+            },
+          },
+          post: {
+            responses: {
+              '2XX': {},
+            },
+          },
+        },
+      },
+    },
+    errors: [
+      {
+        message: 'Operation must have at least one "2xx" or "3xx" response.',
+        path: ['paths', '/path', 'get', 'responses'],
+        severity: DiagnosticSeverity.Warning,
+      },
+      {
+        message: 'Operation must have at least one "2xx" or "3xx" response.',
+        path: ['paths', '/path', 'post', 'responses'],
+        severity: DiagnosticSeverity.Warning,
+      },
+    ],
+  },
+
+  {
+    name: 'oas 3.0.x document has 2XX and 3XX responses set',
+    document: {
+      openapi: '3.0.3',
+      paths: {
+        '/path': {
+          get: {
+            responses: {
+              '3XX': {},
+            },
+          },
+          post: {
+            responses: {
+              '2XX': {},
+            },
+          },
+        },
+      },
+    },
+    errors: [],
+  },
+
+  {
+    name: 'oas 3.1.x document has 2XX and 3XX responses set',
+    document: {
+      openapi: '3.1.0',
+      paths: {
+        '/path': {
+          get: {
+            responses: {
+              '3XX': {},
+            },
+          },
+          post: {
+            responses: {
+              '2XX': {},
+            },
+          },
+        },
+      },
+    },
+    errors: [],
+  },
 ]);