From 643c3eb0992a0cec1666a0e9cb878afafd0c0f83 Mon Sep 17 00:00:00 2001 From: Alexandre Beaurain Date: Thu, 23 May 2024 16:25:34 +0200 Subject: [PATCH] Add harvester for Debian #46 --- docker/requirements.txt | 2 +- .../bombase/core/debian/DebianException.java | 18 ++ .../bombase/core/debian/domain/DebianAPI.java | 253 ++++++++++++++++++ .../core/debian/domain/DebianClient.java | 100 +++++++ .../core/debian/domain/DebianHarvester.java | 23 ++ .../core/debian/domain/package-info.java | 7 + .../bombase/core/debian/package-info.java | 7 + .../bombase/core/meta/MetaInteractor.java | 2 + .../core/debian/domain/DebianClientTest.java | 195 ++++++++++++++ .../debian/domain/DebianHarvesterTest.java | 22 ++ 10 files changed, 628 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/philips/research/bombase/core/debian/DebianException.java create mode 100644 src/main/java/com/philips/research/bombase/core/debian/domain/DebianAPI.java create mode 100644 src/main/java/com/philips/research/bombase/core/debian/domain/DebianClient.java create mode 100644 src/main/java/com/philips/research/bombase/core/debian/domain/DebianHarvester.java create mode 100644 src/main/java/com/philips/research/bombase/core/debian/domain/package-info.java create mode 100644 src/main/java/com/philips/research/bombase/core/debian/package-info.java create mode 100644 src/test/java/com/philips/research/bombase/core/debian/domain/DebianClientTest.java create mode 100644 src/test/java/com/philips/research/bombase/core/debian/domain/DebianHarvesterTest.java diff --git a/docker/requirements.txt b/docker/requirements.txt index c1e4cf6..7e68deb 100644 --- a/docker/requirements.txt +++ b/docker/requirements.txt @@ -50,7 +50,7 @@ pygmars==0.7.0 Pygments==2.10.0 pymaven-patch==0.3.0 pyparsing==2.4.7 -PyYAML==5.4.1 +PyYAML==6.0.1 rdflib==6.0.1 requests==2.26.0 saneyaml==0.5.2 diff --git a/src/main/java/com/philips/research/bombase/core/debian/DebianException.java b/src/main/java/com/philips/research/bombase/core/debian/DebianException.java new file mode 100644 index 0000000..2bba599 --- /dev/null +++ b/src/main/java/com/philips/research/bombase/core/debian/DebianException.java @@ -0,0 +1,18 @@ +/* + * Copyleft (c) 2024, Alexandre Beaurain + * SPDX-License-Identifier: MIT + */ + +package com.philips.research.bombase.core.debian; + +import com.philips.research.bombase.core.BusinessException; + +public class DebianException extends BusinessException { + public DebianException(String message) { + super(message); + } + + public DebianException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/philips/research/bombase/core/debian/domain/DebianAPI.java b/src/main/java/com/philips/research/bombase/core/debian/domain/DebianAPI.java new file mode 100644 index 0000000..5d3b926 --- /dev/null +++ b/src/main/java/com/philips/research/bombase/core/debian/domain/DebianAPI.java @@ -0,0 +1,253 @@ +/* + * Copyleft (c) 2024, Alexandre Beaurain + * SPDX-License-Identifier: MIT + */ + +package com.philips.research.bombase.core.debian.domain; + +import com.philips.research.bombase.core.meta.PackageMetadata; +import com.philips.research.bombase.core.meta.registry.Field; +import com.philips.research.bombase.core.meta.registry.Trust; +import pl.tlinkowski.annotation.basic.NullOr; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Path; +import retrofit2.http.Query; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; + +public interface DebianAPI { + @GET("{distro}/series") + Call series( + @Path("distro") String distro + ); + + @GET("{distro}/+archive/primary?exact_match=true&ws.op=getPublishedBinaries") + Call getSourcePackages( + @Path("distro") String distro, + @Query("distro_arch_series") String distro_arch_series, + @Query("binary_name") String binary_name + ); + + @GET("{distro}/{series}/+source/{source}") + Call getSource(@Path("distro") String distro, @Path("series") String series, @Path("source") String source); + + @GET("{project}") + Call getProject(@Path("project") String project); + + @SuppressWarnings("NotNullFieldNotInitialized") + class Series { + URI self_link; + @NullOr URI web_link; + @NullOr URI resource_type_link; + @NullOr String bug_reporting_guidelines; + @NullOr String bug_reported_acknowledgement; + @NullOr List official_bug_tags; + @NullOr URI active_milestones_collection_link; + @NullOr URI all_milestones_collection_link; + @NullOr boolean active; + @NullOr String summary; + @NullOr URI drivers_collection_link; + String name; + @NullOr String displayname; + @NullOr String fullseriesname; + @NullOr String title; + @NullOr String description; + @NullOr String version; + @NullOr URI distribution_link; + @NullOr List component_names; + @NullOr List suite_names; + @NullOr String status; + @NullOr Date datereleased; + @NullOr URI parent_series_link; + @NullOr URI registrant_link; + @NullOr URI owner_link; + @NullOr Date date_created; + @NullOr URI driver_link; + @NullOr String changeslist; + @NullOr URI nominatedarchindep_link; + @NullOr boolean language_pack_full_export_requested; + @NullOr boolean backports_not_automatic; + @NullOr boolean proposed_not_automatic; + @NullOr boolean include_long_descriptions; + @NullOr List index_compressors; + @NullOr boolean publish_by_hash; + @NullOr boolean advertise_by_hash; + @NullOr boolean publish_i18n_index; + @NullOr URI main_archive_link; + @NullOr boolean supported; + @NullOr URI architectures_collection_link; + @NullOr String http_etag; + } + + @SuppressWarnings("NotNullFieldNotInitialized") + class SeriesCollection { + int start; + int total_size; + List entries; + } + + @SuppressWarnings("NotNullFieldNotInitialized") + class SourcePackageEntry { + URI self_link; + @NullOr URI resource_type_link; + @NullOr String display_name; + @NullOr String component_name; + @NullOr String section_name; + String source_package_name; + String source_package_version; + @NullOr URI distro_arch_series_link; + @NullOr String phased_update_percentage; + @NullOr Date date_published; + @NullOr Date scheduled_deletion_date; + @NullOr String status; + @NullOr String pocket; + @NullOr URI creator_link; + @NullOr Date date_created; + @NullOr Date date_superseded; + @NullOr Date date_made_pending; + @NullOr Date date_removed; + @NullOr URI archive_link; + @NullOr URI copied_from_archive_link; + @NullOr URI removed_by_link; + @NullOr String removal_comment; + String binary_package_name; + String binary_package_version; + @NullOr URI build_link; + @NullOr boolean architecture_specific; + @NullOr String priority_name; + @NullOr String http_etag; + } + + @SuppressWarnings("NotNullFieldNotInitialized") + class SourcePackageCollection { + int start; + int total_size; + List entries; + } + + @SuppressWarnings("NotNullFieldNotInitialized") + class Source { + URI self_link; + @NullOr URI web_link; + @NullOr URI resource_type_link; + @NullOr String bug_reporting_guidelines; + @NullOr String bug_reported_acknowledgement; + @NullOr List official_bug_tags; + String name; + @NullOr String displayname; + @NullOr URI distribution_link; + @NullOr URI distroseries_link; + URI productseries_link; + @NullOr String latest_published_component_name; + @NullOr String http_etag; + } + + @SuppressWarnings("NotNullFieldNotInitialized") + class ResponseJson implements PackageMetadata { + URI self_link; + @NullOr URI web_link; + @NullOr URI resource_type_link; + @NullOr boolean official_answers; + @NullOr boolean official_blueprints; + @NullOr boolean official_codehosting; + @NullOr boolean official_bugs; + @NullOr String information_type; + @NullOr boolean active; + @NullOr String bug_reporting_guidelines; + @NullOr String bug_reported_acknowledgement; + @NullOr List official_bug_tags; + @NullOr URI recipes_collection_link; + @NullOr URI webhooks_collection_link; + @NullOr URI bug_supervisor_link; + @NullOr URI active_milestones_collection_link; + @NullOr URI all_milestones_collection_link; + @NullOr boolean qualifies_for_free_hosting; + @NullOr String reviewer_whiteboard; + @NullOr String is_permitted; + @NullOr String project_reviewed; + @NullOr String license_approved; + @NullOr String display_name; + @NullOr URI icon_link; + @NullOr URI logo_link; + String name; + @NullOr URI owner_link; + @NullOr URI project_group_link; + @NullOr String title; + @NullOr URI registrant_link; + @NullOr URI driver_link; + @NullOr String summary; + @NullOr String description; + @NullOr Date date_created; + @NullOr URI homepage_url; + @NullOr URI wiki_url; + @NullOr URI screenshots_url; + @NullOr URI download_url; + @NullOr String programming_language; + @NullOr String sourceforge_project; + @NullOr String freshmeat_project; + @NullOr URI brand_link; + @NullOr boolean private_bugs; + List licenses; + @NullOr String license_info; + @NullOr URI bug_tracker_link; + @NullOr String date_next_suggest_packaging; + @NullOr URI series_collection_link; + @NullOr URI development_focus_link; + @NullOr URI releases_collection_link; + @NullOr URI translation_focus_link; + @NullOr URI commercial_subscription_link; + @NullOr boolean commercial_subscription_is_due; + @NullOr String remote_product; + @NullOr String security_contact; + @NullOr String vcs; + @NullOr String http_etag; + + @Override + public Trust trust(Field field) { + return Trust.LIKELY; + } + + @Override + public Optional getTitle() { + return Optional.ofNullable(name); + } + + @Override + public Optional getDescription() { + return Optional.ofNullable(description); + } + + @Override + public Optional> getAuthors() { + return Optional.of(new ArrayList()); + } + + @Override + public Optional getHomepage() { + return Optional.ofNullable(homepage_url); + } + + @Override + public Optional getDeclaredLicense() { + if (licenses == null || licenses.size() == 0) { + return null; + } + return Optional.ofNullable(licenses.get(0).toString()); + } + + @Override + public Optional getSourceLocation() { + return Optional.ofNullable(download_url != null ? download_url.toString() : null); + } + + @Override + public Optional getDownloadLocation() { + return Optional.ofNullable(download_url); + } + } +} diff --git a/src/main/java/com/philips/research/bombase/core/debian/domain/DebianClient.java b/src/main/java/com/philips/research/bombase/core/debian/domain/DebianClient.java new file mode 100644 index 0000000..0fcde52 --- /dev/null +++ b/src/main/java/com/philips/research/bombase/core/debian/domain/DebianClient.java @@ -0,0 +1,100 @@ +/* + * Copyleft (c) 2024, Alexandre Beaurain + * SPDX-License-Identifier: MIT + */ + +package com.philips.research.bombase.core.debian.domain; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.github.packageurl.PackageURL; +import com.philips.research.bombase.core.meta.PackageMetadata; +import com.philips.research.bombase.core.debian.DebianException; +import org.springframework.stereotype.Component; +import retrofit2.Call; +import retrofit2.Retrofit; +import retrofit2.converter.jackson.JacksonConverterFactory; + +import java.io.IOException; +import java.net.URI; +import java.util.Optional; +import java.util.Map; + +@Component +public class DebianClient { + private static final ObjectMapper MAPPER = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.NON_PRIVATE) + .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); + + private final DebianAPI rest; + + private static final String baseApiUrl = "https://api.launchpad.net/1.0/"; + + DebianClient() { + this(URI.create(baseApiUrl)); + } + + DebianClient(URI uri) { + final var retrofit = new Retrofit.Builder() + .baseUrl(uri.toASCIIString()) + .addConverterFactory(JacksonConverterFactory.create(MAPPER)) + .build(); + rest = retrofit.create(DebianAPI.class); + } + + Optional getPackageMetadata(PackageURL purl) { + final Map qualifiers = purl.getQualifiers(); + final String distro = "ubuntu"; + final String packageName = purl.getName(); + final var allSeries = query(rest.series(distro)).get().entries; + if (allSeries.size() == 0) { + return Optional.empty(); + } + String seriesName = "noble"; + for ( var serie : allSeries ) { + if (serie.active && serie.datereleased != null) { + seriesName = serie.name; + break; + } + } + String arch = qualifiers != null ? qualifiers.getOrDefault("arch", "amd64") : "amd64"; + if (arch.equals("all")) { + arch = "amd64"; + } + final String distroArchSeries = baseApiUrl + distro + "/" + seriesName + "/" + arch; + final var sourcePackagesCollection = query(rest.getSourcePackages(distro, '"' + distroArchSeries + '"', '"' + packageName + '"')); + final var sourcePackagesEntries = sourcePackagesCollection.get().entries; + if (sourcePackagesEntries.size() == 0) { + return Optional.empty(); + } + final String sourceName = sourcePackagesEntries.get(0).source_package_name; + final var source = query(rest.getSource(distro, seriesName, sourceName)); + if (source.isEmpty() || source.get() == null || source.get().productseries_link == null) { + return Optional.empty(); + } + final String projectName = source.get().productseries_link.toString().replace(baseApiUrl, "").replaceAll("/.*", ""); + return query(rest.getProject(projectName)); + } + + private Optional query(Call query) { + try { + final var response = query.execute(); + if (response.code() == 404) { + return Optional.empty(); + } + if (!response.isSuccessful()) { + throw new DebianException("Debian server responded with status " + response.code()); + } + return Optional.ofNullable(response.body()); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("JSON formatting error", e); + } catch (IOException e) { + throw new DebianException("Debian is not reachable"); + } + } +} diff --git a/src/main/java/com/philips/research/bombase/core/debian/domain/DebianHarvester.java b/src/main/java/com/philips/research/bombase/core/debian/domain/DebianHarvester.java new file mode 100644 index 0000000..d134f79 --- /dev/null +++ b/src/main/java/com/philips/research/bombase/core/debian/domain/DebianHarvester.java @@ -0,0 +1,23 @@ +/* + * Copyleft (c) 2024, Alexandre Beaurain + * SPDX-License-Identifier: MIT + */ + +package com.philips.research.bombase.core.debian.domain; + +import com.philips.research.bombase.core.meta.AbstractRepoHarvester; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class DebianHarvester extends AbstractRepoHarvester { + @Autowired + DebianHarvester(DebianClient client) { + super(client::getPackageMetadata); + } + + @Override + protected boolean isSupportedType(String type) { + return type.equals("deb"); + } +} diff --git a/src/main/java/com/philips/research/bombase/core/debian/domain/package-info.java b/src/main/java/com/philips/research/bombase/core/debian/domain/package-info.java new file mode 100644 index 0000000..ab3219c --- /dev/null +++ b/src/main/java/com/philips/research/bombase/core/debian/domain/package-info.java @@ -0,0 +1,7 @@ +/* + * Copyleft (c) 2024, Alexandre Beaurain + * SPDX-License-Identifier: MIT + */ + +@pl.tlinkowski.annotation.basic.NonNullPackage +package com.philips.research.bombase.core.debian.domain; diff --git a/src/main/java/com/philips/research/bombase/core/debian/package-info.java b/src/main/java/com/philips/research/bombase/core/debian/package-info.java new file mode 100644 index 0000000..53b1f8b --- /dev/null +++ b/src/main/java/com/philips/research/bombase/core/debian/package-info.java @@ -0,0 +1,7 @@ +/* + * Copyleft (c) 2024, Alexandre Beaurain + * SPDX-License-Identifier: MIT + */ + +@pl.tlinkowski.annotation.basic.NonNullPackage +package com.philips.research.bombase.core.debian; diff --git a/src/main/java/com/philips/research/bombase/core/meta/MetaInteractor.java b/src/main/java/com/philips/research/bombase/core/meta/MetaInteractor.java index ee1c751..8fb8716 100644 --- a/src/main/java/com/philips/research/bombase/core/meta/MetaInteractor.java +++ b/src/main/java/com/philips/research/bombase/core/meta/MetaInteractor.java @@ -16,6 +16,7 @@ import com.philips.research.bombase.core.meta.registry.MetaRegistry; import com.philips.research.bombase.core.meta.registry.Trust; import com.philips.research.bombase.core.npm.domain.NpmHarvester; +import com.philips.research.bombase.core.debian.domain.DebianHarvester; import com.philips.research.bombase.core.nuget.domain.NugetHarvester; import com.philips.research.bombase.core.pypi.domain.PyPiHarvester; import com.philips.research.bombase.core.source_scan.domain.SourceLicensesHarvester; @@ -58,6 +59,7 @@ void init() { installListener(NpmHarvester.class); installListener(NugetHarvester.class); installListener(MavenHarvester.class); + installListener(DebianHarvester.class); installListener(LicenseCleaner.class); if (properties.isScanLicenses()) { installListener(SourceLicensesHarvester.class); diff --git a/src/test/java/com/philips/research/bombase/core/debian/domain/DebianClientTest.java b/src/test/java/com/philips/research/bombase/core/debian/domain/DebianClientTest.java new file mode 100644 index 0000000..427833e --- /dev/null +++ b/src/test/java/com/philips/research/bombase/core/debian/domain/DebianClientTest.java @@ -0,0 +1,195 @@ +/* + * Copyleft (c) 2024, Alexandre Beaurain + * SPDX-License-Identifier: MIT + */ + +package com.philips.research.bombase.core.debian.domain; + +import com.github.packageurl.MalformedPackageURLException; +import com.github.packageurl.PackageURL; +import com.philips.research.bombase.core.meta.PackageMetadata; +import com.philips.research.bombase.core.debian.DebianException; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import pl.tlinkowski.annotation.basic.NullOr; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.URI; +import java.util.List; +import java.util.ArrayList; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class DebianClientTest { + private static final int PORT = 1084; + private static final PackageURL PURL = createPurl("pkg:deb/debian/package-name@version"); + private static final String HOMEPAGE_URL = "https://example.com/home-page"; + private static final String DOWNLOAD_URL = "https://example.com/download"; + private static final String API = "https://api.launchpad.net/1.0/"; + + private static final String DESCRIPTION = "Description"; + private static final String HTTP_ETAG = "\"ac6176d8d018519021e077f08a9c39fb2fa6257e-4405102f75bbae89c324c528d4e205ebf4bae82b\""; + private static final String LICENSE = "MIT"; + + private static final String PROJECT_NAME = "project-name"; + private static final String SERIE_NAME = "noble"; + private static final String PACKAGE_NAME = "package-name"; + private static final String SOURCE_NAME = "source-name"; + private static final String VERSION = "version"; + + private final DebianClient client = new DebianClient(URI.create("http://localhost:" + PORT)); + private final MockWebServer mockServer = new MockWebServer(); + + static PackageURL createPurl(String purl) { + try { + return new PackageURL(purl); + } catch (MalformedPackageURLException e) { + throw new RuntimeException(e); + } + } + + @BeforeEach + void setUp() throws IOException { + mockServer.start(PORT); + } + + @AfterEach + void tearDown() throws IOException { + mockServer.shutdown(); + } + + void enqueueSeriesMock() throws JSONException { + List entries = new ArrayList(); + entries.add(new JSONObject() + .put("self_link", API + "ubuntu/noble") + .put("datereleased", "2024-04-25T16:04:56.109664+00:00") + .put("active", true) + .put("name", SERIE_NAME)); + mockServer.enqueue(new MockResponse().setBody(new JSONObject() + .put("start", 0) + .put("total_size", 1) + .put("entries", new JSONArray(entries)) + .toString())); + } + + void enqueueSourcePackagesMock() throws JSONException { + List entries = new ArrayList(); + entries.add(new JSONObject() + .put("self_link", API + "ubuntu/+archive/primary/+binarypub/200881569") + .put("source_package_name", PACKAGE_NAME) + .put("source_package_version", VERSION) + .put("binary_package_name", SOURCE_NAME) + .put("binary_package_version", VERSION)); + mockServer.enqueue(new MockResponse().setBody(new JSONObject() + .put("start", 0) + .put("total_size", 1) + .put("entries", new JSONArray(entries)) + .toString())); + } + + void enqueueSourceMock() throws JSONException { + mockServer.enqueue(new MockResponse().setBody(new JSONObject() + .put("self_link", API + "ubuntu/" + SERIE_NAME + "/+source/" + SOURCE_NAME) + .put("name", SOURCE_NAME) + .put("productseries_link", API + PROJECT_NAME + "/head") + .toString())); + } + + void enqueueProjectMock() throws JSONException { + List licenses = new ArrayList(); + licenses.add(LICENSE); + mockServer.enqueue(new MockResponse().setBody(new JSONObject() + .put("self_link", "") + .put("name", PROJECT_NAME) + .put("description", DESCRIPTION) + .put("homepage_url", HOMEPAGE_URL) + .put("download_url", DOWNLOAD_URL) + .put("licenses", new JSONArray(licenses)) + .put("http_etag", HTTP_ETAG) + .toString())); + } + + @Nested + class ApiNotWorkingProperly { + + @Test + void noSeriesAnswer() throws JSONException { + mockServer.enqueue(new MockResponse().setBody(new JSONObject() + .put("start", 0) + .put("total_size", 0) + .put("entries", new JSONArray(new ArrayList())) + .toString())); + assertThat(client.getPackageMetadata(PURL)).isEmpty(); + } + + @Test + void noSourcePackagesAnswer() throws Exception { + enqueueSeriesMock(); + mockServer.enqueue(new MockResponse().setBody(new JSONObject() + .put("start", 0) + .put("total_size", 0) + .put("entries", new JSONArray(new ArrayList())) + .toString())); + assertThat(client.getPackageMetadata(PURL)).isEmpty(); + } + + @Test + void noSourceAnwser() throws Exception { + enqueueSeriesMock(); + enqueueSourcePackagesMock(); + mockServer.enqueue(new MockResponse().setResponseCode(404).setBody("Object: , name: '" + SOURCE_NAME + "'")); + assertThat(client.getPackageMetadata(PURL)).isEmpty(); + } + + @Test + void noProjectAnwser() throws Exception { + enqueueSeriesMock(); + enqueueSourcePackagesMock(); + enqueueSourceMock(); + mockServer.enqueue(new MockResponse().setResponseCode(404).setBody("Object: , name: '" + PROJECT_NAME + "'")); + assertThat(client.getPackageMetadata(PURL)).isEmpty(); + } + + @Test + void throws_serverNotReachable() { + var serverlessClient = new DebianClient(URI.create("http://localhost:1234")); + assertThatThrownBy(() -> serverlessClient.getPackageMetadata(PURL)) + .isInstanceOf(DebianException.class) + .hasMessageContaining("not reachable"); + } + } + + @Nested + class WithMetaData { + + @BeforeEach + void setUp() throws JSONException { + enqueueSeriesMock(); + enqueueSourcePackagesMock(); + enqueueSourceMock(); + enqueueProjectMock(); + } + + @Test + void getsInitialMetaDataFromServer() throws Exception { + final var definition = client.getPackageMetadata(PURL).orElseThrow(); + + assertThat(definition.getTitle()).contains(PROJECT_NAME); + assertThat(definition.getDescription()).contains(DESCRIPTION); + assertThat(definition.getHomepage()).contains(URI.create(HOMEPAGE_URL)); + assertThat(definition.getSourceLocation()).contains(DOWNLOAD_URL); + assertThat(definition.getDeclaredLicense()).contains(LICENSE); + assertThat(definition.getDownloadLocation()).contains(URI.create(DOWNLOAD_URL)); + } + } +} diff --git a/src/test/java/com/philips/research/bombase/core/debian/domain/DebianHarvesterTest.java b/src/test/java/com/philips/research/bombase/core/debian/domain/DebianHarvesterTest.java new file mode 100644 index 0000000..575d17f --- /dev/null +++ b/src/test/java/com/philips/research/bombase/core/debian/domain/DebianHarvesterTest.java @@ -0,0 +1,22 @@ +/* + * Copyleft (c) 2024, Alexandre Beaurain + * SPDX-License-Identifier: MIT + */ + +package com.philips.research.bombase.core.debian.domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class DebianHarvesterTest { + private final DebianClient client = mock(DebianClient.class); + private final DebianHarvester harvester = new DebianHarvester(client); + + @Test + void triggersForSupportedType() { + assertThat(harvester.isSupportedType("generic")).isFalse(); + assertThat(harvester.isSupportedType("deb")).isTrue(); + } +}