Repository: asyncapi/parser-js
Pull request #825 (2023-08-01 11:24:52)

feat: enable AsyncAPI v3 validation
Tests are failing. Read comments below.

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_parser-js&pullRequest=825)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_parser-js&pullRequest=825)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825&metric=duplicated_lines_density&view=list) No Duplication information


smoya: Validation fails with the following errors:

```json
[
   {
      "code":"asyncapi-document-resolved",
      "message":"'someOperation1' property must match exactly one schema in oneOf",
      "path":[
         "operations",
         "someOperation1"
      ],
      "range":{
         "end":{
            "character":46,
            "line":20
         },
         "start":{
            "character":21,
            "line":10
         }
      },
      "severity":0
   },
   {
      "code":"asyncapi-document-resolved",
      "message":"'someOperation2' property must match exactly one schema in oneOf",
      "path":[
         "operations",
         "someOperation2"
      ],
      "range":{
         "end":{
            "character":46,
            "line":35
         },
         "start":{
            "character":21,
            "line":24
         }
      },
      "severity":0
   }
]
```
The validation rule that fails is the `asyncapi-document-resolved`, which checks if the AsyncAPI document is valid (using JSON Schema) **after resolving references**.
Tests are failing due to the fact `channel` property under `Operation` one should be just a reference to a channel and not a channel object directly, and the rule applies after dereferencing the channel object, which becomes an invalid document since the expected channel value should be a reference.

Thanks @jonaslagoni for helping (and actually finding) that out.

The error messages shown above are also wrong. We do some error filtering as stated in https://github.com/asyncapi/parser-js/blob/master/src/ruleset/functions/documentStructure.ts#L20, and it seems we are doing too much and, in this case, we skip the real error, which can be found in the list of errors prior to the filtering:

```json
[
  {
    "instancePath": "/operations/someOperation1",
    "schemaPath": "#/required",
    "keyword": "required",
    "params": {
      "missingProperty": "$ref"
    },
    "message": "must have required property '$ref'"
  },
  {
    "instancePath": "/operations/someOperation1/channel",
    "schemaPath": "#/required",
    "keyword": "required",
    "params": {
      "missingProperty": "$ref"
    },
    "message": "must have required property '$ref'"
  },
  {
    "instancePath": "/operations/someOperation1",
    "schemaPath": "#/additionalProperties/oneOf",
    "keyword": "oneOf",
    "params": {
      "passingSchemas": null
    },
    "message": "must match exactly one schema in oneOf"
  },
  {
    "instancePath": "/operations/someOperation2",
    "schemaPath": "#/required",
    "keyword": "required",
    "params": {
      "missingProperty": "$ref"
    },
    "message": "must have required property '$ref'"
  },
  {
    "instancePath": "/operations/someOperation2/channel",
    "schemaPath": "#/required",
    "keyword": "required",
    "params": {
      "missingProperty": "$ref"
    },
    "message": "must have required property '$ref'"
  },
  {
    "instancePath": "/operations/someOperation2",
    "schemaPath": "#/additionalProperties/oneOf",
    "keyword": "oneOf",
    "params": {
      "passingSchemas": null
    },
    "message": "must match exactly one schema in oneOf"
  }
]
```

The expected error should be the ones with an `instancePath` pointing to the operation channel, where the message says must have required property '$ref'.


smoya: Based on my prior comment, this is what we need to do next in this PR:

1. To fix the bug in the error messages filter, so the error that stays is the one that points to each operation.channel field.
2. To skip validation of operation.channel for v3 in the `asyncapi-document-resolved` (still will happen in `asyncapi-document-unresolved`).

Alternatives for the second point will be to either allow by JSON Schema to define `operation.channel` as object aswell, which will then incompatible with the current spec, or rather to keep different JSON Schema files, one for pre and another for post reference resolution. I think this is also discarded as it is a lot of work and effort.
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_parser-js&pullRequest=825)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_parser-js&pullRequest=825)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_parser-js&pullRequest=825)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_parser-js&pullRequest=825)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825&metric=duplicated_lines_density&view=list) No Duplication information


smoya: I’m thinking that we should not go in the direction of manipulating the schema for the operation.channel in the mid term. Having to manipulate the schema from now on is not either good nor maintainable IMHO.

The alternative is to remove the `asyncapi-document-resolved` rule forever and just relay on the ``asyncapi-document-unresolved`. The point is that we are filtering errors [here](https://github.com/smoya/parser-js/blob/feat/enableV3Validation/src/ruleset/functions/documentStructure.ts#L86-L90), so we would need to return them and not filter. What is the real implication of it?

WDYT @magicmatatjahu @jonaslagoni 
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_parser-js&pullRequest=825)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825&metric=duplicated_lines_density&view=list) No Duplication information


magicmatatjahu: @smoya Issues are fixed :)
smoya: > @smoya Issues are fixed :)

Thanks for the help @magicmatatjahu 🙌 🙌 !!

I still have [this concern](https://github.com/asyncapi/parser-js/pull/825#issuecomment-1667682844) and I would love to know your opinion as you are the one with most knowledge on this code.

I would like to have this PR merged but to keep iterating after so we go for a better and maintainable alternative.

cc @jonaslagoni @Amzani 
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_parser-js&pullRequest=825)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825&metric=duplicated_lines_density&view=list) No Duplication information


smoya: >  There is also a idea to allow inline objects in spec itself - but then 3.1.0 is needed.

Yeah, this is something I want to bring up and discuss with the community.

> `asyncapi-document-resolved` is very needed because we should also check the correctness of imported objects from external files/https (external refs). Without this rule how to check if something imported is ok?

Yeah, I agree there is no better alternative and, for sure, this validation is an important addition on top of the basic schema validation.

Maybe we could work on an automation, for every time we find a property with **only** a reference to a `Reference Object` , to modify that schema to allow the object as well (oneOf or just right the definition object as you did).
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_parser-js&pullRequest=825)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_parser-js&pullRequest=825)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_parser-js&pullRequest=825&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_parser-js&pullRequest=825&metric=duplicated_lines_density&view=list) No Duplication information


jonaslagoni: /rtm

Patch:
diff --git a/src/ruleset/functions/documentStructure.ts b/src/ruleset/functions/documentStructure.ts
index a00268d..2b8db6e 100644
--- a/src/ruleset/functions/documentStructure.ts
+++ b/src/ruleset/functions/documentStructure.ts
@@ -1,12 +1,17 @@
+/* eslint-disable sonarjs/no-duplicate-string */
+
 import specs from '@asyncapi/specs';
 import { createRulesetFunction } from '@stoplight/spectral-core';
 import { schema as schemaFn } from '@stoplight/spectral-functions';
 
+import { AsyncAPIFormats } from '../formats';
+import { getSemver } from '../../utils';
+
 import type { ErrorObject } from 'ajv';
 import type { IFunctionResult, Format } from '@stoplight/spectral-core';
-import { AsyncAPIFormats } from '../formats';
 
 type AsyncAPIVersions = keyof typeof specs.schemas;
+type RawSchema = Record<string, unknown>;
 
 function shouldIgnoreError(error: ErrorObject): boolean {
   return (
@@ -45,24 +50,52 @@ function prepareResults(errors: ErrorObject[]): void {
   }
 }
 
-function getCopyOfSchema(version: AsyncAPIVersions): Record<string, unknown> {
-  return JSON.parse(JSON.stringify(specs.schemas[version])) as Record<string, unknown>;
+// this is needed because some v3 object fields are expected to be only `$ref` to other objects. 
+// In order to validate resolved references, we modify those schemas and instead allow the definition of the object
+function prepareV3ResolvedSchema(copied: any): any {
+  // channel object
+  const channelObject = copied.definitions['http://asyncapi.com/definitions/3.0.0/channel.json'];
+  channelObject.properties.servers.items.$ref = 'http://asyncapi.com/definitions/3.0.0/server.json';
+
+  // operation object
+  const operationSchema = copied.definitions['http://asyncapi.com/definitions/3.0.0/operation.json'];
+  operationSchema.properties.channel.$ref = 'http://asyncapi.com/definitions/3.0.0/channel.json';
+  operationSchema.properties.messages.items.$ref = 'http://asyncapi.com/definitions/3.0.0/messageObject.json';
+
+  // operation reply object
+  const operationReplySchema = copied.definitions['http://asyncapi.com/definitions/3.0.0/operationReply.json'];
+  operationReplySchema.properties.channel.$ref = 'http://asyncapi.com/definitions/3.0.0/channel.json';
+  operationReplySchema.properties.messages.items.$ref = 'http://asyncapi.com/definitions/3.0.0/messageObject.json';
+
+  return copied;
+}
+
+function getCopyOfSchema(version: AsyncAPIVersions): RawSchema {
+  return JSON.parse(JSON.stringify(specs.schemas[version])) as RawSchema;
 }
 
-const serializedSchemas = new Map<AsyncAPIVersions, Record<string, unknown>>();
-function getSerializedSchema(version: AsyncAPIVersions): Record<string, unknown> {
-  const schema = serializedSchemas.get(version);
+const serializedSchemas = new Map<AsyncAPIVersions, RawSchema>();
+function getSerializedSchema(version: AsyncAPIVersions, resolved: boolean): RawSchema {
+  const serializedSchemaKey = resolved ? `${version}-resolved` : `${version}-unresolved`;
+  const schema = serializedSchemas.get(serializedSchemaKey as AsyncAPIVersions);
   if (schema) {
     return schema;
   }
 
   // Copy to not operate on the original json schema - between imports (in different modules) we operate on this same schema.
-  const copied = getCopyOfSchema(version) as { definitions: Record<string, unknown> };
+  let copied = getCopyOfSchema(version) as { '$id': string, definitions: RawSchema };
   // Remove the meta schemas because they are already present within Ajv, and it's not possible to add duplicated schemas.
   delete copied.definitions['http://json-schema.org/draft-07/schema'];
   delete copied.definitions['http://json-schema.org/draft-04/schema'];
+  // Spectral caches the schemas using '$id' property
+  copied['$id'] = copied['$id'].replace('asyncapi.json', `asyncapi-${resolved ? 'resolved' : 'unresolved'}.json`);
+
+  const { major } = getSemver(version);
+  if (resolved && major === 3) {
+    copied = prepareV3ResolvedSchema(copied);
+  }
 
-  serializedSchemas.set(version, copied);
+  serializedSchemas.set(serializedSchemaKey as AsyncAPIVersions, copied);
   return copied;
 }
 
@@ -80,10 +113,10 @@ function filterRefErrors(errors: IFunctionResult[], resolved: boolean) {
     });
 }
 
-export function getSchema(docFormats: Set<Format>): Record<string, any> | void {
+export function getSchema(docFormats: Set<Format>, resolved: boolean): Record<string, any> | void {
   for (const [version, format] of AsyncAPIFormats) {
     if (docFormats.has(format)) {
-      return getSerializedSchema(version as AsyncAPIVersions);
+      return getSerializedSchema(version as AsyncAPIVersions, resolved);
     }
   }
 }
@@ -107,16 +140,17 @@ export const documentStructure = createRulesetFunction<unknown, { resolved: bool
       return;
     }
 
-    const schema = getSchema(formats);
+    const resolved = options.resolved;
+    const schema = getSchema(formats, resolved);
     if (!schema) {
       return;
     }
 
-    const errors = schemaFn(targetVal, { allErrors: true, schema, prepareResults: options.resolved ? prepareResults : undefined }, context);
+    const errors = schemaFn(targetVal, { allErrors: true, schema, prepareResults: resolved ? prepareResults : undefined }, context);
     if (!Array.isArray(errors)) {
       return;
     }
 
-    return filterRefErrors(errors, options.resolved);
+    return filterRefErrors(errors, resolved);
   },
 );
diff --git a/src/ruleset/ruleset.ts b/src/ruleset/ruleset.ts
index c24ebfd..105621f 100644
--- a/src/ruleset/ruleset.ts
+++ b/src/ruleset/ruleset.ts
@@ -1,5 +1,3 @@
-
-import { lastVersion } from '../constants';
 import { truthy, schema } from '@stoplight/spectral-functions';
 
 import { documentStructure } from './functions/documentStructure';
@@ -7,10 +5,11 @@ import { internal } from './functions/internal';
 import { isAsyncAPIDocument } from './functions/isAsyncAPIDocument';
 import { unusedComponent } from './functions/unusedComponent';
 import { AsyncAPIFormats } from './formats';
+import { lastVersion } from '../constants';
 
 export const coreRuleset = {
   description: 'Core AsyncAPI x.x.x ruleset.',
-  formats: AsyncAPIFormats.filterByMajorVersions(['2']).formats(), // Validation for AsyncAPI v3 is still WIP.
+  formats: AsyncAPIFormats.formats(),
   rules: {
     /**
      * Root Object rules
@@ -81,7 +80,7 @@ export const coreRuleset = {
 
 export const recommendedRuleset = {
   description: 'Recommended AsyncAPI x.x.x ruleset.',
-  formats: AsyncAPIFormats.filterByMajorVersions(['2']).formats(), // Validation for AsyncAPI v3 is still WIP.
+  formats: AsyncAPIFormats.filterByMajorVersions(['2']).formats(), // Recommended validation for AsyncAPI v3 is still WIP.
   rules: {
     /**
      * Root Object rules


Test patch:
diff --git a/test/custom-operations/apply-traits-v3.spec.ts b/test/custom-operations/apply-traits-v3.spec.ts
index 19f627c..d4cbfae 100644
--- a/test/custom-operations/apply-traits-v3.spec.ts
+++ b/test/custom-operations/apply-traits-v3.spec.ts
@@ -13,8 +13,15 @@ describe('custom operations - apply traits v3', function() {
         title: 'Valid AsyncApi document',
         version: '1.0',
       },
+      channels: {
+        channel1: {}
+      },
       operations: {
         someOperation1: {
+          action: 'send',
+          channel: {
+            $ref: '#/channels/channel1'
+          },
           traits: [
             {
               description: 'some description' 
@@ -25,6 +32,10 @@ describe('custom operations - apply traits v3', function() {
           ]
         },
         someOperation2: {
+          action: 'send',
+          channel: {
+            $ref: '#/channels/channel1'
+          },
           description: 'root description',
           traits: [
             {
@@ -38,16 +49,18 @@ describe('custom operations - apply traits v3', function() {
       }
     };
     const { document, diagnostics } = await parser.parse(documentRaw);
+    expect(diagnostics).toHaveLength(0);
+    
     const v3Document = document as AsyncAPIDocumentV3;
     expect(v3Document).toBeInstanceOf(AsyncAPIDocumentV3);
 
     const someOperation1 = v3Document?.json()?.operations?.someOperation1;
     delete someOperation1?.traits;
-    expect(someOperation1).toEqual({ description: 'another description' });
+    expect(someOperation1).toEqual({ action: 'send', channel: {}, description: 'another description' });
 
     const someOperation2 = v3Document?.json()?.operations?.someOperation2;
     delete someOperation2?.traits;
-    expect(someOperation2).toEqual({ description: 'root description' });
+    expect(someOperation2).toEqual({ action: 'send', channel: {}, description: 'root description' });
   });
 
   it('should apply traits to messages (channels)', async function() {
@@ -59,8 +72,8 @@ describe('custom operations - apply traits v3', function() {
       },
       channels: {
         someChannel1: {
-          messages: [
-            {
+          messages: {
+            someMessage: {
               traits: [
                 {
                   messageId: 'traitMessageId',
@@ -71,11 +84,11 @@ describe('custom operations - apply traits v3', function() {
                 }
               ]
             }
-          ]
+          }
         },
         someChannel2: {
-          messages: [
-            {
+          messages: {
+            someMessage: {
               messageId: 'rootMessageId',
               description: 'root description',
               traits: [
@@ -88,20 +101,20 @@ describe('custom operations - apply traits v3', function() {
                 }
               ]
             }
-          ]
+          }
         }
       }
     };
-    const { document } = await parser.parse(documentRaw);
+    const { diagnostics, document } = await parser.parse(documentRaw);
     
     const v3Document = document as AsyncAPIDocumentV3;
     expect(v3Document).toBeInstanceOf(AsyncAPIDocumentV3);
 
-    const message1 = v3Document?.json()?.channels?.someChannel1?.messages?.[0];
+    const message1 = v3Document?.json()?.channels?.someChannel1?.messages?.someMessage;
     delete (message1 as v3.MessageObject)?.traits;
     expect(message1).toEqual({ messageId: 'traitMessageId', description: 'another description', 'x-parser-message-name': 'traitMessageId' });
 
-    const message2 = v3Document?.json()?.channels?.someChannel2?.messages?.[0];
+    const message2 = v3Document?.json()?.channels?.someChannel2?.messages?.someMessage;
     delete (message2 as v3.MessageObject)?.traits;
     expect(message2).toEqual({ messageId: 'rootMessageId', description: 'root description', 'x-parser-message-name': 'rootMessageId' });
   });
diff --git a/test/parse.spec.ts b/test/parse.spec.ts
index aec7002..e8c941c 100644
--- a/test/parse.spec.ts
+++ b/test/parse.spec.ts
@@ -60,8 +60,8 @@ describe('parse()', function() {
     };
     const { document, diagnostics } = await parser.parse(documentRaw);
     
-    expect(document).toBeInstanceOf(AsyncAPIDocumentV3);
-    expect(diagnostics.length === 0).toEqual(true); // Validation in v3 is still not enabled. This test will intentionally fail once that changes.
+    expect(document === undefined).toEqual(true);
+    expect(diagnostics.length > 0).toEqual(true);
   });
 
   it('should return extras', async function() {
diff --git a/test/ruleset/rules/asyncapi-document-resolved.spec.ts b/test/ruleset/rules/asyncapi-document-resolved.spec.ts
index 7369c1d..b900621 100644
--- a/test/ruleset/rules/asyncapi-document-resolved.spec.ts
+++ b/test/ruleset/rules/asyncapi-document-resolved.spec.ts
@@ -15,7 +15,7 @@ testRule('asyncapi-document-resolved', [
   },
 
   {
-    name: 'invalid case (channels property is missing)',
+    name: 'invalid case for 2.X.X (channels property is missing)',
     document: {
       asyncapi: '2.0.0',
       info: {
@@ -32,7 +32,7 @@ testRule('asyncapi-document-resolved', [
   },
 
   {
-    name: 'valid case (case when other errors should also occur but we filter them out)',
+    name: 'valid case for 2.X.X (case validating $ref resolution works as expected)',
     document: {
       asyncapi: '2.0.0',
       info: {
@@ -309,4 +309,89 @@ testRule('asyncapi-document-resolved', [
     },
     errors: [],
   },
+
+  {
+    name: 'valid case (3.0.0 version)',
+    document: {
+      asyncapi: '3.0.0',
+      info: {
+        title: 'Signup service example (internal)',
+        version: '0.1.0',
+      },
+      channels: {
+        'user/signedup': {
+          address: 'user/signedup',
+          messages: {
+            'subscribe.message': {
+              payload: {}
+            }
+          }
+        }
+      },
+      operations: {
+        'user/signedup.subscribe': {
+          action: 'send',
+          channel: {
+            address: 'user/signedup',
+            messages: {
+              'subscribe.message': {
+                payload: {}
+              }
+            }
+          },
+          messages: [
+            {
+              payload: {}
+            }
+          ]
+        }
+      },
+    },
+    errors: [],
+  },
+
+  {
+    name: 'invalid case for 3.X.X (info.version property is missing)',
+    document: {
+      asyncapi: '3.0.0',
+      info: {
+        title: 'Valid AsyncApi document',
+      },
+    },
+    errors: [
+      {
+        message: '"info" property must have required property "version"',
+        severity: DiagnosticSeverity.Error,
+      },
+    ],
+  },
+
+  {
+    name: 'valid case for 3.X.X (case validating $ref resolution works as expected)',
+    document: {
+      asyncapi: '3.0.0',
+      info: {
+        title: 'Signup service example (internal)',
+        version: '0.1.0',
+      },
+      channels: {
+        userSignedup: {
+          address: 'user/signedup',
+          messages: {
+            'subscribe.message': {
+              $ref: '#/components/messages/testMessage'
+            }
+          }
+        }
+      },
+      components: {
+        messages: {
+          testMessage: {
+            payload: {}
+          }
+        }
+      }
+    },
+    errors: [],
+  },
 ]);
diff --git a/test/ruleset/rules/asyncapi-document-unresolved.spec.ts b/test/ruleset/rules/asyncapi-document-unresolved.spec.ts
index 7787605..91e1cdd 100644
--- a/test/ruleset/rules/asyncapi-document-unresolved.spec.ts
+++ b/test/ruleset/rules/asyncapi-document-unresolved.spec.ts
@@ -2,7 +2,7 @@ import { testRule, DiagnosticSeverity } from '../tester';
 
 testRule('asyncapi-document-unresolved', [
   {
-    name: 'valid case',
+    name: 'valid case for 2.X.X',
     document: {
       asyncapi: '2.0.0',
       info: {
@@ -28,7 +28,7 @@ testRule('asyncapi-document-unresolved', [
   },
 
   {
-    name: 'invalid case (reference for operation object is not allowed)',
+    name: 'invalid case for 2.X.X (reference for operation object is not allowed)',
     document: {
       asyncapi: '2.0.0',
       info: {
@@ -58,26 +58,49 @@ testRule('asyncapi-document-unresolved', [
   },
 
   {
-    name: 'invalid case (case when other errors should also occur but we filter them out - required info field is omitted)',
+    name: 'valid case for 3.X.X',
     document: {
-      asyncapi: '2.0.0',
+      asyncapi: '3.0.0',
+      info: {
+        title: 'Valid AsyncApi document',
+        version: '1.0',
+      },
       channels: {
-        someChannel: {
-          publish: {
-            $ref: '#/components/x-operations/someOperation',
-          },
+        userSignedup: {
+          address: 'user/signedup',
+          messages: {
+            'subscribe.message': {
+              $ref: '#/components/messages/testMessage'
+            }
+          }
+        }
+      },
+      components: {
+        messages: {
+          someMessage: {},
         },
       },
+    },
+    errors: [],
+  },
+
+  {
+    name: 'invalid case for 3.X.X (reference for info object is not allowed)',
+    document: {
+      asyncapi: '3.0.0',
+      info: {
+        $ref: '#/components/x-titles/someTitle'
+      },
       components: {
-        'x-operations': {
-          someOperation: {},
+        'x-titles': {
+          someTitle: 'some-title',
         },
       },
     },
     errors: [
       {
         message: 'Referencing in this place is not allowed',
-        path: ['channels', 'someChannel', 'publish'],
+        path: ['info'],
         severity: DiagnosticSeverity.Error,
       },
     ],