From 37ddc9b5e8b44dcf05cfbfe621e29ed0a4a397ee Mon Sep 17 00:00:00 2001 From: yashkohli88 Date: Wed, 18 Dec 2024 21:01:48 +0530 Subject: [PATCH 1/6] Updated testcases related to Origins API --- .../e2e-test-service/originsTest.js | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 tools/integration/test/integration/e2e-test-service/originsTest.js diff --git a/tools/integration/test/integration/e2e-test-service/originsTest.js b/tools/integration/test/integration/e2e-test-service/originsTest.js new file mode 100644 index 0000000..5ba5b99 --- /dev/null +++ b/tools/integration/test/integration/e2e-test-service/originsTest.js @@ -0,0 +1,108 @@ +const assert = require('assert') +const { callFetch } = require('../../../lib/fetch') +const { devApiBaseUrl, prodApiBaseUrl, getComponents, definition } = require('../testConfig') + +const ORIGIN_EXCLUSIONS = ['go/golang', 'debsrc/debian', 'maven/mavengoogle'] +const ORIGIN_REVISIONS_EXCLUSIONS = ['debsrc/debian'] +const EXTRA_COMPONENTS = ['maven/mavencentral/org.apache.httpcomponents', 'maven/mavencentral/org.apache.httpcompon'] + +;(async function validateOriginsApi() { + const components = await getComponents() + + describe('Validate origins API between dev and prod', function () { + this.timeout(definition.timeout) + + // Rest interval to avoid overloading the servers + afterEach(() => new Promise(resolve => setTimeout(resolve, definition.timeout / 2))) + + components.forEach(component => { + if (shouldCheckOrigins(component)) { + it(`checks origins API response for ${component}`, () => fetchAndCompareOrigins(component)) + } + if (shouldCheckOriginsWithRevisions(component)) { + it(`checks origins API with revisions response for ${component}`, () => + fetchAndCompareOriginsWithRevisions(component)) + } + }) + + EXTRA_COMPONENTS.forEach(component => { + if (shouldCheckOrigins(component)) { + it(`checks origins API response for ${component}`, () => fetchAndCompareOrigins(component)) + } + }) + }) +})() + +function extractIds(response) { + return response.map(item => item.id) +} + +function assertOriginsMatch(actual, expected) { + const sortedActualIds = extractIds(actual).sort() + const sortedExpectedIds = extractIds(expected).sort() + + assert.deepStrictEqual(sortedActualIds, sortedExpectedIds) +} + +function isNotInExclusionList(list, coordinate) { + return !list.some(excluded => coordinate.includes(excluded)) +} + +function shouldCheckOrigins(coordinate) { + return isNotInExclusionList(ORIGIN_EXCLUSIONS, coordinate) +} + +function shouldCheckOriginsWithRevisions(coordinate) { + return isNotInExclusionList(ORIGIN_REVISIONS_EXCLUSIONS, coordinate) +} + +function resolveProviderType(type, provider) { + switch (type) { + case 'git': + return 'github' + case 'gem': + return 'rubygems' + case 'conda': + return `conda/${provider}` + case 'maven': + return provider === 'mavengoogle' ? provider : type + default: + return type + } +} + +function parseCoordinates(coordinates) { + const [type, provider, namespaceToken, name, version] = coordinates.split('/') + const namespace = namespaceToken === '-' ? '' : namespaceToken + return { type, provider, namespace, name, version } +} +function buildCondaURL(coordinates) { + const { type, provider, name } = parseCoordinates(coordinates) + return `${type}/${provider}/${name}` +} + +function buildOriginUrl(coordinates) { + const { type, provider, namespace, name } = parseCoordinates(coordinates) + const resolvedType = resolveProviderType(type, provider) + return `${resolvedType}${namespace ? `/${namespace}` : ''}${name ? `/${name}` : ''}` +} + +async function fetchAndCompareOriginsWithRevisions(coordinates) { + const originUrl = buildOriginUrl(coordinates) + const [devResponse, prodResponse] = await Promise.all([ + callFetch(`${devApiBaseUrl}/origins/${originUrl}/revisions`).then(res => res.json()), + callFetch(`${prodApiBaseUrl}/origins/${originUrl}/revisions`).then(res => res.json()) + ]) + assertOriginsMatch(devResponse, prodResponse) +} + +async function fetchAndCompareOrigins(coordinates) { + const originUrl = coordinates.startsWith('conda/') ? buildCondaURL(coordinates) : buildOriginUrl(coordinates) + + const [devResponse, prodResponse] = await Promise.all([ + callFetch(`${devApiBaseUrl}/origins/${originUrl}`).then(res => res.json()), + callFetch(`${prodApiBaseUrl}/origins/${originUrl}`).then(res => res.json()) + ]) + + assertOriginsMatch(devResponse, prodResponse) +} From ac4ff444c61a7ee8635353762ac5e6b438cf54a3 Mon Sep 17 00:00:00 2001 From: yashkohli88 Date: Fri, 20 Dec 2024 18:55:29 +0530 Subject: [PATCH 2/6] Updated testcase for maven and gradle --- .../e2e-test-service/originsTest.js | 89 ++++++++++--------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/tools/integration/test/integration/e2e-test-service/originsTest.js b/tools/integration/test/integration/e2e-test-service/originsTest.js index 5ba5b99..3d302d8 100644 --- a/tools/integration/test/integration/e2e-test-service/originsTest.js +++ b/tools/integration/test/integration/e2e-test-service/originsTest.js @@ -2,34 +2,40 @@ const assert = require('assert') const { callFetch } = require('../../../lib/fetch') const { devApiBaseUrl, prodApiBaseUrl, getComponents, definition } = require('../testConfig') -const ORIGIN_EXCLUSIONS = ['go/golang', 'debsrc/debian', 'maven/mavengoogle'] -const ORIGIN_REVISIONS_EXCLUSIONS = ['debsrc/debian'] -const EXTRA_COMPONENTS = ['maven/mavencentral/org.apache.httpcomponents', 'maven/mavencentral/org.apache.httpcompon'] +const ORIGIN_EXCLUSION_LIST = ['go/golang', 'debsrc/debian', 'maven/mavengoogle'] +const ORIGIN_REVISIONS_EXCLUSION_LIST = ['debsrc/debian'] + +const MAVEN_COMPONENT_GROUP_ID = 'maven/mavencentral/org.apache.httpcomponents' +const MAVEN_COMPONENT_PARTIAL_GROUP_ID = 'maven/mavencentral/org.apache.httpcompon' +const GRADLE_COMPONENT_ENDPOINT = 'gradleplugin/io.github.lognet.grpc-spring-boot' ;(async function validateOriginsApi() { const components = await getComponents() - describe('Validate origins API between dev and prod', function () { + describe('Validate Origins API Between Dev and Prod', function () { this.timeout(definition.timeout) - // Rest interval to avoid overloading the servers afterEach(() => new Promise(resolve => setTimeout(resolve, definition.timeout / 2))) - components.forEach(component => { - if (shouldCheckOrigins(component)) { - it(`checks origins API response for ${component}`, () => fetchAndCompareOrigins(component)) - } - if (shouldCheckOriginsWithRevisions(component)) { - it(`checks origins API with revisions response for ${component}`, () => - fetchAndCompareOriginsWithRevisions(component)) - } + components.filter(isOriginAllowed).forEach(component => { + it(`Validates Origins API response for ${component}`, () => compareOrigins(component)) }) - EXTRA_COMPONENTS.forEach(component => { - if (shouldCheckOrigins(component)) { - it(`checks origins API response for ${component}`, () => fetchAndCompareOrigins(component)) - } + components.filter(isOriginWithRevisionsAllowed).forEach(component => { + it(`Validates Origins API response with revisions for ${component}`, () => compareOriginsWithRevisions(component)) }) + + it('Validates Origins API response for a Maven component with only a group ID', () => + compareOrigins(MAVEN_COMPONENT_GROUP_ID)) + + it('Validates Origins API response for a Maven component with a partial group ID for suggestion checks', () => + compareOrigins(MAVEN_COMPONENT_PARTIAL_GROUP_ID)) + + it('Validates Origins API response for a Gradle plugin component', () => + compareEndpoints(GRADLE_COMPONENT_ENDPOINT)) + + it('Validates Origins API with revisions response for a Gradle plugin component', () => + compareEndpoints(`${GRADLE_COMPONENT_ENDPOINT}/revisions`)) }) })() @@ -37,26 +43,26 @@ function extractIds(response) { return response.map(item => item.id) } -function assertOriginsMatch(actual, expected) { +function assertResponsesMatch(actual, expected) { const sortedActualIds = extractIds(actual).sort() const sortedExpectedIds = extractIds(expected).sort() assert.deepStrictEqual(sortedActualIds, sortedExpectedIds) } -function isNotInExclusionList(list, coordinate) { - return !list.some(excluded => coordinate.includes(excluded)) +function isCoordinateAllowed(coordinate, exclusionList) { + return !exclusionList.some(excluded => coordinate.includes(excluded)) } -function shouldCheckOrigins(coordinate) { - return isNotInExclusionList(ORIGIN_EXCLUSIONS, coordinate) +function isOriginAllowed(coordinate) { + return isCoordinateAllowed(coordinate, ORIGIN_EXCLUSION_LIST) } -function shouldCheckOriginsWithRevisions(coordinate) { - return isNotInExclusionList(ORIGIN_REVISIONS_EXCLUSIONS, coordinate) +function isOriginWithRevisionsAllowed(coordinate) { + return isCoordinateAllowed(coordinate, ORIGIN_REVISIONS_EXCLUSION_LIST) } -function resolveProviderType(type, provider) { +function getProviderType(type, provider) { switch (type) { case 'git': return 'github' @@ -72,37 +78,36 @@ function resolveProviderType(type, provider) { } function parseCoordinates(coordinates) { - const [type, provider, namespaceToken, name, version] = coordinates.split('/') - const namespace = namespaceToken === '-' ? '' : namespaceToken + const [type, provider, namespaceOrEmpty, name, version] = coordinates.split('/') + const namespace = namespaceOrEmpty === '-' ? '' : namespaceOrEmpty return { type, provider, namespace, name, version } } -function buildCondaURL(coordinates) { + +function buildCondaUrl(coordinates) { const { type, provider, name } = parseCoordinates(coordinates) return `${type}/${provider}/${name}` } function buildOriginUrl(coordinates) { const { type, provider, namespace, name } = parseCoordinates(coordinates) - const resolvedType = resolveProviderType(type, provider) + const resolvedType = getProviderType(type, provider) return `${resolvedType}${namespace ? `/${namespace}` : ''}${name ? `/${name}` : ''}` } -async function fetchAndCompareOriginsWithRevisions(coordinates) { - const originUrl = buildOriginUrl(coordinates) +async function compareEndpoints(endpoint) { const [devResponse, prodResponse] = await Promise.all([ - callFetch(`${devApiBaseUrl}/origins/${originUrl}/revisions`).then(res => res.json()), - callFetch(`${prodApiBaseUrl}/origins/${originUrl}/revisions`).then(res => res.json()) + callFetch(`${devApiBaseUrl}/origins/${endpoint}`).then(res => res.json()), + callFetch(`${prodApiBaseUrl}/origins/${endpoint}`).then(res => res.json()) ]) - assertOriginsMatch(devResponse, prodResponse) + assertResponsesMatch(devResponse, prodResponse) } -async function fetchAndCompareOrigins(coordinates) { - const originUrl = coordinates.startsWith('conda/') ? buildCondaURL(coordinates) : buildOriginUrl(coordinates) - - const [devResponse, prodResponse] = await Promise.all([ - callFetch(`${devApiBaseUrl}/origins/${originUrl}`).then(res => res.json()), - callFetch(`${prodApiBaseUrl}/origins/${originUrl}`).then(res => res.json()) - ]) +async function compareOriginsWithRevisions(coordinates) { + const originUrl = buildOriginUrl(coordinates) + compareEndpoints(`${originUrl}/revisions`) +} - assertOriginsMatch(devResponse, prodResponse) +async function compareOrigins(coordinates) { + const originUrl = coordinates.startsWith('conda/') ? buildCondaUrl(coordinates) : buildOriginUrl(coordinates) + compareEndpoints(`${originUrl}`) } From f14fdb52aeb427fee680fe37581fa7f29d0c1f25 Mon Sep 17 00:00:00 2001 From: yashkohli88 Date: Wed, 8 Jan 2025 20:10:08 +0530 Subject: [PATCH 3/6] Refactored code for better performance --- .../test/integration/e2e-test-service/originsTest.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tools/integration/test/integration/e2e-test-service/originsTest.js b/tools/integration/test/integration/e2e-test-service/originsTest.js index 3d302d8..5fd6386 100644 --- a/tools/integration/test/integration/e2e-test-service/originsTest.js +++ b/tools/integration/test/integration/e2e-test-service/originsTest.js @@ -65,9 +65,8 @@ function isOriginWithRevisionsAllowed(coordinate) { function getProviderType(type, provider) { switch (type) { case 'git': - return 'github' case 'gem': - return 'rubygems' + return provider case 'conda': return `conda/${provider}` case 'maven': @@ -104,10 +103,10 @@ async function compareEndpoints(endpoint) { async function compareOriginsWithRevisions(coordinates) { const originUrl = buildOriginUrl(coordinates) - compareEndpoints(`${originUrl}/revisions`) + await compareEndpoints(`${originUrl}/revisions`) } async function compareOrigins(coordinates) { const originUrl = coordinates.startsWith('conda/') ? buildCondaUrl(coordinates) : buildOriginUrl(coordinates) - compareEndpoints(`${originUrl}`) + await compareEndpoints(`${originUrl}`) } From a99f7cde26d17ae2e1c19dc12f28ae3da6fb5a85 Mon Sep 17 00:00:00 2001 From: yashkohli88 Date: Wed, 8 Jan 2025 20:46:58 +0530 Subject: [PATCH 4/6] Update copyright header --- .../test/integration/e2e-test-service/originsTest.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/integration/test/integration/e2e-test-service/originsTest.js b/tools/integration/test/integration/e2e-test-service/originsTest.js index 5fd6386..a89e3ba 100644 --- a/tools/integration/test/integration/e2e-test-service/originsTest.js +++ b/tools/integration/test/integration/e2e-test-service/originsTest.js @@ -1,3 +1,6 @@ +// (c) Copyright 2024, SAP SE and ClearlyDefined contributors. Licensed under the MIT license. +// SPDX-License-Identifier: MIT + const assert = require('assert') const { callFetch } = require('../../../lib/fetch') const { devApiBaseUrl, prodApiBaseUrl, getComponents, definition } = require('../testConfig') From df1c5173e132f71ab7c3ded7d3aef8384bbbe87b Mon Sep 17 00:00:00 2001 From: yashkohli88 Date: Tue, 14 Jan 2025 00:49:21 +0530 Subject: [PATCH 5/6] Updated originsTest to handle timeout --- tools/integration/lib/fetch.js | 2 +- .../test/integration/e2e-test-service/originsTest.js | 8 ++++---- tools/integration/test/integration/testConfig.js | 3 +++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/tools/integration/lib/fetch.js b/tools/integration/lib/fetch.js index 292ce99..0ca7531 100644 --- a/tools/integration/lib/fetch.js +++ b/tools/integration/lib/fetch.js @@ -46,7 +46,7 @@ async function fetchWithRetry(fetcher, retryOpts) { const response = await withRetry(async () => { const resp = await fetcher() // retry on 5xx - if (resp.status >= 500) verifyResponse(resp) + if (resp?.status >= 500 ) verifyResponse(resp) return resp }, retryOpts) return verifyResponse(response) diff --git a/tools/integration/test/integration/e2e-test-service/originsTest.js b/tools/integration/test/integration/e2e-test-service/originsTest.js index a89e3ba..be61e8a 100644 --- a/tools/integration/test/integration/e2e-test-service/originsTest.js +++ b/tools/integration/test/integration/e2e-test-service/originsTest.js @@ -2,8 +2,8 @@ // SPDX-License-Identifier: MIT const assert = require('assert') -const { callFetch } = require('../../../lib/fetch') -const { devApiBaseUrl, prodApiBaseUrl, getComponents, definition } = require('../testConfig') +const { callFetchWithRetry: callFetch } = require('../../../lib/fetch') +const { devApiBaseUrl, prodApiBaseUrl, getComponents, origins } = require('../testConfig') const ORIGIN_EXCLUSION_LIST = ['go/golang', 'debsrc/debian', 'maven/mavengoogle'] const ORIGIN_REVISIONS_EXCLUSION_LIST = ['debsrc/debian'] @@ -16,9 +16,9 @@ const GRADLE_COMPONENT_ENDPOINT = 'gradleplugin/io.github.lognet.grpc-spring-boo const components = await getComponents() describe('Validate Origins API Between Dev and Prod', function () { - this.timeout(definition.timeout) + this.timeout(origins.timeout) - afterEach(() => new Promise(resolve => setTimeout(resolve, definition.timeout / 2))) + afterEach(() => new Promise(resolve => setTimeout(resolve, origins.timeout / 2))) components.filter(isOriginAllowed).forEach(component => { it(`Validates Origins API response for ${component}`, () => compareOrigins(component)) diff --git a/tools/integration/test/integration/testConfig.js b/tools/integration/test/integration/testConfig.js index d269184..73b9a93 100644 --- a/tools/integration/test/integration/testConfig.js +++ b/tools/integration/test/integration/testConfig.js @@ -87,5 +87,8 @@ module.exports = { }, definition: { timeout: 1000 * 60 // for each component + }, + origins:{ + timeout: 1000 * 60 * 2 } } From b5defbf6188bc24f30c0ecc68df7f18ee2b64ad9 Mon Sep 17 00:00:00 2001 From: yashkohli88 Date: Tue, 14 Jan 2025 00:50:25 +0530 Subject: [PATCH 6/6] Fix formatting --- tools/integration/lib/fetch.js | 2 +- tools/integration/test/integration/testConfig.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/integration/lib/fetch.js b/tools/integration/lib/fetch.js index 0ca7531..388c034 100644 --- a/tools/integration/lib/fetch.js +++ b/tools/integration/lib/fetch.js @@ -46,7 +46,7 @@ async function fetchWithRetry(fetcher, retryOpts) { const response = await withRetry(async () => { const resp = await fetcher() // retry on 5xx - if (resp?.status >= 500 ) verifyResponse(resp) + if (resp?.status >= 500) verifyResponse(resp) return resp }, retryOpts) return verifyResponse(response) diff --git a/tools/integration/test/integration/testConfig.js b/tools/integration/test/integration/testConfig.js index 73b9a93..11087a7 100644 --- a/tools/integration/test/integration/testConfig.js +++ b/tools/integration/test/integration/testConfig.js @@ -88,7 +88,7 @@ module.exports = { definition: { timeout: 1000 * 60 // for each component }, - origins:{ + origins: { timeout: 1000 * 60 * 2 } }