From b04d4722064f4668ac9f4dae9f32d7fbffc74d05 Mon Sep 17 00:00:00 2001
From: Sergei Zharinov <zharinov@users.noreply.github.com>
Date: Wed, 14 Jul 2021 11:48:45 +0400
Subject: [PATCH] feat(pep440): Implement isLessThanRange function (#9915)

Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
Co-authored-by: Rhys Arkins <rhys@arkins.net>
---
 lib/versioning/pep440/index.spec.ts | 35 +++++++++++++++++++++++++++
 lib/versioning/pep440/index.ts      |  3 ++-
 lib/versioning/pep440/range.ts      | 37 ++++++++++++++++++++++++++++-
 3 files changed, 73 insertions(+), 2 deletions(-)

diff --git a/lib/versioning/pep440/index.spec.ts b/lib/versioning/pep440/index.spec.ts
index 50407d8e16..7e40db69c7 100644
--- a/lib/versioning/pep440/index.spec.ts
+++ b/lib/versioning/pep440/index.spec.ts
@@ -178,3 +178,38 @@ describe('pep440.getNewValue()', () => {
     expect(invalid).toEqual('==1.2.3');
   });
 });
+
+describe('pep.isLessThanRange()', () => {
+  test.each([
+    ['>= 1.0.0, < 2.0.0', '0.9.9.9', true],
+    ['>= 1.0.0, < 2.0.0', '1.0.0a0', true],
+
+    ['> 1.0.0, < 2.0.0', '1.0.0.0', true],
+    ['> 1.0.0, < 2.0.0', '2.0.1.0', false],
+    ['> 1.0.0, < 2.0.0', '2.0.0.0', false],
+    ['> 1.0.0, < 2.0.0', '2.0.0a0', false],
+
+    ['== 1.2.3', '1.2.2.9', true],
+    ['== 1.2.3', '1.2.3a0', true],
+    ['== 1.2.3', '1.2.3.0', false],
+    ['== 1.2.3', '1.2.3.1', false],
+    ['== 1.2.3', '1.2.4a0', false],
+
+    ['!= 1.2.3', '1.2.2.9', false],
+    ['!= 1.2.3', '1.2.3.0', false],
+    ['!= 1.2.3', '1.2.3.1', false],
+
+    ['< 1.0.0', '0.0.1', false],
+    ['< 1.0.0', '1.0.0', false],
+    ['< 1.0.0', '2.0.0', false],
+
+    ['<= 1.0.0', '0.0.1', false],
+    ['<= 1.0.0', '1.0.0', false],
+    ['<= 1.0.0', '2.0.0', false],
+
+    ['< 1.0.0, > 2.0.0', '0.0.1', true], // fixme (maybe)
+    ['< 1.0.0, > 2.0.0', '3.0.0', false],
+  ])(`%s\t%s\t%s`, (range, version, expected) => {
+    expect(pep440.isLessThanRange(version, range)).toBe(expected);
+  });
+});
diff --git a/lib/versioning/pep440/index.ts b/lib/versioning/pep440/index.ts
index 27962b2875..464abc75d9 100644
--- a/lib/versioning/pep440/index.ts
+++ b/lib/versioning/pep440/index.ts
@@ -1,7 +1,7 @@
 import * as pep440 from '@renovate/pep440';
 import { filter } from '@renovate/pep440/lib/specifier';
 import type { VersioningApi } from '../types';
-import { getNewValue } from './range';
+import { getNewValue, isLessThanRange } from './range';
 
 export const id = 'pep440';
 export const displayName = 'PEP440';
@@ -69,6 +69,7 @@ export const api: VersioningApi = {
   minSatisfyingVersion,
   getNewValue,
   sortVersions,
+  isLessThanRange,
 };
 
 export default api;
diff --git a/lib/versioning/pep440/range.ts b/lib/versioning/pep440/range.ts
index f08bf0f6f0..c2c9d85627 100644
--- a/lib/versioning/pep440/range.ts
+++ b/lib/versioning/pep440/range.ts
@@ -1,4 +1,4 @@
-import { gte, lte, satisfies } from '@renovate/pep440';
+import { gte, lt, lte, satisfies } from '@renovate/pep440';
 import { parse as parseRange } from '@renovate/pep440/lib/specifier';
 import { parse as parseVersion } from '@renovate/pep440/lib/version';
 import { logger } from '../../logger';
@@ -154,3 +154,38 @@ export function getNewValue({
 
   return result;
 }
+
+export function isLessThanRange(input: string, range: string): boolean {
+  try {
+    let invertResult = true;
+
+    const results = range
+      .split(',')
+      .map((x) =>
+        x
+          .replace(/\s*/g, '')
+          .split(/(~=|==|!=|<=|>=|<|>|===)/)
+          .slice(1)
+      )
+      .map(([op, version]) => {
+        if (['!=', '<=', '<'].includes(op)) {
+          return true;
+        }
+        invertResult = false;
+        if (['~=', '==', '>=', '==='].includes(op)) {
+          return lt(input, version);
+        }
+        if (op === '>') {
+          return lte(input, version);
+        }
+        // istanbul ignore next
+        return false;
+      });
+
+    const result = results.every((res) => res === true);
+
+    return invertResult ? !result : result;
+  } catch (err) /* istanbul ignore next */ {
+    return false;
+  }
+}
-- 
GitLab