JAL-4199 Test basic service parameter construction
authorMateusz Warowny <mmzwarowny@dundee.ac.uk>
Mon, 19 Jun 2023 11:08:55 +0000 (13:08 +0200)
committerMateusz Warowny <mmzwarowny@dundee.ac.uk>
Mon, 19 Jun 2023 11:08:55 +0000 (13:08 +0200)
j11lib/slivka-client.jar
test/jalview/ws2/client/slivka/SlivkaWSDiscovererTest.java

index 3c5290d..11b2f93 100644 (file)
Binary files a/j11lib/slivka-client.jar and b/j11lib/slivka-client.jar differ
index a656f14..870acb1 100644 (file)
@@ -12,8 +12,10 @@ import java.net.URL;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.function.Function;
 
+import org.hamcrest.Matcher;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.DataProvider;
@@ -21,9 +23,14 @@ import org.testng.annotations.Test;
 
 import jalview.bin.Cache;
 import jalview.bin.Console;
+import jalview.ws.params.ValueConstrainI.ValueType;
+import jalview.ws.params.simple.DoubleParameter;
+import jalview.ws.params.simple.IntegerParameter;
+import jalview.ws.params.simple.StringParameter;
 import jalview.ws2.actions.alignment.AlignmentAction;
 import jalview.ws2.actions.annotation.AnnotationAction;
 import jalview.ws2.client.api.WebServiceDiscovererI;
+import uk.ac.dundee.compbio.slivkaclient.Parameter;
 import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
 import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
 
@@ -52,45 +59,45 @@ public class SlivkaWSDiscovererTest
   public void getStatusForUrl_servicesReturned_statusIsOK() throws Exception
   {
     when(clientMock.getServices())
-            .thenReturn(List.of(mock(SlivkaService.class)));
+        .thenReturn(List.of(mock(SlivkaService.class)));
     var discoverer = new SlivkaWSDiscoverer(
-            url -> url.toString().equals("http://example.org") ? clientMock
-                    : null);
+        url -> url.toString().equals("http://example.org") ? clientMock
+            : null);
     assertThat(discoverer.getStatusForUrl(new URL("http://example.org")),
-            is(WebServiceDiscovererI.STATUS_OK));
+        is(WebServiceDiscovererI.STATUS_OK));
   }
 
   @Test
   public void getStatusForUrl_noServicesReturned_statusIsNoServices()
-          throws Exception
+      throws Exception
   {
     when(clientMock.getServices()).thenReturn(List.of());
     var discoverer = new SlivkaWSDiscoverer(
-            url -> url.toString().equals("http://example.org") ? clientMock
-                    : null);
+        url -> url.toString().equals("http://example.org") ? clientMock
+            : null);
     assertThat(discoverer.getStatusForUrl(new URL("http://example.org")),
-            is(WebServiceDiscovererI.STATUS_NO_SERVICES));
+        is(WebServiceDiscovererI.STATUS_NO_SERVICES));
   }
 
   @Test
   public void getStatusForUrl_exceptionThrown_statusIsInvalid()
-          throws Exception
+      throws Exception
   {
     when(clientMock.getServices()).thenThrow(new IOException());
     var discoverer = new SlivkaWSDiscoverer(
-            url -> url.toString().equals("http://example.org") ? clientMock
-                    : null);
+        url -> url.toString().equals("http://example.org") ? clientMock
+            : null);
     assertThat(discoverer.getStatusForUrl(new URL("http://example.org")),
-            is(WebServiceDiscovererI.STATUS_INVALID));
+        is(WebServiceDiscovererI.STATUS_INVALID));
   }
 
   @Test
   public void testGetUrls_noPropEntry_defaultUrlReturned()
-          throws MalformedURLException
+      throws MalformedURLException
   {
     var discoverer = SlivkaWSDiscoverer.getInstance();
     assertThat(discoverer.getUrls(),
-            contains(new URL("https://www.compbio.dundee.ac.uk/slivka/")));
+        contains(new URL("https://www.compbio.dundee.ac.uk/slivka/")));
   }
 
   @DataProvider
@@ -102,7 +109,7 @@ public class SlivkaWSDiscovererTest
             List.of(new URL("https://example.org/slivka/")) },
         { "https://www.compbio.dundee.ac.uk/,http://www.example.org/",
             List.of(new URL("https://www.compbio.dundee.ac.uk/"),
-                    new URL("http://www.example.org/")) },
+                new URL("http://www.example.org/")) },
         { "http://example.org/,", List.of(new URL("http://example.org/")) },
         { ",http://example.org", List.of(new URL("http://example.org")) },
         { "", List.of() },
@@ -114,7 +121,7 @@ public class SlivkaWSDiscovererTest
 
   @Test(dataProvider = "urlPropertyValues")
   public void testGetUrls_urlsProperlyParsed(String propValue,
-          List<URL> expected)
+      List<URL> expected)
   {
     Cache.setProperty(URLS_PROPERTY_NAME, propValue);
     var discoverer = SlivkaWSDiscoverer.getInstance();
@@ -148,13 +155,13 @@ public class SlivkaWSDiscovererTest
         { List.of(new URL("http://example.org/slivka/")),
             "http://example.org/slivka/" },
         { List.of(new URL("https://www.compbio.dundee.ac.uk/slivka/"),
-                new URL("http://example.org")),
+            new URL("http://example.org")),
             "https://www.compbio.dundee.ac.uk/slivka/,http://example.org" }, };
   }
 
   @Test(dataProvider = "urlsList")
   public void testSetUrls_urlsPropertySet(List<URL> urls, String expected)
-          throws MalformedURLException
+      throws MalformedURLException
   {
     var discoverer = SlivkaWSDiscoverer.getInstance();
     discoverer.setUrls(urls);
@@ -163,27 +170,27 @@ public class SlivkaWSDiscovererTest
 
   @Test
   public void testFetchServices_oneService_basicDataMatches()
-          throws IOException
+      throws IOException
   {
     var service = new SlivkaService(
-            URI.create("http://example.org/api/services/example"),
-            "example", "Example name", "Example service description",
-            "John Smith", "1.0", "MIT License",
-            List.of("operation::analysis::multiple sequence alignment"),
-            List.of(), List.of(), null);
+        URI.create("http://example.org/api/services/example"),
+        "example", "Example name", "Example service description",
+        "John Smith", "1.0", "MIT License",
+        List.of("operation::analysis::multiple sequence alignment"),
+        List.of(), List.of(), null);
     when(clientMock.getServices()).thenReturn(List.of(service));
     when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/"));
     var discoverer = new SlivkaWSDiscoverer(url -> clientMock);
     var webServices = discoverer
-            .fetchServices(new URL("http://example.org/"));
+        .fetchServices(new URL("http://example.org/"));
     assertThat(webServices, hasSize(1));
     var webService = webServices.get(0);
     assertThat(webService.getUrl(),
-            equalTo(new URL("http://example.org/")));
+        equalTo(new URL("http://example.org/")));
     assertThat(webService.getClientName(), equalTo("slivka"));
     assertThat(webService.getName(), equalTo("Example name"));
     assertThat(webService.getDescription(),
-            equalTo("Example service description"));
+        equalTo("Example service description"));
   }
 
   @DataProvider
@@ -207,119 +214,121 @@ public class SlivkaWSDiscovererTest
 
   @Test(dataProvider = "validMultipleSequenceAlignmentClassifiers")
   public void testFetchServices_multipleSequenceAlignmentClassifier_serviceTypeIsMSA(
-          String classifier) throws IOException
+      String classifier) throws IOException
   {
     var service = new SlivkaService(URI.create("http://example.org/"),
-            "example", "name", "description", "author", "1.0", "MIT",
-            List.of(classifier), List.of(), List.of(), null);
+        "example", "name", "description", "author", "1.0", "MIT",
+        List.of(classifier), List.of(), List.of(), null);
     when(clientMock.getServices()).thenReturn(List.of(service));
     when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/"));
     var discoverer = new SlivkaWSDiscoverer(url -> clientMock);
     var webServices = discoverer
-            .fetchServices(new URL("http://example.org/"));
+        .fetchServices(new URL("http://example.org/"));
     assertThat(webServices, hasSize(1));
     assertThat(webServices.get(0).getCategory(), equalTo("Alignment"));
     assertThat(webServices.get(0).getActionClass(),
-            typeCompatibleWith(AlignmentAction.class));
+        typeCompatibleWith(AlignmentAction.class));
   }
 
   @DataProvider
   public SlivkaService[] multipleSequenceAlignmentService()
   {
-    return new SlivkaService[] { new SlivkaService(
+    return new SlivkaService[] {
+        new SlivkaService(
             URI.create("http://example.org/"), "example", "Examaple name",
             "Example description", "John Smith", "1.0", "MIT",
             List.of("Operation :: Analysis :: Multiple sequence alignment"),
             List.of(), List.of(), null),
         new SlivkaService(
-                URI.create("http://example.org/api/services/muscle"),
-                "muscle", "MUSCLE",
-                "MUltiple Sequence Comparison by Log- Expectation",
-                "Robert C. Edgar", "3.8.31", "Public domain",
-                List.of("Topic :: Computational biology :: Sequence analysis",
-                        "Operation :: Analysis :: Sequence analysis :: Sequence alignment :: Multiple sequence alignment"),
-                List.of(), List.of(), null),
+            URI.create("http://example.org/api/services/muscle"),
+            "muscle", "MUSCLE",
+            "MUltiple Sequence Comparison by Log- Expectation",
+            "Robert C. Edgar", "3.8.31", "Public domain",
+            List.of("Topic :: Computational biology :: Sequence analysis",
+                "Operation :: Analysis :: Sequence analysis :: Sequence alignment :: Multiple sequence alignment"),
+            List.of(), List.of(), null),
         new SlivkaService(
-                URI.create("http://example.org/api/services/tcoffee"),
-                "tcoffee", "TCoffee",
-                "Tree-based Consistency Objective Function for Alignment Evaluation",
-                "Cedric Notredame", "13.41.0", "GNU GPL",
-                List.of("Topic :: Computational biology :: Sequence analysis",
-                        "Operation :: Analysis :: Sequence analysis :: Sequence alignment :: Multiple sequence alignment"),
-                List.of(), List.of(), null) };
+            URI.create("http://example.org/api/services/tcoffee"),
+            "tcoffee", "TCoffee",
+            "Tree-based Consistency Objective Function for Alignment Evaluation",
+            "Cedric Notredame", "13.41.0", "GNU GPL",
+            List.of("Topic :: Computational biology :: Sequence analysis",
+                "Operation :: Analysis :: Sequence analysis :: Sequence alignment :: Multiple sequence alignment"),
+            List.of(), List.of(), null) };
   }
 
   @Test(dataProvider = "multipleSequenceAlignmentService")
   public void testFetchServices_multipleSequenceAlignmentService_actionTypeIsAlignment(
-          SlivkaService service) throws IOException
+      SlivkaService service) throws IOException
   {
     when(clientMock.getServices()).thenReturn(List.of(service));
     when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/"));
     var discoverer = new SlivkaWSDiscoverer(url -> clientMock);
     var webServices = discoverer
-            .fetchServices(new URL("http://example.org/"));
+        .fetchServices(new URL("http://example.org/"));
     assertThat(webServices.get(0).getCategory(), equalTo("Alignment"));
     assertThat(webServices.get(0).getActionClass(),
-            typeCompatibleWith(AlignmentAction.class));
+        typeCompatibleWith(AlignmentAction.class));
   }
 
   @Test(dataProvider = "multipleSequenceAlignmentService")
   public void testFetchServices_multipleSequenceAlignmentService_serviceIsNonInteractive(
-          SlivkaService service) throws IOException
+      SlivkaService service) throws IOException
   {
     when(clientMock.getServices()).thenReturn(List.of(service));
     when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/"));
     var discoverer = new SlivkaWSDiscoverer(url -> clientMock);
     var webServices = discoverer
-            .fetchServices(new URL("http://example.org/"));
+        .fetchServices(new URL("http://example.org/"));
     assertThat(webServices.get(0).isInteractive(), is(false));
   }
 
   @DataProvider
   public SlivkaService[] clustalFamilyService()
   {
-    return new SlivkaService[] { new SlivkaService(
+    return new SlivkaService[] {
+        new SlivkaService(
             URI.create("http://example.org/api/services/clustalo"),
             "clustalo", "ClustalO",
             "Clustal Omega is the latest addition to the Clustal family.",
             "Fabian Sievers, et al.", "1.2.4", "GNU GPL ver. 2",
             List.of("Topic :: Computational biology :: Sequence analysis",
-                    "Operation :: Analysis :: Sequence analysis :: Sequence alignment :: Multiple sequence alignment"),
+                "Operation :: Analysis :: Sequence analysis :: Sequence alignment :: Multiple sequence alignment"),
             List.of(), List.of(), null),
         new SlivkaService(
-                URI.create("http://example.org/api/services/clustalw"),
-                "clustalw", "ClustalW",
-                "ClustalW is a general purpose multiple alignment program.",
-                "Larkin MA, et al.", "2.1", "GNU GPL ver. 3",
-                List.of("Topic :: Computation biology :: Sequence analysis",
-                        "Operation :: Analysis :: Multiple sequence alignment"),
-                List.of(), List.of(), null),
+            URI.create("http://example.org/api/services/clustalw"),
+            "clustalw", "ClustalW",
+            "ClustalW is a general purpose multiple alignment program.",
+            "Larkin MA, et al.", "2.1", "GNU GPL ver. 3",
+            List.of("Topic :: Computation biology :: Sequence analysis",
+                "Operation :: Analysis :: Multiple sequence alignment"),
+            List.of(), List.of(), null),
         new SlivkaService(
-                URI.create("http://example.org/api/services/clustalw2"),
-                "clustalw2", "ClustalW2",
-                "ClustalW is a general purpose multiple alignment program.",
-                "Larkin MA, et al.", "2.1", "GNU GPL ver. 3",
-                List.of("Topic :: Computation biology :: Sequence analysis",
-                        "Operation :: Analysis :: Multiple sequence alignment"),
-                List.of(), List.of(), null), };
+            URI.create("http://example.org/api/services/clustalw2"),
+            "clustalw2", "ClustalW2",
+            "ClustalW is a general purpose multiple alignment program.",
+            "Larkin MA, et al.", "2.1", "GNU GPL ver. 3",
+            List.of("Topic :: Computation biology :: Sequence analysis",
+                "Operation :: Analysis :: Multiple sequence alignment"),
+            List.of(), List.of(), null), };
   }
 
   @Test(dataProvider = "clustalFamilyService")
   public void testFetchService_clustalFamilyService_containsTwoActions(
-          SlivkaService service) throws IOException
+      SlivkaService service) throws IOException
   {
     when(clientMock.getServices()).thenReturn(List.of(service));
     when(clientMock.getUrl()).thenReturn(URI.create("http://example.org"));
     var discoverer = new SlivkaWSDiscoverer(url -> clientMock);
     var webServices = discoverer
-            .fetchServices(new URL("http://example.org"));
+        .fetchServices(new URL("http://example.org"));
     var actions = webServices.get(0).getActions();
     assertThat(actions, hasSize(2));
     assertThat(actions.get(0), allOf(hasProperty("name", is("Alignment")),
-            hasProperty("subcategory", is("Align"))));
+        hasProperty("subcategory", is("Align"))));
     assertThat(actions.get(1),
-            allOf(hasProperty("name", is("Re-alignment")),
-                    hasProperty("subcategory", is("Realign"))));
+        allOf(hasProperty("name", is("Re-alignment")),
+            hasProperty("subcategory", is("Realign"))));
   }
 
   @DataProvider
@@ -347,26 +356,26 @@ public class SlivkaWSDiscovererTest
     for (var classifier : validRNASecondaryStructurePredictionClassifiers())
     {
       services.add(new SlivkaService(URI.create("http://example.org/"),
-              "example", "name", "description", "author", "1.0", "MIT",
-              List.of(classifier), List.of(), List.of(), null));
+          "example", "name", "description", "author", "1.0", "MIT",
+          List.of(classifier), List.of(), List.of(), null));
     }
     return services.iterator();
   }
 
   @Test(dataProvider = "RNASecondaryStructurePredictionService")
   public void testFetchServices_RNASecStrPredClassifier_serviceTypeIsRNASecStrPred(
-          SlivkaService service) throws IOException
+      SlivkaService service) throws IOException
   {
     when(clientMock.getServices()).thenReturn(List.of(service));
     when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/"));
     var discoverer = new SlivkaWSDiscoverer(url -> clientMock);
     var webServices = discoverer
-            .fetchServices(new URL("http://example.org/"));
+        .fetchServices(new URL("http://example.org/"));
     assertThat(webServices, hasSize(1));
     assertThat(webServices.get(0).getCategory(),
-            equalTo("Secondary Structure Prediction"));
+        equalTo("Secondary Structure Prediction"));
     assertThat(webServices.get(0).getActionClass(),
-            typeCompatibleWith(AnnotationAction.class));
+        typeCompatibleWith(AnnotationAction.class));
   }
 
   @DataProvider
@@ -387,28 +396,28 @@ public class SlivkaWSDiscovererTest
     for (var classifier : validConservationAnalysisClassifiers())
     {
       services.add(new SlivkaService(URI.create("http://example.org/"),
-              "example", "name", "description", "author", "1.0", "MIT",
-              List.of(classifier), List.of(), List.of(), null));
+          "example", "name", "description", "author", "1.0", "MIT",
+          List.of(classifier), List.of(), List.of(), null));
     }
     return services.iterator();
   }
 
   @Test(dataProvider = "validConservationAnalysisClassifiers")
   public void testFetchServices_conservationAnalysisClassifier_serviceTypeIsConservation(
-          String classifier) throws IOException
+      String classifier) throws IOException
   {
     var service = new SlivkaService(URI.create("http://example.org/"),
-            "example", "name", "description", "author", "1.0", "MIT",
-            List.of(classifier), List.of(), List.of(), null);
+        "example", "name", "description", "author", "1.0", "MIT",
+        List.of(classifier), List.of(), List.of(), null);
     when(clientMock.getServices()).thenReturn(List.of(service));
     when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/"));
     var discoverer = new SlivkaWSDiscoverer(url -> clientMock);
     var webServices = discoverer
-            .fetchServices(new URL("http://example.org/"));
+        .fetchServices(new URL("http://example.org/"));
     assertThat(webServices, hasSize(1));
     assertThat(webServices.get(0).getCategory(), equalTo("Conservation"));
     assertThat(webServices.get(0).getActionClass(),
-            typeCompatibleWith(AnnotationAction.class));
+        typeCompatibleWith(AnnotationAction.class));
   }
 
   @DataProvider
@@ -420,21 +429,21 @@ public class SlivkaWSDiscovererTest
 
   @Test(dataProvider = "validProteinSequenceAnalysisClassifiers")
   public void testFetchServices_proteinSequenceAnalysisClassifier_serviceTypeIsProtSeqAnalysis(
-          String classifier) throws IOException
+      String classifier) throws IOException
   {
     var service = new SlivkaService(URI.create("http://example.org/"),
-            "example", "name", "description", "author", "1.0", "MIT",
-            List.of(classifier), List.of(), List.of(), null);
+        "example", "name", "description", "author", "1.0", "MIT",
+        List.of(classifier), List.of(), List.of(), null);
     when(clientMock.getServices()).thenReturn(List.of(service));
     when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/"));
     var discoverer = new SlivkaWSDiscoverer(url -> clientMock);
     var webServices = discoverer
-            .fetchServices(new URL("http://example.org/"));
+        .fetchServices(new URL("http://example.org/"));
     assertThat(webServices, hasSize(1));
     assertThat(webServices.get(0).getCategory(),
-            equalTo("Protein Disorder"));
+        equalTo("Protein Disorder"));
     assertThat(webServices.get(0).getActionClass(),
-            typeCompatibleWith(AnnotationAction.class));
+        typeCompatibleWith(AnnotationAction.class));
   }
 
   @DataProvider
@@ -453,20 +462,198 @@ public class SlivkaWSDiscovererTest
     enabled = false, // sec. str. pred. not implemented for slivka
     dataProvider = "validProteinSecondaryStructurePredictionClassifiers")
   public void testFetchServices_proteinSecStrPredClassifier_serviceTypeIsProtSecStrPred(
-          String classifier) throws IOException
+      String classifier) throws IOException
   {
     var service = new SlivkaService(URI.create("http://example.org/"),
-            "example", "name", "description", "author", "1.0", "MIT",
-            List.of(classifier), List.of(), List.of(), null);
+        "example", "name", "description", "author", "1.0", "MIT",
+        List.of(classifier), List.of(), List.of(), null);
     when(clientMock.getServices()).thenReturn(List.of(service));
     when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/"));
     var discoverer = new SlivkaWSDiscoverer(url -> clientMock);
     var webServices = discoverer
-            .fetchServices(new URL("http://example.org/"));
+        .fetchServices(new URL("http://example.org/"));
     assertThat(webServices, hasSize(1));
     assertThat(webServices.get(0).getCategory(),
-            equalTo("Protein Disorder"));
+        equalTo("Protein Disorder"));
     assertThat(webServices.get(0).getActionClass(),
-            typeCompatibleWith(AnnotationAction.class));
+        typeCompatibleWith(AnnotationAction.class));
+  }
+
+  @DataProvider
+  public SlivkaService[] unrecognisedService()
+  {
+    return new SlivkaService[] {
+        new SlivkaService(URI.create("http://example.org/"), "example",
+            "Example name", "Example description", "John Smith",
+            "1.0.0", "Apache License, version 2.0",
+            List.of("This :: Classifier :: Does not exist"), List.of(),
+            List.of(), null) };
+  }
+
+  @Test(dataProvider = "unrecognisedService")
+  public void testFetchServices_unrecognisedService_noServiceDiscovered(
+      SlivkaService service) throws IOException
+  {
+    when(clientMock.getServices()).thenReturn(List.of(service));
+    when(clientMock.getUrl()).thenReturn(URI.create("http://example.org"));
+    var discoverer = new SlivkaWSDiscoverer(url -> clientMock);
+    var webServices = discoverer
+        .fetchServices(new URL("http://example.org"));
+    assertThat(webServices, hasSize(0));
+  }
+
+  @DataProvider
+  public Object[] serviceParameterAndMappedClass()
+  {
+    return new Object[][] {
+        {
+            new Parameter.IntegerParameter("param", "Parameter", "Description",
+                true, false, null, Map.of(), null, null),
+            IntegerParameter.class
+        },
+        {
+            new Parameter.DecimalParameter("param", "Parameter",
+                "Description", true, false, null, Map.of(), null, null,
+                false, false),
+            DoubleParameter.class
+        },
+        {
+            new Parameter.TextParameter("param", "Parameter", "Description",
+                true, false, null, Map.of(), 0, null),
+            StringParameter.class
+        },
+        {
+            new Parameter.FlagParameter("param", "Parameter", "Description",
+                true, false, null, Map.of()),
+            StringParameter.class
+        },
+        {
+            new Parameter.ChoiceParameter("param", "Parameter", "Description",
+                true, false, null, Map.of(), List.of()),
+            StringParameter.class
+        },
+    };
+  }
+
+  @Test(dataProvider = "serviceParameterAndMappedClass")
+  public void testServiceParameter_slivkaParameterMappedToJalviewParameter(
+      Parameter slivkaParameter, Class<?> expectedClass)
+      throws IOException
+  {
+    var service = new SlivkaService(URI.create("http://example.org"),
+        "example", "name", "description", "author", "1.0",
+        "MIT License",
+        List.of("Operation :: Analysis :: Multiple sequence alignment"),
+        List.of(slivkaParameter), List.of(), null);
+    when(clientMock.getServices()).thenReturn(List.of(service));
+    when(clientMock.getUrl()).thenReturn(URI.create("http://example.org"));
+    var discoverer = new SlivkaWSDiscoverer(url -> clientMock);
+    var webServices = discoverer
+        .fetchServices(new URL("http://example.org"));
+    var paramDatastore = webServices.get(0).getParamDatastore();
+    var arguments = paramDatastore.getServiceParameters();
+    assertThat(arguments.get(0), instanceOf(expectedClass));
+  }
+
+  @DataProvider
+  public Object[][] serviceParametersAndPropertyMatcher()
+  {
+    return new Object[][] {
+        {
+            new Parameter.IntegerParameter("param1", "Parameter 1",
+                "Description of parameter 1", true, false, null, Map.of(),
+                null, null),
+            allOf(
+                hasProperty("name", equalTo("param1")),
+                hasProperty("label", equalTo("Parameter 1")),
+                hasProperty("description", equalTo("Description of parameter 1")),
+                hasProperty("required", is(true)),
+                hasProperty("value", nullValue()))
+        },
+        {
+            new Parameter.IntegerParameter("param2", null, null, true, false,
+                null, Map.of(), null, null),
+            allOf(
+                hasProperty("name", equalTo("param2")),
+                hasProperty("label", equalTo("param2")),
+                hasProperty("description", nullValue()),
+                hasProperty("required", is(true)),
+                hasProperty("value", nullValue()))
+        },
+        {
+            new Parameter.IntegerParameter("param3", "Parameter 3", "", false,
+                false, 12, Map.of(), null, null),
+            allOf(
+                hasProperty("name", equalTo("param3")),
+                hasProperty("label", equalTo("Parameter 3")),
+                hasProperty("description", equalTo("")),
+                hasProperty("required", is(false)),
+                hasProperty("value", equalTo("12")))
+        },
+    };
+  }
+
+  @Test(dataProvider = "serviceParametersAndInfoMatcher")
+  public void testServiceParameters_testBasicParameterProperties(
+      Parameter parameter, Matcher<Object> matcher) throws IOException
+  {
+    var service = new SlivkaService(URI.create("http://example.org/"),
+        "example", "Example name", "Example description", "John Smith",
+        "1.0", "MIT",
+        List.of("Operation :: Analysis :: Multiple sequence alignment"),
+        List.of(parameter), List.of(), null);
+    when(clientMock.getServices()).thenReturn(List.of(service));
+    when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/"));
+    var discoverer = new SlivkaWSDiscoverer(url -> clientMock);
+    var webServices = discoverer
+        .fetchServices(new URL("http://example.org/"));
+    var paramDatastore = webServices.get(0).getParamDatastore();
+    var arguments = paramDatastore.getServiceParameters();
+    assertThat(arguments.get(0), matcher);
+  }
+
+  @DataProvider
+  public Object[][] integerParametersAndPropertyMatcher()
+  {
+    return new Object[][] {
+        {
+            new Parameter.IntegerParameter("param", null, null, true, false,
+                null, Map.of(), null, null),
+            hasProperty("validValue", hasProperty("type", is(ValueType.Integer)))
+        },
+        {
+            new Parameter.IntegerParameter("param", null, null, true, false,
+                null, Map.of(), null, null),
+            hasProperty("validValue", allOf(
+                hasProperty("min", nullValue()),
+                hasProperty("max", nullValue()))),
+        },
+        {
+            new Parameter.IntegerParameter("param", null, null, true, false,
+                null, Map.of(), -12, 42),
+            hasProperty("validValue", allOf(
+                hasProperty("min", is(-12)),
+                hasProperty("max", is(42))))
+        },
+    };
+  }
+
+  @Test(dataProvider = "integerParametersAndPropertyMatcher")
+  public void testServiceParameters_testIntegerProperties(
+      Parameter parameter, Matcher<Object> matcher) throws IOException
+  {
+    var service = new SlivkaService(URI.create("http://example.org"),
+        "example", "Example name", "Example description", "John Smith",
+        "1.0", "MIT",
+        List.of("Operation :: Analysis :: Multiple Sequence Alignment"),
+        List.of(parameter), List.of(), null);
+    when(clientMock.getServices()).thenReturn(List.of(service));
+    when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/"));
+    var discoverer = new SlivkaWSDiscoverer(url -> clientMock);
+    var webServices = discoverer
+        .fetchServices(new URL("http://example.org/"));
+    var paramDatastore = webServices.get(0).getParamDatastore();
+    var arguments = paramDatastore.getServiceParameters();
+    assertThat(arguments.get(0), matcher);
   }
 }