You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2022/09/30 12:21:16 UTC

[karaf] branch main updated: KARAF-7552: add tests for feature:repo commands

This is an automated email from the ASF dual-hosted git repository.

jbonofre pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/karaf.git


The following commit(s) were added to refs/heads/main by this push:
     new 35eca03aac KARAF-7552: add tests for feature:repo commands
     new 2eb29580ad Merge pull request #1621 from awrb/KARAF-7552
35eca03aac is described below

commit 35eca03aaca414b45d093644d38b75d82a771758
Author: Aleksy Wróblewski <al...@bbbit.io>
AuthorDate: Sat Sep 17 21:35:23 2022 +0200

    KARAF-7552: add tests for feature:repo commands
---
 .../karaf/features/command/RepoAddCommand.java     |   8 +-
 .../karaf/features/command/RepoRefreshCommand.java |   4 +-
 .../karaf/features/command/RepoRemoveCommand.java  |   6 +-
 .../karaf/features/command/RepoAddCommandTest.java |  77 +++++++++++
 .../features/command/RepoListCommandTest.java      | 141 +++++++++++++++++++++
 .../features/command/RepoRefreshCommandTest.java   | 128 +++++++++++++++++++
 .../features/command/RepoRemoveCommandTest.java    | 112 ++++++++++++++++
 .../command/RepositoryCommandTestBase.java         |  32 +++++
 8 files changed, 499 insertions(+), 9 deletions(-)

diff --git a/features/command/src/main/java/org/apache/karaf/features/command/RepoAddCommand.java b/features/command/src/main/java/org/apache/karaf/features/command/RepoAddCommand.java
index 91ff430bf2..c5a1077542 100644
--- a/features/command/src/main/java/org/apache/karaf/features/command/RepoAddCommand.java
+++ b/features/command/src/main/java/org/apache/karaf/features/command/RepoAddCommand.java
@@ -32,13 +32,13 @@ public class RepoAddCommand extends FeaturesCommandSupport {
 
     @Argument(index = 0, name = "name/url", description = "Shortcut name of the features repository or the full URL", required = true, multiValued = false)
     @Completion(AvailableRepoNameCompleter.class)
-    private String nameOrUrl;
-    
+    String nameOrUrl;
+
     @Argument(index = 1, name = "version", description = "The version of the features repository if using features repository name as first argument. It should be empty if using the URL", required = false, multiValued = false)
-    private String version;
+    String version;
 
     @Option(name = "-i", aliases = { "--install" }, description = "Install all features contained in the features repository", required = false, multiValued = false)
-    private boolean install;
+    boolean install;
 
     @Override
     protected void doExecute(FeaturesService featuresService) throws Exception {
diff --git a/features/command/src/main/java/org/apache/karaf/features/command/RepoRefreshCommand.java b/features/command/src/main/java/org/apache/karaf/features/command/RepoRefreshCommand.java
index b20a4c8bb7..c0a3657d89 100644
--- a/features/command/src/main/java/org/apache/karaf/features/command/RepoRefreshCommand.java
+++ b/features/command/src/main/java/org/apache/karaf/features/command/RepoRefreshCommand.java
@@ -35,10 +35,10 @@ public class RepoRefreshCommand extends FeaturesCommandSupport {
     
     @Argument(index = 0, name = "repository", description = "Shortcut name of the feature repository or the full URI", required = false, multiValued = false)
     @Completion(InstalledRepoUriCompleter.class)
-    private String nameOrUrl;
+    String nameOrUrl;
     
     @Argument(index = 1, name = "Feature version", description = "The version of the feature if using the feature name. Should be empty if using the uri", required = false, multiValued = false)
-    private String version;
+    String version;
 
     @Override
     protected void doExecute(FeaturesService featuresService) throws Exception {
diff --git a/features/command/src/main/java/org/apache/karaf/features/command/RepoRemoveCommand.java b/features/command/src/main/java/org/apache/karaf/features/command/RepoRemoveCommand.java
index 63a7ee921c..4f556733ed 100644
--- a/features/command/src/main/java/org/apache/karaf/features/command/RepoRemoveCommand.java
+++ b/features/command/src/main/java/org/apache/karaf/features/command/RepoRemoveCommand.java
@@ -32,13 +32,13 @@ public class RepoRemoveCommand extends FeaturesCommandSupport {
 
 	@Argument(index = 0, name = "repository", description = "Shortcut name of the feature repository or the full URI", required = true, multiValued = false)
 	@Completion(InstalledRepoUriCompleter.class)
-	private String nameOrUrl;
+	String nameOrUrl;
 
 	@Argument(index = 1, name = "Feature version", description = "The version of the feature if using the feature name. Should be empty if using the uri", required = false, multiValued = false)
-	private String version;
+	String version;
 
     @Option(name = "-u", aliases = { "--uninstall-all" }, description = "Uninstall all features from the repository", required = false, multiValued = false)
-    private boolean uninstall;
+    boolean uninstall;
 
     protected void doExecute(FeaturesService featuresService) throws Exception {
     	URI uri;
diff --git a/features/command/src/test/java/org/apache/karaf/features/command/RepoAddCommandTest.java b/features/command/src/test/java/org/apache/karaf/features/command/RepoAddCommandTest.java
new file mode 100644
index 0000000000..90a477c99d
--- /dev/null
+++ b/features/command/src/test/java/org/apache/karaf/features/command/RepoAddCommandTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.features.command;
+
+import junit.framework.AssertionFailedError;
+import org.apache.karaf.features.FeaturesService;
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import java.net.URI;
+
+public class RepoAddCommandTest {
+    @Test
+    public void testAddBlacklistedRepository() throws Exception {
+        String name = "repo";
+        String version = "1.2.0-SNAPSHOT";
+        URI uri = URI.create("mvn:group:" + name + ":" + version);
+        FeaturesService service = EasyMock.createMock(FeaturesService.class);
+        EasyMock.expect(service.getRepositoryUriFor(name, version)).andReturn(uri);
+        EasyMock.expect(service.isRepositoryUriBlacklisted(uri)).andReturn(true);
+        service.addRepository(uri);
+        EasyMock.expectLastCall().andThrow(
+                new AssertionFailedError("Repository added despite being blacklisted")).anyTimes();
+
+        EasyMock.replay(service);
+
+        RepoAddCommand repoAddCommand = new RepoAddCommand();
+        repoAddCommand.setFeaturesService(service);
+        repoAddCommand.install = true;
+        repoAddCommand.nameOrUrl = name;
+        repoAddCommand.version = version;
+
+        repoAddCommand.execute();
+
+        EasyMock.verify(service);
+    }
+
+    @Test
+    public void testAddNonBlacklistedRepository() throws Exception {
+        String name = "repo";
+        String version = "1.2.0-SNAPSHOT";
+        boolean install = true;
+
+        URI uri = URI.create("mvn:group:" + name + ":" + version);
+        FeaturesService service = EasyMock.createMock(FeaturesService.class);
+        EasyMock.expect(service.getRepositoryUriFor(name, version)).andReturn(uri);
+        EasyMock.expect(service.isRepositoryUriBlacklisted(uri)).andReturn(false);
+        service.addRepository(uri, install);
+        EasyMock.expectLastCall();
+
+        EasyMock.replay(service);
+
+        RepoAddCommand repoAddCommand = new RepoAddCommand();
+        repoAddCommand.setFeaturesService(service);
+        repoAddCommand.install = install;
+        repoAddCommand.nameOrUrl = name;
+        repoAddCommand.version = version;
+
+        repoAddCommand.execute();
+
+        EasyMock.verify(service);
+    }
+}
diff --git a/features/command/src/test/java/org/apache/karaf/features/command/RepoListCommandTest.java b/features/command/src/test/java/org/apache/karaf/features/command/RepoListCommandTest.java
new file mode 100644
index 0000000000..888138b620
--- /dev/null
+++ b/features/command/src/test/java/org/apache/karaf/features/command/RepoListCommandTest.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.features.command;
+
+import org.apache.karaf.features.FeaturesService;
+import org.apache.karaf.features.Repository;
+import org.easymock.Capture;
+import org.easymock.CaptureType;
+import org.easymock.EasyMock;
+import org.junit.Test;
+import shaded.org.apache.commons.lang3.StringUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.net.URI;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class RepoListCommandTest extends RepositoryCommandTestBase {
+
+    @Test
+    public void testListReposAfterReload() throws Exception {
+        FeaturesService service = EasyMock.createMock(FeaturesService.class);
+
+        String repoName = "SomeRepository";
+        Repository repo = mockRepository(repoName, "1.0.0");
+        EasyMock.expect(repo.isBlacklisted()).andReturn(false);
+
+        EasyMock.expect(service.listRepositories()).andReturn(new Repository[]{repo}).anyTimes();
+        Capture<URI> uriCapture = Capture.newInstance(CaptureType.ALL);
+        service.addRepository(EasyMock.capture(uriCapture));
+        EasyMock.expectLastCall().times(1);
+
+        EasyMock.replay(service, repo);
+
+        RepoListCommand repoListCommand = new RepoListCommand();
+        repoListCommand.setFeaturesService(service);
+        repoListCommand.showBlacklisted = false;
+        repoListCommand.noFormat = true;
+        repoListCommand.reload = true;
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream out = new PrintStream(baos);
+        System.setOut(out);
+
+        repoListCommand.execute();
+        out.flush();
+
+        assertEquals(repo.getURI(), uriCapture.getValue());
+
+        String commandOutput = baos.toString();
+        assertTrue(commandOutput.contains(repoName + "\t" + repo.getURI()));
+
+        EasyMock.verify(service, repo);
+    }
+
+    @Test
+    public void testBlacklistedRepositoriesNotShown() throws Exception {
+        FeaturesService service = EasyMock.createMock(FeaturesService.class);
+        Repository blacklistedRepo = EasyMock.createMock(Repository.class);
+        EasyMock.expect(blacklistedRepo.isBlacklisted()).andReturn(true);
+
+        String whitelistedRepoName = "SomeRepository";
+        Repository whitelistedRepo = mockRepository(whitelistedRepoName, "1.0.0");
+        EasyMock.expect(whitelistedRepo.isBlacklisted()).andReturn(false);
+
+        EasyMock.expect(service.listRepositories()).andReturn(new Repository[]{blacklistedRepo, whitelistedRepo});
+
+        EasyMock.replay(service, blacklistedRepo, whitelistedRepo);
+
+        RepoListCommand repoListCommand = new RepoListCommand();
+        repoListCommand.setFeaturesService(service);
+        repoListCommand.showBlacklisted = false;
+        repoListCommand.noFormat = true;
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream out = new PrintStream(baos);
+        System.setOut(out);
+
+        repoListCommand.execute();
+        out.flush();
+
+        String commandOutput = baos.toString();
+        assertTrue(commandOutput.contains(whitelistedRepoName + "\t" + whitelistedRepo.getURI()));
+
+        EasyMock.verify(service, blacklistedRepo, whitelistedRepo);
+    }
+
+    @Test
+    public void testBlacklistedRepositoriesShown() throws Exception {
+        FeaturesService service = EasyMock.createMock(FeaturesService.class);
+        String blacklistedRepoName = "BlacklistedRepo";
+        Repository blacklistedRepo = mockRepository(blacklistedRepoName, "1.0.0");
+        EasyMock.expect(blacklistedRepo.isBlacklisted()).andReturn(true);
+
+        String whitelistedRepoName = "SomeRepository";
+        Repository whitelistedRepo = mockRepository(whitelistedRepoName, "1.0.0");
+        EasyMock.expect(whitelistedRepo.isBlacklisted()).andReturn(false);
+
+        EasyMock.expect(service.listRepositories()).andReturn(new Repository[]{blacklistedRepo, whitelistedRepo});
+
+        EasyMock.replay(service, blacklistedRepo, whitelistedRepo);
+
+        RepoListCommand repoListCommand = new RepoListCommand();
+        repoListCommand.setFeaturesService(service);
+        repoListCommand.showBlacklisted = true;
+        repoListCommand.noFormat = true;
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream out = new PrintStream(baos);
+        System.setOut(out);
+
+        repoListCommand.execute();
+        out.flush();
+
+        String[] commandOutput = baos.toString().split("\\R");
+        assertEquals(2, commandOutput.length);
+        String blacklistedRepoLineNoSpaces = blacklistedRepoName + blacklistedRepo.getURI() + "yes";
+        assertEquals(StringUtils.deleteWhitespace(commandOutput[0].trim()), blacklistedRepoLineNoSpaces);
+
+        String whitelistedRepoLineNoSpaces = whitelistedRepoName + whitelistedRepo.getURI() + "no";
+        assertEquals(StringUtils.deleteWhitespace(commandOutput[1]), whitelistedRepoLineNoSpaces);
+
+        EasyMock.verify(service, blacklistedRepo, whitelistedRepo);
+    }
+}
diff --git a/features/command/src/test/java/org/apache/karaf/features/command/RepoRefreshCommandTest.java b/features/command/src/test/java/org/apache/karaf/features/command/RepoRefreshCommandTest.java
new file mode 100644
index 0000000000..c43c6f8ec3
--- /dev/null
+++ b/features/command/src/test/java/org/apache/karaf/features/command/RepoRefreshCommandTest.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.features.command;
+
+import org.apache.karaf.features.FeaturesService;
+import org.apache.karaf.features.Repository;
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+
+public class RepoRefreshCommandTest extends RepositoryCommandTestBase {
+    @Test
+    public void testRefreshAllRepositories() throws Exception {
+        FeaturesService service = EasyMock.createMock(FeaturesService.class);
+
+        Repository repo1 = EasyMock.createMock(Repository.class);
+        URI repo1Uri = URI.create("mvn:group:repo1:1.0.0");
+        EasyMock.expect(repo1.getURI()).andReturn(repo1Uri);
+
+        Repository repo2 = EasyMock.createMock(Repository.class);
+        URI repo2Uri = URI.create("mvn:group2:repo2:2.0.0");
+        EasyMock.expect(repo2.getURI()).andReturn(repo2Uri);
+
+        EasyMock.expect(service.listRepositories()).andReturn(new Repository[]{repo1, repo2});
+
+        service.refreshRepositories(new LinkedHashSet<>(Arrays.asList(repo1Uri, repo2Uri)));
+        EasyMock.expectLastCall();
+
+        EasyMock.replay(service, repo1, repo2);
+
+        RepoRefreshCommand repoRefreshCommand = new RepoRefreshCommand();
+        repoRefreshCommand.setFeaturesService(service);
+
+        repoRefreshCommand.execute();
+
+        EasyMock.verify(service, repo1, repo2);
+    }
+
+    @Test
+    public void testRefreshRepositorySpecificVersion() throws Exception {
+        FeaturesService service = EasyMock.createMock(FeaturesService.class);
+        String repoName = "specificRepo";
+        String repoVersion = "1.0.0";
+        URI uri = URI.create("mvn:group:" + repoName + ":" + repoVersion);
+        EasyMock.expect(service.getRepositoryUriFor(repoName, repoVersion)).andReturn(uri);
+
+        service.refreshRepositories(new LinkedHashSet<>(Collections.singletonList(uri)));
+        EasyMock.expectLastCall();
+
+        EasyMock.replay(service);
+
+        RepoRefreshCommand repoRefreshCommand = new RepoRefreshCommand();
+        repoRefreshCommand.nameOrUrl = repoName;
+        repoRefreshCommand.version = repoVersion;
+        repoRefreshCommand.setFeaturesService(service);
+
+        repoRefreshCommand.execute();
+
+        EasyMock.verify(service);
+    }
+
+    @Test
+    public void testRefreshRepositoryLatestVersion() throws Exception {
+        FeaturesService service = EasyMock.createMock(FeaturesService.class);
+        String repoName = "specificRepo";
+        URI uri = URI.create("mvn:group:" + repoName + ":LATEST");
+        EasyMock.expect(service.getRepositoryUriFor(repoName, "LATEST")).andReturn(uri);
+
+        service.refreshRepositories(new LinkedHashSet<>(Collections.singletonList(uri)));
+        EasyMock.expectLastCall();
+
+        EasyMock.replay(service);
+
+        RepoRefreshCommand repoRefreshCommand = new RepoRefreshCommand();
+        repoRefreshCommand.nameOrUrl = repoName;
+        repoRefreshCommand.version = null;
+        repoRefreshCommand.setFeaturesService(service);
+
+        repoRefreshCommand.execute();
+
+        EasyMock.verify(service);
+    }
+
+    @Test
+    public void testRefreshRepositoriesMatchingNamePattern() throws Exception {
+        FeaturesService service = EasyMock.createMock(FeaturesService.class);
+        String repoName = "specificRepo";
+        String repoVersion = "1.0.0";
+        EasyMock.expect(service.getRepositoryUriFor(repoName, repoVersion)).andReturn(null);
+
+        Repository matchingRepo = mockRepository(repoName, repoVersion);
+        Repository nonMatchingRepo = mockRepository("someRepo", "1.0.0");
+
+        EasyMock.expect(service.listRepositories()).andReturn(new Repository[]{matchingRepo, nonMatchingRepo});
+
+        service.refreshRepositories(new LinkedHashSet<>(Collections.singletonList(EasyMock.anyObject(URI.class))));
+        EasyMock.expectLastCall();
+
+        EasyMock.replay(service, matchingRepo, nonMatchingRepo);
+
+        RepoRefreshCommand repoRefreshCommand = new RepoRefreshCommand();
+        repoRefreshCommand.nameOrUrl = repoName;
+        repoRefreshCommand.version = repoVersion;
+        repoRefreshCommand.setFeaturesService(service);
+
+        repoRefreshCommand.execute();
+
+        EasyMock.verify(service, matchingRepo, nonMatchingRepo);
+    }
+}
diff --git a/features/command/src/test/java/org/apache/karaf/features/command/RepoRemoveCommandTest.java b/features/command/src/test/java/org/apache/karaf/features/command/RepoRemoveCommandTest.java
new file mode 100644
index 0000000000..ae2ebe3fdf
--- /dev/null
+++ b/features/command/src/test/java/org/apache/karaf/features/command/RepoRemoveCommandTest.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.features.command;
+
+import junit.framework.AssertionFailedError;
+import org.apache.karaf.features.FeaturesService;
+import org.apache.karaf.features.Repository;
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import java.net.URI;
+
+public class RepoRemoveCommandTest extends RepositoryCommandTestBase {
+    @Test
+    public void testRemoveRepo() throws Exception {
+        FeaturesService service = EasyMock.createMock(FeaturesService.class);
+        String repoName = "specificRepo";
+        String repoVersion = "1.0.0";
+        boolean uninstall = true;
+
+        URI uri = URI.create("mvn:group:" + repoName + ":" + repoVersion);
+        EasyMock.expect(service.getRepositoryUriFor(repoName, repoVersion)).andReturn(uri);
+
+        service.removeRepository(uri, uninstall);
+        EasyMock.expectLastCall();
+
+        EasyMock.replay(service);
+
+        RepoRemoveCommand repoRemoveCommand = new RepoRemoveCommand();
+        repoRemoveCommand.nameOrUrl = repoName;
+        repoRemoveCommand.version = repoVersion;
+        repoRemoveCommand.uninstall = uninstall;
+        repoRemoveCommand.setFeaturesService(service);
+
+        repoRemoveCommand.execute();
+
+        EasyMock.verify(service);
+    }
+
+    @Test
+    public void testNoMatchingRepo() throws Exception {
+        FeaturesService service = EasyMock.createMock(FeaturesService.class);
+        String repoName = "specificRepo";
+        String repoVersion = "1.0.0";
+        boolean uninstall = true;
+
+        URI uri = URI.create("mvn:group:" + repoName + ":" + repoVersion);
+        EasyMock.expect(service.getRepositoryUriFor(repoName, repoVersion)).andReturn(null);
+        EasyMock.expect(service.listRepositories()).andReturn(new Repository[]{});
+
+        service.removeRepository(uri, uninstall);
+        EasyMock.expectLastCall().andThrow(new AssertionFailedError()).anyTimes();
+
+        EasyMock.replay(service);
+
+        RepoRemoveCommand repoRemoveCommand = new RepoRemoveCommand();
+        repoRemoveCommand.nameOrUrl = repoName;
+        repoRemoveCommand.version = repoVersion;
+        repoRemoveCommand.uninstall = uninstall;
+        repoRemoveCommand.setFeaturesService(service);
+
+        repoRemoveCommand.execute();
+
+        EasyMock.verify(service);
+    }
+
+    @Test
+    public void testMultipleReposMatching() throws Exception {
+        String duplicatedRepoName = "repo";
+        String repoVersion = "1.0.0";
+        boolean uninstall = true;
+
+        FeaturesService service = EasyMock.createMock(FeaturesService.class);
+
+        EasyMock.expect(service.getRepositoryUriFor(duplicatedRepoName, repoVersion)).andReturn(null);
+
+        Repository repo1 = mockRepository(duplicatedRepoName, "2.0.0");
+        Repository repo2 = mockRepository(duplicatedRepoName, "3.0.0");
+
+        EasyMock.expect(service.listRepositories()).andReturn(new Repository[]{repo1, repo2});
+
+        service.removeRepository(EasyMock.anyObject(URI.class), EasyMock.anyBoolean());
+        EasyMock.expectLastCall().andThrow(new AssertionFailedError(
+                "Removed a repo even though it shouldn't have because multiple repos matched the criteria")).anyTimes();
+
+        EasyMock.replay(service, repo1, repo2);
+
+        RepoRemoveCommand repoRemoveCommand = new RepoRemoveCommand();
+        repoRemoveCommand.uninstall = uninstall;
+        repoRemoveCommand.nameOrUrl = duplicatedRepoName;
+        repoRemoveCommand.version = repoVersion;
+        repoRemoveCommand.setFeaturesService(service);
+
+        repoRemoveCommand.execute();
+
+        EasyMock.verify(service, repo1, repo2);
+    }
+}
diff --git a/features/command/src/test/java/org/apache/karaf/features/command/RepositoryCommandTestBase.java b/features/command/src/test/java/org/apache/karaf/features/command/RepositoryCommandTestBase.java
new file mode 100644
index 0000000000..43516322d2
--- /dev/null
+++ b/features/command/src/test/java/org/apache/karaf/features/command/RepositoryCommandTestBase.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.features.command;
+
+import org.apache.karaf.features.Repository;
+import org.easymock.EasyMock;
+
+import java.net.URI;
+
+public class RepositoryCommandTestBase {
+    protected Repository mockRepository(String repoName, String version) {
+        Repository repo = EasyMock.createMock(Repository.class);
+        URI repoUri = URI.create("mvn:group:" + repoName + ":" + version);
+        EasyMock.expect(repo.getURI()).andReturn(repoUri).anyTimes();
+        EasyMock.expect(repo.getName()).andReturn(repoName).anyTimes();
+        return repo;
+    }
+}