diff --git a/src/domains/models/SpecificationFile.ts b/src/domains/models/SpecificationFile.ts index 5370f6e67..18c19800c 100644 --- a/src/domains/models/SpecificationFile.ts +++ b/src/domains/models/SpecificationFile.ts @@ -237,7 +237,8 @@ export async function fileExists(name: string): Promise { return true; } - const extension = name.split('.')[1]; + // Bug fix #1940: Use path.extname() for multi-dot filenames (e.g., my.asyncapi.yaml) + const extension = path.extname(name).slice(1); const allowedExtenstion = ['yml', 'yaml', 'json']; diff --git a/src/domains/services/validation.service.ts b/src/domains/services/validation.service.ts index 11ac45fc4..ad91ece95 100644 --- a/src/domains/services/validation.service.ts +++ b/src/domains/services/validation.service.ts @@ -70,7 +70,8 @@ const convertGitHubWebUrl = (url: string): string => { // Handle GitHub web URLs like: https://github.com/owner/repo/blob/branch/path // eslint-disable-next-line no-useless-escape - const githubWebPattern = /^https:\/\/github\.com\/([^\/]+)\/([^\/]+)\/blob\/([^\/]+)\/(.+)$/; + // Bug fix #1940: Support slash-based branch names (e.g., feature/new-validation) + const githubWebPattern = /^https:\/\/github\.com\/([^\/]+)\/([^\/]+)\/blob\/(.+?)\/(.+)$/; const match = urlWithoutFragment.match(githubWebPattern); if (match) { diff --git a/test/unit/services/validation.service.test.ts b/test/unit/services/validation.service.test.ts index 401381bfe..2179e9cda 100644 --- a/test/unit/services/validation.service.test.ts +++ b/test/unit/services/validation.service.test.ts @@ -253,5 +253,46 @@ describe('ValidationService', () => { expect(result.data?.diagnostics).to.be.an('array'); } }); + + it('should handle GitHub URLs with slash-based branch names (e.g., feature/new-validation)', async () => { + // Test AsyncAPI document with slash-based branch name reference + const asyncAPIWithSlashBranch = `{ + "asyncapi": "2.6.0", + "info": { + "title": "Test Service with Slash Branch", + "version": "1.0.0" + }, + "channels": { + "user/event": { + "publish": { + "message": { + "payload": { + "$ref": "https://github.com/asyncapi/spec/blob/feature/new-validation/examples/streetlights.yml#/channels/light/measured/message/payload" + } + } + } + } + } +}`; + + const specFile = new Specification(asyncAPIWithSlashBranch); + const options = { + 'diagnostics-format': 'stylish' as const + }; + + const result = await validationService.validateDocument(specFile, options); + + // Validation should execute successfully (may be invalid due to 404, but URL parsing works) + expect(result.success).to.equal(true); + if (result.success) { + expect(result.data).to.have.property('status'); + expect(result.data).to.have.property('diagnostics'); + // Should not have URL parsing errors - the branch name should be correctly extracted + const urlParseErrors = result.data?.diagnostics?.filter((d: any) => + d.message?.includes('Failed to parse URL') || d.message?.includes('Invalid URL') + ); + expect(urlParseErrors).to.have.lengthOf(0); + } + }); }); });