// Path: src/main/java/org/terasology/launcher/model/Build.java
// public enum Build {
//     STABLE,
//     NIGHTLY
// }
// 
// Path: src/main/java/org/terasology/launcher/model/GameIdentifier.java
// public class GameIdentifier {
// 
//     private static final Logger logger = LoggerFactory.getLogger(GameIdentifier.class);
// 
//     final String displayVersion;
//     final Build build;
//     final Profile profile;
// 
//     public GameIdentifier(String displayVersion, Build build, Profile profile) {
//         this.displayVersion = displayVersion;
//         this.build = build;
//         this.profile = profile;
//     }
// 
//     public String getDisplayVersion() {
//         return displayVersion;
//     }
// 
//     public Build getBuild() {
//         return build;
//     }
// 
//     public Profile getProfile() {
//         return profile;
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) {
//             return true;
//         }
//         if (o == null || getClass() != o.getClass()) {
//             return false;
//         }
//         GameIdentifier that = (GameIdentifier) o;
//         return displayVersion.equals(that.displayVersion)
//                 && build == that.build
//                 && profile == that.profile;
//     }
// 
//     @Override
//     public int hashCode() {
//         return Objects.hash(displayVersion, build, profile);
//     }
// 
//     @Override
//     public String toString() {
//         return MoreObjects.toStringHelper(this)
//                 .add("version", displayVersion)
//                 .add("build", build)
//                 .add("profile", profile)
//                 .toString();
//     }
// }
// 
// Path: src/main/java/org/terasology/launcher/model/GameRelease.java
// public class GameRelease {
//     final GameIdentifier id;
//     final ReleaseMetadata releaseMetadata;
//     final URL url;
// 
//     public GameRelease(GameIdentifier id, URL url, ReleaseMetadata releaseMetadata) {
//         this.id = id;
//         this.url = url;
//         this.releaseMetadata = releaseMetadata;
//     }
// 
//     public GameIdentifier getId() {
//         return id;
//     }
// 
//     public URL getUrl() {
//         return url;
//     }
// 
//     /**
//      * The changelog associated with the game release
//      */
//     public String getChangelog() {
//         return releaseMetadata.getChangelog();
//     }
// 
//     public Date getTimestamp() {
//         return releaseMetadata.getTimestamp();
//     }
// 
//     @Override
//     public String toString() {
//         return id.getDisplayVersion();
//     }
// }
// 
// Path: src/main/java/org/terasology/launcher/model/Profile.java
// public enum Profile {
//     OMEGA,
//     ENGINE
// }
// 
// Path: src/main/java/org/terasology/launcher/model/ReleaseMetadata.java
// public class ReleaseMetadata {
//     private final String changelog;
//     private final Date timestamp;
// 
//     public ReleaseMetadata(String changelog, Date timestamp) {
//         this.changelog = changelog;
//         this.timestamp = timestamp;
//     }
// 
//     /**
//      * The change log of this release as a single markdown string.
//      */
//     public String getChangelog() {
//         return changelog;
//     }
// 
//     /**
//      * The timestamp of the CI run that built this release.
//      */
//     public Date getTimestamp() {
//         return timestamp;
//     }
// }


import com.google.gson.Gson;
import com.vdurmont.semver4j.Semver;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.terasology.launcher.model.Build;
import org.terasology.launcher.model.GameIdentifier;
import org.terasology.launcher.model.GameRelease;
import org.terasology.launcher.model.Profile;
import org.terasology.launcher.model.ReleaseMetadata;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

    @Test
    @DisplayName("skip builds without version info")
    void skipBuildsWithoutVersionInfo() {
        Properties emptyVersionInfo = new Properties();

        final JenkinsClient stubClient = new StubJenkinsClient(url -> validResult, url -> emptyVersionInfo);

        final JenkinsRepositoryAdapter adapter = new JenkinsRepositoryAdapter(Profile.OMEGA, Build.STABLE, stubClient);

        assertTrue(adapter.fetchReleases().isEmpty());
    }

    @Test
    @DisplayName("process valid response correctly")
    void processValidResponseCorrectly() {
        String displayVersion = "alpha 42 (preview)";
        Semver engineVersion = new Semver("5.0.1-SNAPSHOT", Semver.SemverType.IVY);

        Properties versionInfo = new Properties();
        versionInfo.setProperty("buildNumber", validResult.builds[0].number);
        versionInfo.setProperty("displayVersion", displayVersion);
        versionInfo.setProperty("engineVersion", engineVersion.getValue());

        final JenkinsClient stubClient = new StubJenkinsClient(url -> validResult, url -> versionInfo);

        // The Jenkins adapter should append the build number to the display version to ensure that the version string
        // is unique for two different builds. This is necessary because the display version generated by the CI build
        // is the same of subsequent builds...
        final String expectedVersion = displayVersion + "+" + validResult.builds[0].number;
// Path: src/main/java/org/terasology/launcher/model/Build.java
// public enum Build {
//     STABLE,
//     NIGHTLY
// }
// 
// Path: src/main/java/org/terasology/launcher/model/GameIdentifier.java
// public class GameIdentifier {
// 
//     private static final Logger logger = LoggerFactory.getLogger(GameIdentifier.class);
// 
//     final String displayVersion;
//     final Build build;
//     final Profile profile;
// 
//     public GameIdentifier(String displayVersion, Build build, Profile profile) {
//         this.displayVersion = displayVersion;
//         this.build = build;
//         this.profile = profile;
//     }
// 
//     public String getDisplayVersion() {
//         return displayVersion;
//     }
// 
//     public Build getBuild() {
//         return build;
//     }
// 
//     public Profile getProfile() {
//         return profile;
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) {
//             return true;
//         }
//         if (o == null || getClass() != o.getClass()) {
//             return false;
//         }
//         GameIdentifier that = (GameIdentifier) o;
//         return displayVersion.equals(that.displayVersion)
//                 && build == that.build
//                 && profile == that.profile;
//     }
// 
//     @Override
//     public int hashCode() {
//         return Objects.hash(displayVersion, build, profile);
//     }
// 
//     @Override
//     public String toString() {
//         return MoreObjects.toStringHelper(this)
//                 .add("version", displayVersion)
//                 .add("build", build)
//                 .add("profile", profile)
//                 .toString();
//     }
// }
// 
// Path: src/main/java/org/terasology/launcher/model/GameRelease.java
// public class GameRelease {
//     final GameIdentifier id;
//     final ReleaseMetadata releaseMetadata;
//     final URL url;
// 
//     public GameRelease(GameIdentifier id, URL url, ReleaseMetadata releaseMetadata) {
//         this.id = id;
//         this.url = url;
//         this.releaseMetadata = releaseMetadata;
//     }
// 
//     public GameIdentifier getId() {
//         return id;
//     }
// 
//     public URL getUrl() {
//         return url;
//     }
// 
//     /**
//      * The changelog associated with the game release
//      */
//     public String getChangelog() {
//         return releaseMetadata.getChangelog();
//     }
// 
//     public Date getTimestamp() {
//         return releaseMetadata.getTimestamp();
//     }
// 
//     @Override
//     public String toString() {
//         return id.getDisplayVersion();
//     }
// }
// 
// Path: src/main/java/org/terasology/launcher/model/Profile.java
// public enum Profile {
//     OMEGA,
//     ENGINE
// }
// 
// Path: src/main/java/org/terasology/launcher/model/ReleaseMetadata.java
// public class ReleaseMetadata {
//     private final String changelog;
//     private final Date timestamp;
// 
//     public ReleaseMetadata(String changelog, Date timestamp) {
//         this.changelog = changelog;
//         this.timestamp = timestamp;
//     }
// 
//     /**
//      * The change log of this release as a single markdown string.
//      */
//     public String getChangelog() {
//         return changelog;
//     }
// 
//     /**
//      * The timestamp of the CI run that built this release.
//      */
//     public Date getTimestamp() {
//         return timestamp;
//     }
// }


// Path: src/test/java/org/terasology/launcher/repositories/JenkinsRepositoryAdapterTest.java
import com.google.gson.Gson;
import com.vdurmont.semver4j.Semver;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.terasology.launcher.model.Build;
import org.terasology.launcher.model.GameIdentifier;
import org.terasology.launcher.model.GameRelease;
import org.terasology.launcher.model.Profile;
import org.terasology.launcher.model.ReleaseMetadata;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;


    @Test
    @DisplayName("skip builds without version info")
    void skipBuildsWithoutVersionInfo() {
        Properties emptyVersionInfo = new Properties();

        final JenkinsClient stubClient = new StubJenkinsClient(url -> validResult, url -> emptyVersionInfo);

        final JenkinsRepositoryAdapter adapter = new JenkinsRepositoryAdapter(Profile.OMEGA, Build.STABLE, stubClient);

        assertTrue(adapter.fetchReleases().isEmpty());
    }

    @Test
    @DisplayName("process valid response correctly")
    void processValidResponseCorrectly() {
        String displayVersion = "alpha 42 (preview)";
        Semver engineVersion = new Semver("5.0.1-SNAPSHOT", Semver.SemverType.IVY);

        Properties versionInfo = new Properties();
        versionInfo.setProperty("buildNumber", validResult.builds[0].number);
        versionInfo.setProperty("displayVersion", displayVersion);
        versionInfo.setProperty("engineVersion", engineVersion.getValue());

        final JenkinsClient stubClient = new StubJenkinsClient(url -> validResult, url -> versionInfo);

        // The Jenkins adapter should append the build number to the display version to ensure that the version string
        // is unique for two different builds. This is necessary because the display version generated by the CI build
        // is the same of subsequent builds...
        final String expectedVersion = displayVersion + "+" + validResult.builds[0].number;
        final GameIdentifier id = new GameIdentifier(expectedVersion, Build.STABLE, Profile.OMEGA);