diff --git a/backend/src/services/release.ts b/backend/src/services/release.ts index ed24957e3..b4322a660 100644 --- a/backend/src/services/release.ts +++ b/backend/src/services/release.ts @@ -243,7 +243,7 @@ export async function getModelReleases( modelId: string, querySemver?: string, ): Promise> { - const query = querySemver === undefined ? { modelId } : getQuerySyntax(querySemver, modelId) + const query = querySemver === undefined ? { modelId } : convertSemverQueryToMongoQuery(querySemver, modelId) const results = await Release.aggregate() .match(query) .sort({ updatedAt: -1 }) @@ -335,39 +335,31 @@ export async function getReleaseBySemver(user: UserInterface, modelId: string, s return release } -function getSemverQueryBounds(querySemver: string) { +function parseSemverQuery(querySemver: string) { const semverRangeStandardised = semver.validRange(querySemver, { includePrerelease: false }) if (!semverRangeStandardised) { - throw BadReq(`Semver range, '${querySemver}' is invalid.`) + throw BadReq('Semver range is invalid.', { semverQuery: querySemver }) } const [expressionA, expressionB] = semverRangeStandardised.split(' ') - let lowerInclusivity, lowerSemver, upperInclusivity, upperSemver + let lowerInclusivity, upperInclusivity + let lowerSemverObj, upperSemverObj - //If lower semver + //LOWER SEMVER if (expressionA.includes('>')) { lowerInclusivity = expressionA.includes('>=') - lowerSemver = expressionA.replace(/[<>=]/g, '') + lowerSemverObj = semverStringToObject(expressionA.replace(/[<>=]/g, '')) } else { //upper semver upperInclusivity = expressionA.includes('<=') - upperSemver = expressionA.replace(/[<=]/g, '') + upperSemverObj = semverStringToObject(expressionA.replace(/[<=]/g, '')) } if (expressionB) { upperInclusivity = expressionB.includes('<=') - upperSemver = expressionB.replace(/[<=]/g, '') - } - - let lowerSemverObj, upperSemverObj - if (lowerSemver) { - lowerSemverObj = semverStringToObject(lowerSemver) - } - - if (upperSemver) { - upperSemverObj = semverStringToObject(upperSemver) + upperSemverObj = semverStringToObject(expressionB.replace(/[<=]/g, '')) } return { lowerSemverObj, upperSemverObj, lowerInclusivity, upperInclusivity } @@ -418,8 +410,8 @@ interface QueryBoundInterface { )[] } -export function getQuerySyntax(querySemver: string, modelID: string) { - const { lowerSemverObj, upperSemverObj, lowerInclusivity, upperInclusivity } = getSemverQueryBounds(querySemver) +function convertSemverQueryToMongoQuery(querySemver: string, modelID: string) { + const { lowerSemverObj, upperSemverObj, lowerInclusivity, upperInclusivity } = parseSemverQuery(querySemver) const queryQueue: QueryBoundInterface[] = [] diff --git a/backend/test/services/__snapshots__/release.spec.ts.snap b/backend/test/services/__snapshots__/release.spec.ts.snap index f8b7f9944..c9bbafc77 100644 --- a/backend/test/services/__snapshots__/release.spec.ts.snap +++ b/backend/test/services/__snapshots__/release.spec.ts.snap @@ -1,5 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`services > release > convertSemverQueryToMongoQuery > good 1`] = `undefined`; + exports[`services > release > getModelReleases > good 1`] = `undefined`; exports[`services > release > getModelReleases > good 2`] = `undefined`; diff --git a/backend/test/services/release.spec.ts b/backend/test/services/release.spec.ts index 6d6d241e6..198265ed8 100644 --- a/backend/test/services/release.spec.ts +++ b/backend/test/services/release.spec.ts @@ -9,7 +9,6 @@ import { getAllFileIds, getFileByReleaseFileName, getModelReleases, - getQuerySyntax, getReleaseBySemver, getReleasesForExport, newReleaseComment, @@ -365,78 +364,29 @@ describe('services > release', () => { //test good - give good semver range with lower and upper bounds, returns valid query //test fail - invalid range - test('getQuerySyntax > good'), - async () => { - const testQuery = getQuerySyntax('1.0.x', 'testModelID') - expect(testQuery).toBe({ - modelId: 'testModelID', - $and: [ - { - $or: [ - { - 'semver.major': { - $gte: 1, - }, - 'semver.minor': { - $gte: 0, - }, - 'semver.patch': { - $gte: 0, - }, - }, - { - 'semver.major': { - $gt: 1, - }, - }, - { - 'semver.major': { - $gte: 1, - }, - 'semver.minor': { - $gt: 0, - }, - }, - ], - }, - { - $or: [ - { - 'semver.major': { - $lte: 1, - }, - 'semver.minor': { - $lte: 1, - }, - 'semver.patch': { - $lt: 0, - }, - }, - { - 'semver.major': { - $lt: 1, - }, - }, - { - 'semver.major': { - $lte: 1, - }, - 'semver.minor': { - $lt: 1, - }, - }, - ], - }, - ], - }) - } - - test('getQuerySyntax > bad'), - async () => { - const querySemver = '>2.^2.x' - const modelId = 'testModelID' - expect(() => getQuerySyntax(querySemver, modelId)).rejects.toThrowError(/^Semver range, '>2.^2.x' is invalid./) - } + test('convertSemverQueryToMongoQuery > good', async () => { + releaseModelMocks.aggregate.mockImplementation(() => ({ + match: vi.fn().mockImplementation(() => ({ + sort: vi.fn().mockImplementation(() => ({ + lookup: vi.fn().mockImplementation(() => ({ + append: vi.fn().mockImplementation(() => ({ + lookup: vi.fn().mockImplementation(() => [{ _id: 'release', modelId: 'test', semver: '1.0.0' }]), + })), + })), + })), + })), + })) + + await getModelReleases({ dn: 'user' } as UserInterface, 'modelId', '2.2.X') + + expect(releaseModelMocks.match.mock.calls.at(0)).toMatchSnapshot() + }) + + test('convertSemverQueryToMongoQuery > bad', async () => { + expect(async () => await getModelReleases({ dn: 'user' } as UserInterface, 'test', '^2.2v.x')).rejects.toThrowError( + /^Semver range is invalid./, + ) + }) test('getReleaseBySemver > good', async () => { const mockRelease = { _id: 'release' }