From f27c53f4c8e7c88bf011047b7cdbf36701d6e728 Mon Sep 17 00:00:00 2001
From: Kevin James <KevinJames@thekev.in>
Date: Fri, 23 Aug 2019 14:01:20 -0700
Subject: [PATCH] fix(versioner): fixup regex rollback logic (#4358)

As described on Slack, the regex versioner is currently causing bad PRs
such as [this](https://github.com/TheKevJames/experiments/pull/8) since
it can not properly track the current version. By parsing the `range`
values with the same logic as the other version strings, eveverything
should match up properly.
---
 lib/versioning/regex/index.ts |  17 +++-
 test/versioning/regex.spec.ts | 166 +++++++++++++++++++++++-----------
 2 files changed, 124 insertions(+), 59 deletions(-)

diff --git a/lib/versioning/regex/index.ts b/lib/versioning/regex/index.ts
index d85d0a163d..2f201f35bc 100644
--- a/lib/versioning/regex/index.ts
+++ b/lib/versioning/regex/index.ts
@@ -97,19 +97,28 @@ export class RegExpVersioningApi extends GenericVersioningApi<RegExpVersion> {
   }
 
   isLessThanRange(version: string, range: string): boolean {
-    return ltr(asSemver(this._parse(version)), range);
+    return ltr(asSemver(this._parse(version)), asSemver(this._parse(range)));
   }
 
   maxSatisfyingVersion(versions: string[], range: string): string | null {
-    return maxSatisfying(versions.map(v => asSemver(this._parse(v))), range);
+    return maxSatisfying(
+      versions.map(v => asSemver(this._parse(v))),
+      asSemver(this._parse(range))
+    );
   }
 
   minSatisfyingVersion(versions: string[], range: string): string | null {
-    return minSatisfying(versions.map(v => asSemver(this._parse(v))), range);
+    return minSatisfying(
+      versions.map(v => asSemver(this._parse(v))),
+      asSemver(this._parse(range))
+    );
   }
 
   matches(version: string, range: string) {
-    return satisfies(asSemver(this._parse(version)), range);
+    return satisfies(
+      asSemver(this._parse(version)),
+      asSemver(this._parse(range))
+    );
   }
 }
 
diff --git a/test/versioning/regex.spec.ts b/test/versioning/regex.spec.ts
index 2ab194e116..5cc01583ea 100644
--- a/test/versioning/regex.spec.ts
+++ b/test/versioning/regex.spec.ts
@@ -224,71 +224,103 @@ describe('regex', () => {
 
   describe('.isLessThanRange', () => {
     it('returns true when version less than range', () => {
-      expect(regex.isLessThanRange('1.2.2', '> 1.2.2')).toBe(true);
-      expect(regex.isLessThanRange('1.1.4', '>= 1.1.5 < 2.0')).toBe(true);
-      expect(regex.isLessThanRange('1.2.0a1', '1.2.0')).toBe(true);
-      expect(regex.isLessThanRange('1.2.2', '> 1.2.2 ~> 2.0.0')).toBe(true);
-      expect(regex.isLessThanRange('1.2.2-foo', '> 1.2.2')).toBe(true);
-      expect(regex.isLessThanRange('1.1.4-foo', '>= 1.1.5 < 2.0')).toBe(true);
-      expect(regex.isLessThanRange('1.2.0a1-foo', '1.2.0')).toBe(true);
-      expect(regex.isLessThanRange('1.2.2-foo', '> 1.2.2 ~> 2.0.0')).toBe(true);
-    });
-
-    it('returns false when version greater or satisfies range', () => {
-      expect(regex.isLessThanRange('1.2.2', '<= 1.2.2')).toBe(false);
-      expect(regex.isLessThanRange('2.0.0', '>= 1.1.5 < 2.0')).toBe(false);
-      expect(regex.isLessThanRange('1.2.0b2', '1.1.9')).toBe(false);
-      expect(regex.isLessThanRange('2.0.0', '> 1.2.2 ~> 2.0.0')).toBe(false);
-      expect(regex.isLessThanRange('1.2.2-foo', '<= 1.2.2')).toBe(false);
-      expect(regex.isLessThanRange('2.0.0-foo', '>= 1.1.5 < 2.0')).toBe(false);
-      expect(regex.isLessThanRange('1.2.0b2-foo', '1.1.9')).toBe(false);
-      expect(regex.isLessThanRange('2.0.0-foo', '> 1.2.2 ~> 2.0.0')).toBe(
-        false
-      );
+      expect(regex.isLessThanRange('1.2.2', '1.2.3')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2', '1.2.3-bar')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2', '1.2.3a1')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2', '1.2.3a1-bar')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2-foo', '1.2.3')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2-foo', '1.2.3-bar')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2-foo', '1.2.3a1')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2-foo', '1.2.3a1-bar')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2a1', '1.2.3')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2a1', '1.2.3-bar')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2a1', '1.2.3a1')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2a1', '1.2.3a1-bar')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2a1-foo', '1.2.3')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2a1-foo', '1.2.3-bar')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2a1-foo', '1.2.3a1')).toBe(true);
+      expect(regex.isLessThanRange('1.2.2a1-foo', '1.2.3a1-bar')).toBe(true);
+    });
+
+    it('returns false when version satisfies range', () => {
+      expect(regex.isLessThanRange('1.2.2', '1.2.2')).toBe(false);
+      expect(regex.isLessThanRange('1.2.2', '1.2.2-bar')).toBe(false);
+      expect(regex.isLessThanRange('1.2.2-foo', '1.2.2')).toBe(false);
+      expect(regex.isLessThanRange('1.2.2-foo', '1.2.2-bar')).toBe(false);
+      expect(regex.isLessThanRange('1.2.2a1', '1.2.2a1')).toBe(false);
+      expect(regex.isLessThanRange('1.2.2a1', '1.2.2a1-bar')).toBe(false);
+      expect(regex.isLessThanRange('1.2.2a1-foo', '1.2.2a1')).toBe(false);
+      expect(regex.isLessThanRange('1.2.2a1-foo', '1.2.2a1-bar')).toBe(false);
+    });
+
+    it('returns false when version greater than range', () => {
+      expect(regex.isLessThanRange('1.2.4', '1.2.3')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4', '1.2.3-bar')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4', '1.2.3a1')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4', '1.2.3a1-bar')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4-foo', '1.2.3')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4-foo', '1.2.3-bar')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4-foo', '1.2.3a1')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4-foo', '1.2.3a1-bar')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4a1', '1.2.3')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4a1', '1.2.3-bar')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4a1', '1.2.3a1')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4a1', '1.2.3a1-bar')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4a1-foo', '1.2.3')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4a1-foo', '1.2.3-bar')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4a1-foo', '1.2.3a1')).toBe(false);
+      expect(regex.isLessThanRange('1.2.4a1-foo', '1.2.3a1-bar')).toBe(false);
     });
   });
 
   describe('.maxSatisfyingVersion', () => {
     it('returns greatest version that matches range', () => {
-      expect(regex.maxSatisfyingVersion(['2.1.5', '2.1.6'], '~> 2.1')).toEqual(
-        '2.1.6'
-      );
       expect(
-        regex.maxSatisfyingVersion(['2.1.6', '2.1.5'], '~> 2.1.6')
+        regex.maxSatisfyingVersion(
+          ['2.1.5', '2.1.6a1', '2.1.6', '2.1.6-foo'],
+          '2.1.6'
+        )
       ).toEqual('2.1.6');
-      // Note: `maxSatisfyingVersion()` does not care about `.isCompatible()`
       expect(
-        regex.maxSatisfyingVersion(['2.1.6-foo', '2.1.5'], '~> 2.1.6')
+        regex.maxSatisfyingVersion(
+          ['2.1.5', '2.1.6a1', '2.1.6', '2.1.6-foo'],
+          '2.1.6-foo'
+        )
       ).toEqual('2.1.6');
       expect(
-        regex.maxSatisfyingVersion(['2.1.6', '2.1.6a1', '2.1.5'], '~> 2.1')
+        regex.maxSatisfyingVersion(['2.1.5-foo', '2.1.6'], '2.1.6-foo')
       ).toEqual('2.1.6');
     });
 
-    it('returns null if version that matches range absent', () => {
+    it('returns null if version that matches is absent', () => {
       expect(
-        regex.maxSatisfyingVersion(['1.2.3', '1.2.4'], '>= 3.5.0')
+        regex.maxSatisfyingVersion(['1.2.3', '1.2.4'], '3.5.0')
       ).toBeNull();
     });
   });
 
   describe('.minSatisfyingVersion', () => {
-    it('returns greatest version that matches range', () => {
-      expect(regex.minSatisfyingVersion(['2.1.5', '2.1.6'], '~> 2.1')).toEqual(
-        '2.1.5'
-      );
+    it('returns least version that matches range', () => {
       expect(
-        regex.minSatisfyingVersion(['2.1.6', '2.1.5'], '~> 2.1.6')
+        regex.minSatisfyingVersion(
+          ['2.1.5', '2.1.6a1', '2.1.6', '2.1.6-foo'],
+          '2.1.6'
+        )
+      ).toEqual('2.1.6');
+      expect(
+        regex.minSatisfyingVersion(
+          ['2.1.5', '2.1.6a1', '2.1.6', '2.1.6-foo'],
+          '2.1.6-foo'
+        )
       ).toEqual('2.1.6');
-      // Note: `minSatisfyingVersion()` does not care about `.isCompatible()`
       expect(
-        regex.minSatisfyingVersion(['2.1.6', '2.1.5-foo'], '~> 2.1')
+        regex.minSatisfyingVersion(['2.1.5', '2.1.6-foo'], '2.1.5-foo')
       ).toEqual('2.1.5');
     });
 
-    it('returns null if version that matches range absent', () => {
+    it('returns null if version that matches is absent', () => {
       expect(
-        regex.minSatisfyingVersion(['1.2.3', '1.2.4'], '>= 3.5.0')
+        regex.minSatisfyingVersion(['1.2.3', '1.2.4'], '3.5.0')
       ).toBeNull();
     });
   });
@@ -311,25 +343,49 @@ describe('regex', () => {
 
   describe('.matches', () => {
     it('returns true when version match range', () => {
-      expect(regex.matches('1.2.1', '>= 1.2')).toBe(true);
-      expect(regex.matches('1.2.3', '~> 1.2.1')).toBe(true);
-      expect(regex.matches('1.2.7', '1.2.7')).toBe(true);
-      expect(regex.matches('1.1.6', '> 1.1.5 < 2.0')).toBe(true);
-      expect(regex.matches('1.2.1-foo', '>= 1.2')).toBe(true);
-      expect(regex.matches('1.2.3-foo', '~> 1.2.1')).toBe(true);
-      expect(regex.matches('1.2.7-foo', '1.2.7')).toBe(true);
-      expect(regex.matches('1.1.6-foo', '> 1.1.5 < 2.0')).toBe(true);
+      expect(regex.matches('1.2.2', '1.2.2')).toBe(true);
+      expect(regex.matches('1.2.2', '1.2.2-bar')).toBe(true);
+      expect(regex.matches('1.2.2-foo', '1.2.2')).toBe(true);
+      expect(regex.matches('1.2.2-foo', '1.2.2-bar')).toBe(true);
+      expect(regex.matches('1.2.2a1', '1.2.2a1')).toBe(true);
+      expect(regex.matches('1.2.2a1', '1.2.2a1-bar')).toBe(true);
+      expect(regex.matches('1.2.2a1-foo', '1.2.2a1')).toBe(true);
+      expect(regex.matches('1.2.2a1-foo', '1.2.2a1-bar')).toBe(true);
     });
 
     it('returns false when version not match range', () => {
-      expect(regex.matches('1.2.1', '>= 1.3')).toBe(false);
-      expect(regex.matches('1.3.8', '~> 1.2.1')).toBe(false);
-      expect(regex.matches('1.3.9', '1.3.8')).toBe(false);
-      expect(regex.matches('2.0.0', '> 1.1.5 < 2.0')).toBe(false);
-      expect(regex.matches('1.2.1-foo', '>= 1.3')).toBe(false);
-      expect(regex.matches('1.3.8-foo', '~> 1.2.1')).toBe(false);
-      expect(regex.matches('1.3.9-foo', '1.3.8')).toBe(false);
-      expect(regex.matches('2.0.0-foo', '> 1.1.5 < 2.0')).toBe(false);
+      expect(regex.matches('1.2.2', '1.2.3')).toBe(false);
+      expect(regex.matches('1.2.2', '1.2.3-bar')).toBe(false);
+      expect(regex.matches('1.2.2', '1.2.3a1')).toBe(false);
+      expect(regex.matches('1.2.2', '1.2.3a1-bar')).toBe(false);
+      expect(regex.matches('1.2.2-foo', '1.2.3')).toBe(false);
+      expect(regex.matches('1.2.2-foo', '1.2.3-bar')).toBe(false);
+      expect(regex.matches('1.2.2-foo', '1.2.3a1')).toBe(false);
+      expect(regex.matches('1.2.2-foo', '1.2.3a1-bar')).toBe(false);
+      expect(regex.matches('1.2.2a1', '1.2.3')).toBe(false);
+      expect(regex.matches('1.2.2a1', '1.2.3-bar')).toBe(false);
+      expect(regex.matches('1.2.2a1', '1.2.3a1')).toBe(false);
+      expect(regex.matches('1.2.2a1', '1.2.3a1-bar')).toBe(false);
+      expect(regex.matches('1.2.2a1-foo', '1.2.3')).toBe(false);
+      expect(regex.matches('1.2.2a1-foo', '1.2.3-bar')).toBe(false);
+      expect(regex.matches('1.2.2a1-foo', '1.2.3a1')).toBe(false);
+      expect(regex.matches('1.2.2a1-foo', '1.2.3a1-bar')).toBe(false);
+      expect(regex.matches('1.2.4', '1.2.3')).toBe(false);
+      expect(regex.matches('1.2.4', '1.2.3-bar')).toBe(false);
+      expect(regex.matches('1.2.4', '1.2.3a1')).toBe(false);
+      expect(regex.matches('1.2.4', '1.2.3a1-bar')).toBe(false);
+      expect(regex.matches('1.2.4-foo', '1.2.3')).toBe(false);
+      expect(regex.matches('1.2.4-foo', '1.2.3-bar')).toBe(false);
+      expect(regex.matches('1.2.4-foo', '1.2.3a1')).toBe(false);
+      expect(regex.matches('1.2.4-foo', '1.2.3a1-bar')).toBe(false);
+      expect(regex.matches('1.2.4a1', '1.2.3')).toBe(false);
+      expect(regex.matches('1.2.4a1', '1.2.3-bar')).toBe(false);
+      expect(regex.matches('1.2.4a1', '1.2.3a1')).toBe(false);
+      expect(regex.matches('1.2.4a1', '1.2.3a1-bar')).toBe(false);
+      expect(regex.matches('1.2.4a1-foo', '1.2.3')).toBe(false);
+      expect(regex.matches('1.2.4a1-foo', '1.2.3-bar')).toBe(false);
+      expect(regex.matches('1.2.4a1-foo', '1.2.3a1')).toBe(false);
+      expect(regex.matches('1.2.4a1-foo', '1.2.3a1-bar')).toBe(false);
     });
   });
 });
-- 
GitLab