diff --git a/modules/core/shared/src/main/scala/scaladex/core/model/SemanticVersion.scala b/modules/core/shared/src/main/scala/scaladex/core/model/SemanticVersion.scala index 4fb0a1602..9fbbeeb6a 100644 --- a/modules/core/shared/src/main/scala/scaladex/core/model/SemanticVersion.scala +++ b/modules/core/shared/src/main/scala/scaladex/core/model/SemanticVersion.scala @@ -103,6 +103,13 @@ object SemanticVersion { ) } + /** + * Often we will prefer the latest release, but if there is no full release, we will select the most recent + * pre-release. + */ + val PreferReleases: Ordering[SemanticVersion] = + Ordering.by[SemanticVersion, Boolean](_.isRelease).orElse(ordering) + private def MajorP[A: P]: P[Int] = Number // http://semver.org/#spec-item-10 diff --git a/modules/core/shared/src/test/scala/scaladex/core/model/SemanticVersionTests.scala b/modules/core/shared/src/test/scala/scaladex/core/model/SemanticVersionTests.scala index 9abba45d1..1f37c1151 100644 --- a/modules/core/shared/src/test/scala/scaladex/core/model/SemanticVersionTests.scala +++ b/modules/core/shared/src/test/scala/scaladex/core/model/SemanticVersionTests.scala @@ -3,6 +3,8 @@ package scaladex.core.model import org.scalatest.funspec.AsyncFunSpec import org.scalatest.matchers.should.Matchers import org.scalatest.prop.TableDrivenPropertyChecks +import scaladex.core.model.SemanticVersion.PreferReleases +import scaladex.core.test.Values._ class SemanticVersionTests extends AsyncFunSpec with Matchers with TableDrivenPropertyChecks { it("should parse any version") { @@ -39,6 +41,12 @@ class SemanticVersionTests extends AsyncFunSpec with Matchers with TableDrivenPr forAll(inputs)((lower, higher) => lower shouldBe <(higher)) } + it("should allow us to prefer releases over pre-releases") { + val versions = Seq(`7.0.0`, `7.1.0`, `7.2.0-PREVIEW.1`) + versions.max shouldBe `7.2.0-PREVIEW.1` + versions.max(PreferReleases) shouldBe `7.1.0` + } + it("should encode and decode any version") { val inputs = Table[SemanticVersion]( "semanticVersion", diff --git a/modules/core/shared/src/test/scala/scaladex/core/test/Values.scala b/modules/core/shared/src/test/scala/scaladex/core/test/Values.scala index f74e3d489..08163b2cd 100644 --- a/modules/core/shared/src/test/scala/scaladex/core/test/Values.scala +++ b/modules/core/shared/src/test/scala/scaladex/core/test/Values.scala @@ -36,6 +36,8 @@ object Values { val `2.7.0` = PatchVersion(2, 7, 0) val `7.0.0` = PatchVersion(7, 0, 0) val `7.1.0` = PatchVersion(7, 1, 0) + val `7.2.0-PREVIEW.1` = SemanticVersion.parse("7.2.0-PREVIEW.1").get + val `7.2.0-PREVIEW.2` = SemanticVersion.parse("7.2.0-PREVIEW.2").get val `7.2.0` = PatchVersion(7, 2, 0) val `7.3.0` = PatchVersion(7, 3, 0) diff --git a/modules/server/src/main/scala/scaladex/server/route/Badges.scala b/modules/server/src/main/scala/scaladex/server/route/Badges.scala index a144cb046..b1126f132 100644 --- a/modules/server/src/main/scala/scaladex/server/route/Badges.scala +++ b/modules/server/src/main/scala/scaladex/server/route/Badges.scala @@ -21,6 +21,7 @@ import scaladex.core.model.Scala import scaladex.core.model.ScalaJs import scaladex.core.model.ScalaNative import scaladex.core.model.SemanticVersion +import scaladex.core.model.SemanticVersion.PreferReleases import scaladex.core.service.WebDatabase class Badges(database: WebDatabase)(implicit executionContext: ExecutionContext) { @@ -173,7 +174,7 @@ object Badges { private[route] def summaryOfLatestVersions(versionsByScalaVersions: Map[Scala, Seq[SemanticVersion]]): String = versionsByScalaVersions.view - .mapValues(_.max) + .mapValues(_.max(PreferReleases)) .groupMap { case (_, latestVersion) => latestVersion } { case (scalaVersion, _) => scalaVersion } .toSeq .sortBy(_._1)(SemanticVersion.ordering.reverse) diff --git a/modules/server/src/test/scala/scaladex/server/route/BadgesTests.scala b/modules/server/src/test/scala/scaladex/server/route/BadgesTests.scala index e59e20d72..5d79e2136 100644 --- a/modules/server/src/test/scala/scaladex/server/route/BadgesTests.scala +++ b/modules/server/src/test/scala/scaladex/server/route/BadgesTests.scala @@ -9,9 +9,12 @@ import akka.http.scaladsl.model.Uri import akka.http.scaladsl.model.headers.Location import akka.http.scaladsl.server.Route import org.scalatest.BeforeAndAfterAll +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers import scaladex.core.model.Scala._ import scaladex.core.test.Values._ import scaladex.core.util.ScalaExtensions._ +import scaladex.server.route.Badges.summaryOfLatestVersions class BadgesTests extends ControllerBaseSuite with BeforeAndAfterAll { @@ -22,17 +25,6 @@ class BadgesTests extends ControllerBaseSuite with BeforeAndAfterAll { def insertCats(): Future[Unit] = Cats.allArtifacts.map(database.insertArtifact(_, Seq.empty, now)).sequence.map(_ => ()) - it("should provide a concise summary of latest versions") { - Badges.summaryOfLatestVersions( - Map( - `2.11` -> Seq(`7.0.0`, `7.1.0`), - `2.12` -> Seq(`7.0.0`, `7.1.0`, `7.2.0`), - `2.13` -> Seq(`7.0.0`, `7.1.0`, `7.2.0`, `7.3.0`), - `3` -> Seq(`7.2.0`, `7.3.0`) - ) - ) shouldBe "7.3.0 (Scala 3.x, 2.13), 7.2.0 (Scala 2.12), 7.1.0 (Scala 2.11)" - } - it("should fallback to JVM artifacts") { Get(s"/${Cats.reference}/cats-core/latest-by-scala-version.svg") ~> badgesRoute ~> check { status shouldEqual StatusCodes.TemporaryRedirect @@ -73,3 +65,27 @@ class BadgesTests extends ControllerBaseSuite with BeforeAndAfterAll { } } } + +class BadgesUnitTests extends AnyFunSpec with Matchers { + it("should provide a concise summary of latest versions") { + summaryOfLatestVersions( + Map( + `2.11` -> Seq(`7.0.0`, `7.1.0`), + `2.12` -> Seq(`7.0.0`, `7.1.0`, `7.2.0`), + `2.13` -> Seq(`7.0.0`, `7.1.0`, `7.2.0`, `7.3.0`), + `3` -> Seq(`7.2.0`, `7.3.0`) + ) + ) shouldBe "7.3.0 (Scala 3.x, 2.13), 7.2.0 (Scala 2.12), 7.1.0 (Scala 2.11)" + } + + it("should prefer releases to pre-releases if both are available") { + summaryOfLatestVersions(Map(`2.13` -> Seq(`7.0.0`, `7.1.0`, `7.2.0-PREVIEW.1`))) shouldBe "7.1.0 (Scala 2.13)" + } + + it("should display latest pre-release if no full release is available") { + summaryOfLatestVersions( + Map(`2.13` -> Seq(`7.2.0-PREVIEW.1`, `7.2.0-PREVIEW.2`)) + ) shouldBe s"${`7.2.0-PREVIEW.2`} (Scala 2.13)" + } + +}