mirror of https://github.com/apache/cloudstack.git
minor cleanup and tests
Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
parent
574f0ea40a
commit
568c1aab7a
|
|
@ -19,6 +19,7 @@ package org.apache.cloudstack.veeam;
|
|||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -59,7 +60,7 @@ public class VeeamControlServlet extends HttpServlet {
|
|||
@Override
|
||||
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
|
||||
String method = req.getMethod();
|
||||
String path = normalize(req.getPathInfo());
|
||||
String path = normalize(req);
|
||||
Negotiation.OutFormat outFormat = Negotiation.responseFormat(req);
|
||||
|
||||
LOGGER.info("Received {} request for {} with out format: {}", method, path, outFormat);
|
||||
|
|
@ -107,9 +108,15 @@ public class VeeamControlServlet extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
private String normalize(String pathInfo) {
|
||||
if (pathInfo == null || pathInfo.isBlank()) return "/";
|
||||
return pathInfo;
|
||||
private String normalize(HttpServletRequest req) {
|
||||
String path = req.getPathInfo();
|
||||
if (path == null || path.isBlank()) {
|
||||
path = req.getRequestURI();
|
||||
}
|
||||
if (path == null || path.isBlank()) {
|
||||
return "/";
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
protected void handleRoot(HttpServletRequest req, HttpServletResponse resp, Negotiation.OutFormat outFormat)
|
||||
|
|
@ -117,13 +124,13 @@ public class VeeamControlServlet extends HttpServlet {
|
|||
|
||||
String method = req.getMethod();
|
||||
if (!"GET".equals(method) && !"POST".equals(method)) {
|
||||
// You didn’t list 405; keep it simple with 400
|
||||
throw Error.badRequest("Unsupported method for root: " + method);
|
||||
}
|
||||
Map<String, String> responseData = new HashMap<>();
|
||||
responseData.put("name", VeeamControlService.PLUGIN_NAME);
|
||||
responseData.put("pluginVersion", this.getClass().getPackage().getImplementationVersion());
|
||||
|
||||
writer.write(resp, 200, Map.of(
|
||||
"name", VeeamControlService.PLUGIN_NAME,
|
||||
"pluginVersion", this.getClass().getPackage().getImplementationVersion()), outFormat);
|
||||
writer.write(resp, 200, responseData, outFormat);
|
||||
}
|
||||
|
||||
public void methodNotAllowed(final HttpServletResponse resp, final String allow, final Negotiation.OutFormat outFormat) throws IOException {
|
||||
|
|
|
|||
|
|
@ -26,17 +26,11 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RolePermissionEntity;
|
||||
import org.apache.cloudstack.acl.RoleService;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.Rule;
|
||||
import org.apache.cloudstack.acl.SecurityChecker;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupVO;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
||||
|
|
@ -58,7 +52,6 @@ import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd;
|
|||
import org.apache.cloudstack.api.command.admin.vm.DeployVMCmdByAdmin;
|
||||
import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd;
|
||||
import org.apache.cloudstack.api.command.user.job.ListAsyncJobsCmd;
|
||||
import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd;
|
||||
import org.apache.cloudstack.api.command.user.network.ListNetworksCmd;
|
||||
import org.apache.cloudstack.api.command.user.offering.ListServiceOfferingsCmd;
|
||||
import org.apache.cloudstack.api.command.user.tag.ListTagsCmd;
|
||||
|
|
@ -77,11 +70,8 @@ import org.apache.cloudstack.api.command.user.vmsnapshot.RevertToVMSnapshotCmd;
|
|||
import org.apache.cloudstack.api.command.user.volume.AssignVolumeCmd;
|
||||
import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
|
||||
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
|
||||
import org.apache.cloudstack.api.command.user.volume.DeleteVolumeCmd;
|
||||
import org.apache.cloudstack.api.command.user.volume.DestroyVolumeCmd;
|
||||
import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
|
||||
import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd;
|
||||
import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
|
||||
import org.apache.cloudstack.api.command.user.volume.UpdateVolumeCmd;
|
||||
import org.apache.cloudstack.api.command.user.zone.ListZonesCmd;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
|
|
@ -100,7 +90,6 @@ import org.apache.cloudstack.query.QueryService;
|
|||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.veeam.VeeamControlService;
|
||||
import org.apache.cloudstack.veeam.api.TagsRouteHandler;
|
||||
import org.apache.cloudstack.veeam.api.converter.AsyncJobJoinVOToJobConverter;
|
||||
import org.apache.cloudstack.veeam.api.converter.BackupVOToBackupConverter;
|
||||
import org.apache.cloudstack.veeam.api.converter.ClusterVOToClusterConverter;
|
||||
|
|
@ -190,7 +179,6 @@ import com.cloud.user.Account;
|
|||
import com.cloud.user.AccountService;
|
||||
import com.cloud.user.DomainService;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.UserAccount;
|
||||
import com.cloud.user.UserDataVO;
|
||||
import com.cloud.user.dao.UserDataDao;
|
||||
import com.cloud.uservm.UserVm;
|
||||
|
|
@ -211,28 +199,7 @@ import com.cloud.vm.dao.VMInstanceDetailsDao;
|
|||
import com.cloud.vm.snapshot.VMSnapshotVO;
|
||||
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
|
||||
|
||||
// ToDo: check access for list APIs when not ROOT admin
|
||||
|
||||
public class ServerAdapter extends ManagerBase {
|
||||
private static final String SERVICE_ACCOUNT_NAME = "veemserviceuser";
|
||||
private static final String SERVICE_ACCOUNT_ROLE_NAME = "Veeam Service Role";
|
||||
private static final String SERVICE_ACCOUNT_FIRST_NAME = "Veeam";
|
||||
private static final String SERVICE_ACCOUNT_LAST_NAME = "Service User";
|
||||
private static final List<Class<?>> SERVICE_ACCOUNT_ROLE_ALLOWED_APIS = Arrays.asList(
|
||||
QueryAsyncJobResultCmd.class,
|
||||
ListVMsCmd.class,
|
||||
DeployVMCmd.class,
|
||||
StartVMCmd.class,
|
||||
StopVMCmd.class,
|
||||
DestroyVMCmd.class,
|
||||
ListVolumesCmd.class,
|
||||
CreateVolumeCmd.class,
|
||||
DeleteVolumeCmd.class,
|
||||
AttachVolumeCmd.class,
|
||||
DetachVolumeCmd.class,
|
||||
ResizeVolumeCmd.class,
|
||||
ListNetworksCmd.class
|
||||
);
|
||||
private static final List<Storage.StoragePoolType> SUPPORTED_STORAGE_TYPES = Arrays.asList(
|
||||
Storage.StoragePoolType.Filesystem,
|
||||
Storage.StoragePoolType.NetworkFilesystem,
|
||||
|
|
@ -241,9 +208,6 @@ public class ServerAdapter extends ManagerBase {
|
|||
private static final String VM_TA_KEY = "veeam_tag";
|
||||
private static final String WORKER_VM_GUEST_CPU_MODE = "host-passthrough";
|
||||
|
||||
@Inject
|
||||
RoleService roleService;
|
||||
|
||||
@Inject
|
||||
AccountService accountService;
|
||||
|
||||
|
|
@ -346,17 +310,6 @@ public class ServerAdapter extends ManagerBase {
|
|||
@Inject
|
||||
DomainDao domainDao;
|
||||
|
||||
protected static Tag getDummyTagByName(String name) {
|
||||
Tag tag = new Tag();
|
||||
String id = UUID.nameUUIDFromBytes(String.format("veeam:%s", name.toLowerCase()).getBytes()).toString();
|
||||
tag.setId(id);
|
||||
tag.setName(name);
|
||||
tag.setDescription(String.format("Default %s tag", name.toLowerCase()));
|
||||
tag.setHref(VeeamControlService.ContextPath.value() + TagsRouteHandler.BASE_ROUTE + "/" + id);
|
||||
tag.setParent(ResourceTagVOToTagConverter.getRootTagRef());
|
||||
return tag;
|
||||
}
|
||||
|
||||
protected static Map<String, Tag> getDummyTags() {
|
||||
Map<String, Tag> tags = new HashMap<>();
|
||||
Tag rootTag = ResourceTagVOToTagConverter.getRootTag();
|
||||
|
|
@ -364,56 +317,6 @@ public class ServerAdapter extends ManagerBase {
|
|||
return tags;
|
||||
}
|
||||
|
||||
protected Role createServiceAccountRole() {
|
||||
Role role = roleService.createRole(SERVICE_ACCOUNT_ROLE_NAME, RoleType.User,
|
||||
SERVICE_ACCOUNT_ROLE_NAME, false);
|
||||
for (Class<?> allowedApi : SERVICE_ACCOUNT_ROLE_ALLOWED_APIS) {
|
||||
final String apiName = BaseCmd.getCommandNameByClass(allowedApi);
|
||||
roleService.createRolePermission(role, new Rule(apiName), RolePermissionEntity.Permission.ALLOW,
|
||||
String.format("Allow %s", apiName));
|
||||
}
|
||||
roleService.createRolePermission(role, new Rule("*"), RolePermissionEntity.Permission.DENY,
|
||||
"Deny all");
|
||||
logger.debug("Created default role for Veeam service account in projects: {}", role);
|
||||
return role;
|
||||
}
|
||||
|
||||
protected Role getServiceAccountRole() {
|
||||
List<Role> roles = roleService.findRolesByName(SERVICE_ACCOUNT_ROLE_NAME);
|
||||
if (CollectionUtils.isNotEmpty(roles)) {
|
||||
Role role = roles.get(0);
|
||||
logger.debug("Found default role for Veeam service account in projects: {}", role);
|
||||
return role;
|
||||
}
|
||||
return createServiceAccountRole();
|
||||
}
|
||||
|
||||
protected UserAccount createServiceAccount() {
|
||||
CallContext.register(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM);
|
||||
try {
|
||||
Role role = getServiceAccountRole();
|
||||
UserAccount userAccount = accountService.createUserAccount(SERVICE_ACCOUNT_NAME,
|
||||
UUID.randomUUID().toString(), SERVICE_ACCOUNT_FIRST_NAME,
|
||||
SERVICE_ACCOUNT_LAST_NAME, null, null, SERVICE_ACCOUNT_NAME, Account.Type.NORMAL, role.getId(),
|
||||
1L, null, null, null, null, User.Source.NATIVE);
|
||||
logger.debug("Created Veeam service account: {}", userAccount);
|
||||
return userAccount;
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
|
||||
protected Pair<User, Account> getDefaultServiceAccount() {
|
||||
UserAccount userAccount = accountService.getActiveUserAccount(SERVICE_ACCOUNT_NAME, 1L);
|
||||
if (userAccount == null) {
|
||||
userAccount = createServiceAccount();
|
||||
} else {
|
||||
logger.debug("Veeam service user account found: {}", userAccount);
|
||||
}
|
||||
return new Pair<>(accountService.getActiveUser(userAccount.getId()),
|
||||
accountService.getActiveAccountById(userAccount.getAccountId()));
|
||||
}
|
||||
|
||||
protected void waitForJobCompletion(long jobId) {
|
||||
long timeoutNanos = TimeUnit.MINUTES.toNanos(5);
|
||||
final long deadline = System.nanoTime() + timeoutNanos;
|
||||
|
|
|
|||
|
|
@ -1,110 +0,0 @@
|
|||
// 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.cloudstack.veeam.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.cloudstack.veeam.VeeamControlService;
|
||||
import org.apache.cloudstack.veeam.VeeamControlServlet;
|
||||
|
||||
public class BasicAuthFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(
|
||||
ServletRequest request,
|
||||
ServletResponse response,
|
||||
FilterChain chain
|
||||
) throws IOException, ServletException {
|
||||
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
HttpServletResponse resp = (HttpServletResponse) response;
|
||||
|
||||
String expectedUser = VeeamControlService.Username.value();
|
||||
String expectedPass = VeeamControlService.Password.value();
|
||||
|
||||
String auth = req.getHeader("Authorization");
|
||||
if (auth == null || !auth.regionMatches(true, 0, "Basic ", 0, 6)) {
|
||||
unauthorized(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
String decoded;
|
||||
try {
|
||||
decoded = new String(
|
||||
Base64.getDecoder().decode(auth.substring(6)),
|
||||
StandardCharsets.UTF_8
|
||||
);
|
||||
} catch (IllegalArgumentException e) {
|
||||
unauthorized(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = decoded.indexOf(':');
|
||||
if (idx <= 0) {
|
||||
unauthorized(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
String user = decoded.substring(0, idx);
|
||||
String pass = decoded.substring(idx + 1);
|
||||
|
||||
if (!constantTimeEquals(user, expectedUser)
|
||||
|| !constantTimeEquals(pass, expectedPass)) {
|
||||
unauthorized(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private void unauthorized(HttpServletResponse resp) {
|
||||
throw VeeamControlServlet.Error.unauthorized("Unauthorized");
|
||||
}
|
||||
|
||||
private boolean constantTimeEquals(String a, String b) {
|
||||
byte[] x = a.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] y = b.getBytes(StandardCharsets.UTF_8);
|
||||
if (x.length != y.length) return false;
|
||||
int r = 0;
|
||||
for (int i = 0; i < x.length; i++) {
|
||||
r |= x[i] ^ y[i];
|
||||
}
|
||||
return r == 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
// 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.cloudstack.veeam;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey.Scope;
|
||||
import org.apache.cloudstack.framework.config.impl.ConfigDepotImpl;
|
||||
import org.apache.cloudstack.utils.server.ServerPropertiesUtil;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
public class VeeamControlServerTest {
|
||||
|
||||
private static final String KEY_ENABLED = "integration.veeam.control.enabled";
|
||||
private static final String KEY_PORT = "integration.veeam.control.port";
|
||||
private static final String KEY_CONTEXT_PATH = "integration.veeam.control.context.path";
|
||||
private static final String KEY_DEVELOPER_LOGS = "integration.veeam.control.developer.logs";
|
||||
|
||||
private ConfigDepotImpl previousDepot;
|
||||
private Properties previousServerProperties;
|
||||
private final Map<String, String> globalValues = new HashMap<>();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
previousDepot = getConfigDepot();
|
||||
final ConfigDepotImpl depot = mock(ConfigDepotImpl.class);
|
||||
when(depot.getConfigStringValue(Mockito.anyString(), Mockito.any(), Mockito.any())).thenAnswer(invocation -> {
|
||||
final String key = invocation.getArgument(0);
|
||||
final Scope scope = invocation.getArgument(1);
|
||||
if (scope == Scope.Global) {
|
||||
return globalValues.get(key);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
setConfigDepot(depot);
|
||||
|
||||
previousServerProperties = getServerProperties();
|
||||
Properties props = new Properties();
|
||||
props.setProperty("https.keystore", "");
|
||||
props.setProperty("https.keystore.password", "");
|
||||
setServerProperties(props);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
setConfigDepot(previousDepot);
|
||||
setServerProperties(previousServerProperties);
|
||||
resetConfigKeyCache(VeeamControlService.Enabled);
|
||||
resetConfigKeyCache(VeeamControlService.Port);
|
||||
resetConfigKeyCache(VeeamControlService.ContextPath);
|
||||
resetConfigKeyCache(VeeamControlService.DeveloperLogs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorSortsHandlersByPriorityDescending() throws Exception {
|
||||
final RouteHandler low = mock(RouteHandler.class);
|
||||
final RouteHandler high = mock(RouteHandler.class);
|
||||
when(low.priority()).thenReturn(1);
|
||||
when(high.priority()).thenReturn(10);
|
||||
|
||||
final VeeamControlServer server = new VeeamControlServer(List.of(low, high), mock(VeeamControlService.class));
|
||||
final List<RouteHandler> handlers = getRouteHandlers(server);
|
||||
|
||||
assertEquals(high, handlers.get(0));
|
||||
assertEquals(low, handlers.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartIfEnabledReturnsWithoutStartingWhenDisabled() throws Exception {
|
||||
globalValues.put(KEY_ENABLED, "false");
|
||||
resetConfigKeyCache(VeeamControlService.Enabled);
|
||||
|
||||
final VeeamControlServer server = new VeeamControlServer(List.of(), mock(VeeamControlService.class));
|
||||
server.startIfEnabled();
|
||||
|
||||
assertNull(getJettyServer(server));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartIfEnabledStartsHttpServerWhenEnabled() throws Exception {
|
||||
globalValues.put(KEY_ENABLED, "true");
|
||||
globalValues.put(KEY_PORT, "0");
|
||||
globalValues.put(KEY_CONTEXT_PATH, "/ovirt-engine");
|
||||
resetConfigKeyCache(VeeamControlService.Enabled);
|
||||
resetConfigKeyCache(VeeamControlService.Port);
|
||||
resetConfigKeyCache(VeeamControlService.ContextPath);
|
||||
|
||||
final VeeamControlService service = mock(VeeamControlService.class);
|
||||
when(service.getCurrentManagementServerHostId()).thenReturn(1L);
|
||||
|
||||
final VeeamControlServer server = new VeeamControlServer(List.of(), service);
|
||||
try {
|
||||
server.startIfEnabled();
|
||||
final Server jetty = getJettyServer(server);
|
||||
assertNotNull(jetty);
|
||||
assertTrue(jetty.isStarted());
|
||||
} finally {
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopStopsExistingJettyServerAndClearsReference() throws Exception {
|
||||
final VeeamControlServer server = new VeeamControlServer(List.of(), mock(VeeamControlService.class));
|
||||
final Server jetty = mock(Server.class);
|
||||
setJettyServer(server, jetty);
|
||||
|
||||
server.stop();
|
||||
|
||||
verify(jetty).stop();
|
||||
assertNull(getJettyServer(server));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRequestResponseMetadataIncludesHeadersWhenDeveloperLogsEnabled() throws Exception {
|
||||
globalValues.put(KEY_DEVELOPER_LOGS, "true");
|
||||
resetConfigKeyCache(VeeamControlService.DeveloperLogs);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/api");
|
||||
request.setRemoteAddr("127.0.0.1");
|
||||
request.setQueryString("x=1");
|
||||
request.addHeader("X-Test", "abc");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.setStatus(202);
|
||||
|
||||
final Method metadataMethod = VeeamControlServer.class.getDeclaredMethod("getRequestResponseMetadata",
|
||||
HttpServletRequest.class, HttpServletResponse.class);
|
||||
metadataMethod.setAccessible(true);
|
||||
final String metadata = (String) metadataMethod.invoke(null, request, response);
|
||||
|
||||
assertTrue(metadata.contains("remote address: 127.0.0.1"));
|
||||
assertTrue(metadata.contains("uri: /api?x=1"));
|
||||
assertTrue(metadata.contains("headers: [X-Test=abc;"));
|
||||
assertTrue(metadata.contains("status: 202"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<RouteHandler> getRouteHandlers(final VeeamControlServer server) throws Exception {
|
||||
final Field field = VeeamControlServer.class.getDeclaredField("routeHandlers");
|
||||
field.setAccessible(true);
|
||||
return (List<RouteHandler>) field.get(server);
|
||||
}
|
||||
|
||||
private static Server getJettyServer(final VeeamControlServer server) throws Exception {
|
||||
final Field field = VeeamControlServer.class.getDeclaredField("server");
|
||||
field.setAccessible(true);
|
||||
return (Server) field.get(server);
|
||||
}
|
||||
|
||||
private static void setJettyServer(final VeeamControlServer server, final Server jetty) throws Exception {
|
||||
final Field field = VeeamControlServer.class.getDeclaredField("server");
|
||||
field.setAccessible(true);
|
||||
field.set(server, jetty);
|
||||
}
|
||||
|
||||
private static ConfigDepotImpl getConfigDepot() throws Exception {
|
||||
final Field field = ConfigKey.class.getDeclaredField("s_depot");
|
||||
field.setAccessible(true);
|
||||
return (ConfigDepotImpl) field.get(null);
|
||||
}
|
||||
|
||||
private static void setConfigDepot(final ConfigDepotImpl depot) throws Exception {
|
||||
final Field field = ConfigKey.class.getDeclaredField("s_depot");
|
||||
field.setAccessible(true);
|
||||
field.set(null, depot);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Properties getServerProperties() throws Exception {
|
||||
final Field field = ServerPropertiesUtil.class.getDeclaredField("propertiesRef");
|
||||
field.setAccessible(true);
|
||||
final AtomicReference<Properties> ref = (AtomicReference<Properties>) field.get(null);
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void setServerProperties(final Properties properties) throws Exception {
|
||||
final Field field = ServerPropertiesUtil.class.getDeclaredField("propertiesRef");
|
||||
field.setAccessible(true);
|
||||
final AtomicReference<Properties> ref = (AtomicReference<Properties>) field.get(null);
|
||||
ref.set(properties);
|
||||
}
|
||||
|
||||
private static void resetConfigKeyCache(final ConfigKey<?> configKey) throws Exception {
|
||||
final Field valueField = ConfigKey.class.getDeclaredField("_value");
|
||||
valueField.setAccessible(true);
|
||||
valueField.set(configKey, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -16,25 +16,170 @@
|
|||
// under the License.
|
||||
package org.apache.cloudstack.veeam;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey.Scope;
|
||||
import org.apache.cloudstack.framework.config.impl.ConfigDepotImpl;
|
||||
import org.apache.cloudstack.veeam.api.dto.ImageTransfer;
|
||||
import org.apache.cloudstack.veeam.utils.Mapper;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountService;
|
||||
import com.cloud.utils.UuidUtils;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class VeeamControlServiceImplTest {
|
||||
|
||||
private static final String KEY_ENABLED = "integration.veeam.control.enabled";
|
||||
private static final String KEY_PORT = "integration.veeam.control.port";
|
||||
private static final String KEY_USERNAME = "integration.veeam.control.api.username";
|
||||
private static final String KEY_PASSWORD = "integration.veeam.control.api.password";
|
||||
private static final String KEY_ALLOWED_CIDRS = "integration.veeam.control.allowed.client.cidrs";
|
||||
|
||||
private ConfigDepotImpl previousDepot;
|
||||
private final Map<String, String> globalValues = new HashMap<>();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
previousDepot = getConfigDepot();
|
||||
final ConfigDepotImpl depot = mock(ConfigDepotImpl.class);
|
||||
when(depot.getConfigStringValue(Mockito.anyString(), Mockito.any(), Mockito.any())).thenAnswer(invocation -> {
|
||||
final String key = invocation.getArgument(0);
|
||||
final Scope scope = invocation.getArgument(1);
|
||||
if (scope == Scope.Global) {
|
||||
return globalValues.get(key);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
setConfigDepot(depot);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
setConfigDepot(previousDepot);
|
||||
resetConfigKeyCache(VeeamControlService.Enabled);
|
||||
resetConfigKeyCache(VeeamControlService.Port);
|
||||
resetConfigKeyCache(VeeamControlService.Username);
|
||||
resetConfigKeyCache(VeeamControlService.Password);
|
||||
resetConfigKeyCache(VeeamControlService.AllowedClientCidrs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_parseImageTransfer() {
|
||||
public void testParseImageTransfer() {
|
||||
String data = "{\"active\":false,\"direction\":\"upload\",\"format\":\"cow\",\"inactivity_timeout\":3600,\"phase\":\"cancelled\",\"shallow\":false,\"transferred\":0,\"link\":[],\"disk\":{\"id\":\"dba4d72d-01de-4267-aa8e-305996b53599\"},\"image\":{},\"backup\":{\"creation_date\":0}}";
|
||||
Mapper mapper = new Mapper();
|
||||
try {
|
||||
ImageTransfer request = mapper.jsonMapper().readValue(data, ImageTransfer.class);
|
||||
mapper.jsonMapper().readValue(data, ImageTransfer.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllowedClientCidrsInternalSanitizesAndFiltersInvalidEntries() throws Exception {
|
||||
globalValues.put(KEY_ALLOWED_CIDRS, " 10.0.0.0/24,invalid-cidr, ,192.168.1.100/32 ");
|
||||
resetConfigKeyCache(VeeamControlService.AllowedClientCidrs);
|
||||
final VeeamControlServiceImpl service = new VeeamControlServiceImpl();
|
||||
|
||||
final List<String> cidrs = service.getAllowedClientCidrsInternal();
|
||||
|
||||
assertEquals(List.of("10.0.0.0/24", "192.168.1.100/32"), cidrs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllowedClientCidrsInternalReturnsEmptyListForBlankValue() throws Exception {
|
||||
globalValues.put(KEY_ALLOWED_CIDRS, " ");
|
||||
resetConfigKeyCache(VeeamControlService.AllowedClientCidrs);
|
||||
final VeeamControlServiceImpl service = new VeeamControlServiceImpl();
|
||||
|
||||
assertTrue(service.getAllowedClientCidrsInternal().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCredentials() throws Exception {
|
||||
globalValues.put(KEY_USERNAME, "veeam-user");
|
||||
globalValues.put(KEY_PASSWORD, "veeam-pass");
|
||||
resetConfigKeyCache(VeeamControlService.Username);
|
||||
resetConfigKeyCache(VeeamControlService.Password);
|
||||
final VeeamControlServiceImpl service = new VeeamControlServiceImpl();
|
||||
|
||||
assertTrue(service.validateCredentials("veeam-user", "veeam-pass"));
|
||||
assertFalse(service.validateCredentials("veeam-user", "wrong"));
|
||||
assertFalse(service.validateCredentials("wrong", "veeam-pass"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInstanceIdReturnsSystemAccountUuid() {
|
||||
final VeeamControlServiceImpl service = new VeeamControlServiceImpl();
|
||||
final AccountService accountService = mock(AccountService.class);
|
||||
final Account account = mock(Account.class);
|
||||
service.accountService = accountService;
|
||||
when(accountService.getSystemAccount()).thenReturn(account);
|
||||
when(account.getUuid()).thenReturn("system-account-uuid");
|
||||
|
||||
assertEquals("system-account-uuid", service.getInstanceId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHmacSecretUsesConfiguredInputs() throws Exception {
|
||||
globalValues.put(KEY_PORT, "8095");
|
||||
globalValues.put(KEY_USERNAME, "api-user");
|
||||
globalValues.put(KEY_PASSWORD, "api-pass");
|
||||
resetConfigKeyCache(VeeamControlService.Port);
|
||||
resetConfigKeyCache(VeeamControlService.Username);
|
||||
resetConfigKeyCache(VeeamControlService.Password);
|
||||
|
||||
final VeeamControlServiceImpl service = Mockito.spy(new VeeamControlServiceImpl());
|
||||
Mockito.doReturn("instance-uuid").when(service).getInstanceId();
|
||||
|
||||
final String expected = UuidUtils.nameUUIDFromBytes(
|
||||
"instance-uuid:8095:api-userapi-pass".getBytes(StandardCharsets.UTF_8)).toString();
|
||||
assertEquals(expected, service.getHmacSecret());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetConfigKeysContainsExpectedEntries() {
|
||||
final VeeamControlServiceImpl service = new VeeamControlServiceImpl();
|
||||
|
||||
final ConfigKey<?>[] keys = service.getConfigKeys();
|
||||
|
||||
assertEquals(10, keys.length);
|
||||
assertEquals(KEY_ENABLED, keys[0].key());
|
||||
}
|
||||
|
||||
private static ConfigDepotImpl getConfigDepot() throws Exception {
|
||||
final Field field = ConfigKey.class.getDeclaredField("s_depot");
|
||||
field.setAccessible(true);
|
||||
return (ConfigDepotImpl) field.get(null);
|
||||
}
|
||||
|
||||
private static void setConfigDepot(final ConfigDepotImpl depot) throws Exception {
|
||||
final Field field = ConfigKey.class.getDeclaredField("s_depot");
|
||||
field.setAccessible(true);
|
||||
field.set(null, depot);
|
||||
}
|
||||
|
||||
private static void resetConfigKeyCache(final ConfigKey<?> configKey) throws Exception {
|
||||
final Field valueField = ConfigKey.class.getDeclaredField("_value");
|
||||
valueField.setAccessible(true);
|
||||
valueField.set(configKey, null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
// 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.cloudstack.veeam;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
public class VeeamControlServletTest {
|
||||
|
||||
@Test
|
||||
public void testServiceHandlesRootRequestForGet() throws Exception {
|
||||
final VeeamControlServlet servlet = new VeeamControlServlet(Collections.emptyList());
|
||||
final HttpServletRequest request = new MockHttpServletRequest("GET", "/");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
servlet.service(request, response);
|
||||
|
||||
assertEquals(200, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("CloudStack Veeam Control Service"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServiceReturnsBadRequestForUnsupportedRootMethod() throws Exception {
|
||||
final VeeamControlServlet servlet = new VeeamControlServlet(Collections.emptyList());
|
||||
final HttpServletRequest request = new MockHttpServletRequest("PUT", "/");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
servlet.service(request, response);
|
||||
|
||||
assertEquals(400, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("Unsupported method for root"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServiceDelegatesToMatchingRouteHandler() throws Exception {
|
||||
final RouteHandler handler = mock(RouteHandler.class);
|
||||
final String path = "/api/path";
|
||||
final VeeamControlServlet servlet = new VeeamControlServlet(Collections.singletonList(handler));
|
||||
final HttpServletRequest request = new MockHttpServletRequest("GET", path);
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
when(handler.canHandle("GET", path)).thenReturn(true);
|
||||
|
||||
servlet.service(request, response);
|
||||
|
||||
verify(handler).handle(request, response, path, Negotiation.OutFormat.XML, servlet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServiceReturnsNotFoundWhenNoHandlerMatches() throws Exception {
|
||||
final RouteHandler handler = mock(RouteHandler.class);
|
||||
final String path = "/api/path";
|
||||
final VeeamControlServlet servlet = new VeeamControlServlet(Collections.singletonList(handler));
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("GET", path);
|
||||
request.setPathInfo(path);
|
||||
request.addHeader("Accept", "application/json");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
when(handler.canHandle("GET", path)).thenReturn(false);
|
||||
|
||||
servlet.service(request, response);
|
||||
|
||||
assertEquals(404, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("\"reason\":\"Not found\""));
|
||||
verify(handler).canHandle("GET", path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServiceConvertsHandlerErrorToFaultResponse() throws Exception {
|
||||
final RouteHandler handler = mock(RouteHandler.class);
|
||||
final String path = "/api/faultpath";
|
||||
final VeeamControlServlet servlet = new VeeamControlServlet(Collections.singletonList(handler));
|
||||
final HttpServletRequest request = new MockHttpServletRequest("GET", path);
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
when(handler.canHandle("GET", path)).thenReturn(true);
|
||||
doThrow(VeeamControlServlet.Error.unauthorized("denied")).when(handler)
|
||||
.handle(request, response, path, Negotiation.OutFormat.XML, servlet);
|
||||
|
||||
servlet.service(request, response);
|
||||
|
||||
assertEquals(401, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("denied"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodNotAllowedWritesAllowHeaderAndFault() throws Exception {
|
||||
final VeeamControlServlet servlet = new VeeamControlServlet(Collections.emptyList());
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
servlet.methodNotAllowed(response, "GET, POST", Negotiation.OutFormat.JSON);
|
||||
|
||||
assertEquals(405, response.getStatus());
|
||||
assertEquals("GET, POST", response.getHeader("Allow"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
// 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.cloudstack.veeam.adapter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.command.user.zone.ListZonesCmd;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.utils.Pair;
|
||||
|
||||
public class ApiAccessInterceptorTest {
|
||||
|
||||
private static final long BASE_USER_ID = 100L;
|
||||
private static final long BASE_ACCOUNT_ID = 200L;
|
||||
private static final long SERVICE_USER_ID = 300L;
|
||||
private static final long SERVICE_ACCOUNT_ID = 400L;
|
||||
|
||||
private final ApiAccessInterceptor interceptor = new ApiAccessInterceptor();
|
||||
private final AccountManager accountManager = mock(AccountManager.class);
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
interceptor.accountManager = accountManager;
|
||||
CallContext.unregisterAll();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
CallContext.unregisterAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokePassesThroughWhenTargetIsNull() throws Throwable {
|
||||
final MethodInvocation invocation = mock(MethodInvocation.class);
|
||||
when(invocation.getThis()).thenReturn(null);
|
||||
when(invocation.proceed()).thenReturn("ok");
|
||||
|
||||
final Object result = interceptor.invoke(invocation);
|
||||
|
||||
assertEquals("ok", result);
|
||||
verify(invocation).proceed();
|
||||
verifyNoInteractions(accountManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokePassesThroughWhenMethodHasNoApiAccessAnnotation() throws Throwable {
|
||||
final TestServerAdapter adapter = new TestServerAdapter(serviceUserAccount());
|
||||
final MethodInvocation invocation = mock(MethodInvocation.class);
|
||||
when(invocation.getThis()).thenReturn(adapter);
|
||||
when(invocation.getMethod()).thenReturn(TestServerAdapter.class.getMethod("noApiAccess"));
|
||||
when(invocation.proceed()).thenReturn("done");
|
||||
|
||||
final Object result = interceptor.invoke(invocation);
|
||||
|
||||
assertEquals("done", result);
|
||||
verify(invocation).proceed();
|
||||
verifyNoInteractions(accountManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeChecksApiAccessForDirectlyAnnotatedMethodAndRestoresCallContext() throws Throwable {
|
||||
registerBaseContext();
|
||||
|
||||
final TestServerAdapter adapter = new TestServerAdapter(serviceUserAccount());
|
||||
final MethodInvocation invocation = mock(MethodInvocation.class);
|
||||
when(invocation.getThis()).thenReturn(adapter);
|
||||
when(invocation.getMethod()).thenReturn(TestServerAdapter.class.getMethod("classAnnotated"));
|
||||
when(invocation.proceed()).thenAnswer(i -> {
|
||||
assertEquals(SERVICE_USER_ID, CallContext.current().getCallingUserId());
|
||||
assertEquals(SERVICE_ACCOUNT_ID, CallContext.current().getCallingAccountId());
|
||||
return "secured";
|
||||
});
|
||||
|
||||
final Object result = interceptor.invoke(invocation);
|
||||
|
||||
assertEquals("secured", result);
|
||||
verify(accountManager).checkApiAccess(adapter.getServiceAccount().second(),
|
||||
BaseCmd.getCommandNameByClass(ListZonesCmd.class));
|
||||
assertEquals(BASE_USER_ID, CallContext.current().getCallingUserId());
|
||||
assertEquals(BASE_ACCOUNT_ID, CallContext.current().getCallingAccountId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeFindsAnnotationOnImplementationWhenInterfaceMethodIsUnannotated() throws Throwable {
|
||||
registerBaseContext();
|
||||
|
||||
final TestServerAdapter adapter = new TestServerAdapter(serviceUserAccount());
|
||||
final Method interfaceMethod = ApiContract.class.getMethod("implAnnotatedThroughInterface");
|
||||
final MethodInvocation invocation = mock(MethodInvocation.class);
|
||||
when(invocation.getThis()).thenReturn(adapter);
|
||||
when(invocation.getMethod()).thenReturn(interfaceMethod);
|
||||
when(invocation.proceed()).thenReturn("ok");
|
||||
|
||||
final Object result = interceptor.invoke(invocation);
|
||||
|
||||
assertEquals("ok", result);
|
||||
verify(accountManager).checkApiAccess(adapter.getServiceAccount().second(),
|
||||
BaseCmd.getCommandNameByClass(ListZonesCmd.class));
|
||||
assertEquals(BASE_USER_ID, CallContext.current().getCallingUserId());
|
||||
assertEquals(BASE_ACCOUNT_ID, CallContext.current().getCallingAccountId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeUnregistersServiceContextWhenProceedThrows() throws Throwable {
|
||||
registerBaseContext();
|
||||
|
||||
final TestServerAdapter adapter = new TestServerAdapter(serviceUserAccount());
|
||||
final MethodInvocation invocation = mock(MethodInvocation.class);
|
||||
final RuntimeException expected = new RuntimeException("boom");
|
||||
when(invocation.getThis()).thenReturn(adapter);
|
||||
when(invocation.getMethod()).thenReturn(TestServerAdapter.class.getMethod("classAnnotated"));
|
||||
when(invocation.proceed()).thenThrow(expected);
|
||||
|
||||
try {
|
||||
interceptor.invoke(invocation);
|
||||
} catch (RuntimeException e) {
|
||||
assertSame(expected, e);
|
||||
}
|
||||
|
||||
verify(accountManager).checkApiAccess(adapter.getServiceAccount().second(),
|
||||
BaseCmd.getCommandNameByClass(ListZonesCmd.class));
|
||||
assertEquals(BASE_USER_ID, CallContext.current().getCallingUserId());
|
||||
assertEquals(BASE_ACCOUNT_ID, CallContext.current().getCallingAccountId());
|
||||
}
|
||||
|
||||
private static void registerBaseContext() {
|
||||
final User baseUser = mock(User.class);
|
||||
final Account baseAccount = mock(Account.class);
|
||||
when(baseUser.getId()).thenReturn(BASE_USER_ID);
|
||||
when(baseAccount.getId()).thenReturn(BASE_ACCOUNT_ID);
|
||||
CallContext.register(baseUser, baseAccount);
|
||||
}
|
||||
|
||||
private static Pair<User, Account> serviceUserAccount() {
|
||||
final User serviceUser = mock(User.class);
|
||||
final Account serviceAccount = mock(Account.class);
|
||||
when(serviceUser.getId()).thenReturn(SERVICE_USER_ID);
|
||||
when(serviceAccount.getId()).thenReturn(SERVICE_ACCOUNT_ID);
|
||||
return new Pair<>(serviceUser, serviceAccount);
|
||||
}
|
||||
|
||||
private interface ApiContract {
|
||||
String implAnnotatedThroughInterface();
|
||||
}
|
||||
|
||||
private static class TestServerAdapter extends ServerAdapter implements ApiContract {
|
||||
private final Pair<User, Account> serviceAccount;
|
||||
|
||||
private TestServerAdapter(final Pair<User, Account> serviceAccount) {
|
||||
this.serviceAccount = serviceAccount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<User, Account> getServiceAccount() {
|
||||
return serviceAccount;
|
||||
}
|
||||
|
||||
@ApiAccess(command = ListZonesCmd.class)
|
||||
public String classAnnotated() {
|
||||
return "classAnnotated";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ApiAccess(command = ListZonesCmd.class)
|
||||
public String implAnnotatedThroughInterface() {
|
||||
return "implAnnotatedThroughInterface";
|
||||
}
|
||||
|
||||
public String noApiAccess() {
|
||||
return "noApiAccess";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,972 @@
|
|||
// 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.cloudstack.veeam.adapter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.backup.BackupVO;
|
||||
import org.apache.cloudstack.backup.dao.BackupDao;
|
||||
import org.apache.cloudstack.backup.dao.ImageTransferDao;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.jobs.dao.AsyncJobDao;
|
||||
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.veeam.api.dto.DataCenter;
|
||||
import org.apache.cloudstack.veeam.api.dto.Disk;
|
||||
import org.apache.cloudstack.veeam.api.dto.Tag;
|
||||
import org.apache.cloudstack.veeam.api.dto.Vm;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import com.cloud.api.query.dao.AsyncJobJoinDao;
|
||||
import com.cloud.api.query.dao.DataCenterJoinDao;
|
||||
import com.cloud.api.query.dao.HostJoinDao;
|
||||
import com.cloud.api.query.dao.StoragePoolJoinDao;
|
||||
import com.cloud.api.query.dao.UserVmJoinDao;
|
||||
import com.cloud.api.query.dao.VolumeJoinDao;
|
||||
import com.cloud.api.query.vo.AsyncJobJoinVO;
|
||||
import com.cloud.api.query.vo.DataCenterJoinVO;
|
||||
import com.cloud.api.query.vo.HostJoinVO;
|
||||
import com.cloud.api.query.vo.StoragePoolJoinVO;
|
||||
import com.cloud.api.query.vo.VolumeJoinVO;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.dao.ClusterDao;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.domain.dao.DomainDao;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.network.NetworkModel;
|
||||
import com.cloud.network.Networks;
|
||||
import com.cloud.network.dao.NetworkDao;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.projects.Project;
|
||||
import com.cloud.projects.ProjectManager;
|
||||
import com.cloud.service.ServiceOfferingVO;
|
||||
import com.cloud.service.dao.ServiceOfferingDao;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountService;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.Ternary;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
import com.cloud.vm.VmDetailConstants;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ServerAdapterTest {
|
||||
|
||||
@InjectMocks
|
||||
ServerAdapter serverAdapter;
|
||||
|
||||
@Mock AccountService accountService;
|
||||
@Mock DataCenterDao dataCenterDao;
|
||||
@Mock DataCenterJoinDao dataCenterJoinDao;
|
||||
@Mock StoragePoolJoinDao storagePoolJoinDao;
|
||||
@Mock ClusterDao clusterDao;
|
||||
@Mock HostJoinDao hostJoinDao;
|
||||
@Mock NetworkDao networkDao;
|
||||
@Mock UserVmDao userVmDao;
|
||||
@Mock UserVmJoinDao userVmJoinDao;
|
||||
@Mock VolumeDao volumeDao;
|
||||
@Mock VolumeJoinDao volumeJoinDao;
|
||||
// kept minimal: only mocks used directly by tests
|
||||
@Mock com.cloud.storage.VolumeApiService volumeApiService;
|
||||
@Mock PrimaryDataStoreDao primaryDataStoreDao;
|
||||
@Mock ImageTransferDao imageTransferDao;
|
||||
@Mock ServiceOfferingDao serviceOfferingDao;
|
||||
@Mock VMTemplateDao templateDao;
|
||||
@Mock UserVmManager userVmManager;
|
||||
@Mock AsyncJobDao asyncJobDao;
|
||||
@Mock AsyncJobJoinDao asyncJobJoinDao;
|
||||
@Mock VMSnapshotDao vmSnapshotDao;
|
||||
@Mock BackupDao backupDao;
|
||||
@Mock NetworkModel networkModel;
|
||||
@Mock ProjectManager projectManager;
|
||||
@Mock DomainDao domainDao;
|
||||
|
||||
@Before
|
||||
public void setupCallContext() {
|
||||
CallContext.register(Mockito.mock(User.class), Mockito.mock(Account.class));
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanupCallContext() {
|
||||
CallContext.unregister();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetProvisionedSizeInGb_ExactlyOneGB() {
|
||||
long gb = 1024L * 1024L * 1024L;
|
||||
assertEquals(1L, ServerAdapter.getProvisionedSizeInGb(String.valueOf(gb)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProvisionedSizeInGb_MultipleGB() {
|
||||
long gb = 1024L * 1024L * 1024L;
|
||||
assertEquals(5L, ServerAdapter.getProvisionedSizeInGb(String.valueOf(5 * gb)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProvisionedSizeInGb_LessThanOneGB_RoundsUpToOne() {
|
||||
assertEquals(1L, ServerAdapter.getProvisionedSizeInGb("512"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProvisionedSizeInGb_NotExactGB_RoundsUp() {
|
||||
long gb = 1024L * 1024L * 1024L;
|
||||
assertEquals(2L, ServerAdapter.getProvisionedSizeInGb(String.valueOf(gb + gb / 2)));
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testGetProvisionedSizeInGb_InvalidString_Throws() {
|
||||
ServerAdapter.getProvisionedSizeInGb("not-a-number");
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testGetProvisionedSizeInGb_Zero_Throws() {
|
||||
ServerAdapter.getProvisionedSizeInGb("0");
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testGetProvisionedSizeInGb_Negative_Throws() {
|
||||
ServerAdapter.getProvisionedSizeInGb("-1073741824");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetDetailsForInstanceCreation_WithUserdata_AddsCpuMode() {
|
||||
ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
|
||||
when(offering.isCustomized()).thenReturn(false);
|
||||
|
||||
Map<String, String> result = ServerAdapter.getDetailsForInstanceCreation("#!/bin/bash", offering, null);
|
||||
|
||||
assertEquals("host-passthrough", result.get(VmDetailConstants.GUEST_CPU_MODE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDetailsForInstanceCreation_NoUserdata_NoCpuMode() {
|
||||
ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
|
||||
when(offering.isCustomized()).thenReturn(false);
|
||||
|
||||
Map<String, String> result = ServerAdapter.getDetailsForInstanceCreation(null, offering, null);
|
||||
|
||||
assertFalse(result.containsKey(VmDetailConstants.GUEST_CPU_MODE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDetailsForInstanceCreation_CustomizedOffering_AddsDetails() {
|
||||
ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
|
||||
when(offering.isCustomized()).thenReturn(true);
|
||||
when(offering.getCpu()).thenReturn(4);
|
||||
when(offering.getRamSize()).thenReturn(2048);
|
||||
when(offering.getSpeed()).thenReturn(null);
|
||||
|
||||
Map<String, String> result = ServerAdapter.getDetailsForInstanceCreation(null, offering, null);
|
||||
|
||||
assertEquals("4", result.get(VmDetailConstants.CPU_NUMBER));
|
||||
assertEquals("2048", result.get(VmDetailConstants.MEMORY));
|
||||
assertEquals("1000", result.get(VmDetailConstants.CPU_SPEED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDetailsForInstanceCreation_CustomizedOffering_WithSpeed_DoesNotAddDefaultCpuSpeed() {
|
||||
ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
|
||||
when(offering.isCustomized()).thenReturn(true);
|
||||
when(offering.getCpu()).thenReturn(2);
|
||||
when(offering.getRamSize()).thenReturn(1024);
|
||||
when(offering.getSpeed()).thenReturn(2000);
|
||||
|
||||
Map<String, String> result = ServerAdapter.getDetailsForInstanceCreation(null, offering, null);
|
||||
|
||||
assertFalse(result.containsKey(VmDetailConstants.CPU_SPEED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDetailsForInstanceCreation_SkipsBiosAndUefiKeys() {
|
||||
ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
|
||||
when(offering.isCustomized()).thenReturn(false);
|
||||
|
||||
Map<String, String> existingDetails = new HashMap<>();
|
||||
existingDetails.put("BIOS", "bios_value");
|
||||
existingDetails.put("UEFI", "uefi_value");
|
||||
existingDetails.put("custom_key", "custom_value");
|
||||
|
||||
Map<String, String> result = ServerAdapter.getDetailsForInstanceCreation(null, offering, existingDetails);
|
||||
|
||||
assertFalse(result.containsKey("BIOS"));
|
||||
assertFalse(result.containsKey("UEFI"));
|
||||
assertEquals("custom_value", result.get("custom_key"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDetailsForInstanceCreation_PreservesExistingCpuSpeed() {
|
||||
ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
|
||||
when(offering.isCustomized()).thenReturn(true);
|
||||
when(offering.getCpu()).thenReturn(2);
|
||||
when(offering.getRamSize()).thenReturn(1024);
|
||||
when(offering.getSpeed()).thenReturn(null);
|
||||
|
||||
Map<String, String> existingDetails = new HashMap<>();
|
||||
existingDetails.put(VmDetailConstants.CPU_SPEED, "3000");
|
||||
|
||||
Map<String, String> result = ServerAdapter.getDetailsForInstanceCreation(null, offering, existingDetails);
|
||||
|
||||
assertEquals("3000", result.get(VmDetailConstants.CPU_SPEED));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetDummyTags_ContainsRootTag() {
|
||||
Map<String, Tag> tags = ServerAdapter.getDummyTags();
|
||||
assertNotNull(tags);
|
||||
assertFalse(tags.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetTemplateForInstanceCreation_NullUuid_ReturnsNull() {
|
||||
assertNull(serverAdapter.getTemplateForInstanceCreation(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTemplateForInstanceCreation_BlankUuid_ReturnsNull() {
|
||||
assertNull(serverAdapter.getTemplateForInstanceCreation(" "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTemplateForInstanceCreation_TemplateNotFound_ReturnsNull() {
|
||||
when(templateDao.findByUuid("missing-uuid")).thenReturn(null);
|
||||
assertNull(serverAdapter.getTemplateForInstanceCreation("missing-uuid"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTemplateForInstanceCreation_TemplateFound_ReturnsTemplate() {
|
||||
VMTemplateVO template = mock(VMTemplateVO.class);
|
||||
when(templateDao.findByUuid("valid-uuid")).thenReturn(template);
|
||||
assertEquals(template, serverAdapter.getTemplateForInstanceCreation("valid-uuid"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetZoneById_NullId_ReturnsNull() {
|
||||
assertNull(serverAdapter.getZoneById(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetZoneById_ReturnsVoFromDao() {
|
||||
DataCenterJoinVO vo = mock(DataCenterJoinVO.class);
|
||||
when(dataCenterJoinDao.findById(1L)).thenReturn(vo);
|
||||
assertEquals(vo, serverAdapter.getZoneById(1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHostById_NullId_ReturnsNull() {
|
||||
assertNull(serverAdapter.getHostById(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHostById_ReturnsVoFromDao() {
|
||||
HostJoinVO vo = mock(HostJoinVO.class);
|
||||
when(hostJoinDao.findById(2L)).thenReturn(vo);
|
||||
assertEquals(vo, serverAdapter.getHostById(2L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetVolumeById_NullId_ReturnsNull() {
|
||||
assertNull(serverAdapter.getVolumeById(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetVolumeById_ReturnsVoFromDao() {
|
||||
VolumeJoinVO vo = mock(VolumeJoinVO.class);
|
||||
when(volumeJoinDao.findById(3L)).thenReturn(vo);
|
||||
assertEquals(vo, serverAdapter.getVolumeById(3L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNetworkById_NullId_ReturnsNull() {
|
||||
assertNull(serverAdapter.getNetworkById(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNetworkById_ReturnsVoFromDao() {
|
||||
NetworkVO vo = mock(NetworkVO.class);
|
||||
when(networkDao.findById(4L)).thenReturn(vo);
|
||||
assertEquals(vo, serverAdapter.getNetworkById(4L));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testWaitForJobCompletion_JobNotFound_Returns() {
|
||||
when(asyncJobDao.findById(99L)).thenReturn(null);
|
||||
serverAdapter.waitForJobCompletion(99L);
|
||||
verify(asyncJobDao).findById(99L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWaitForJobCompletion_JobAlreadySucceeded_Returns() {
|
||||
AsyncJobVO job = mock(AsyncJobVO.class);
|
||||
when(job.getStatus()).thenReturn(AsyncJobVO.Status.SUCCEEDED);
|
||||
when(asyncJobDao.findById(1L)).thenReturn(job);
|
||||
serverAdapter.waitForJobCompletion(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWaitForJobCompletion_JobAlreadyFailed_Returns() {
|
||||
AsyncJobVO job = mock(AsyncJobVO.class);
|
||||
when(job.getStatus()).thenReturn(AsyncJobVO.Status.FAILED);
|
||||
when(asyncJobDao.findById(2L)).thenReturn(job);
|
||||
serverAdapter.waitForJobCompletion(2L);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testWaitForJobCompletion_NullJobJoinVO_Returns() {
|
||||
AsyncJobJoinVO job = null;
|
||||
serverAdapter.waitForJobCompletion(job);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWaitForJobCompletion_CompletedJobJoinVO_DelegatesById() {
|
||||
AsyncJobJoinVO jobVO = mock(AsyncJobJoinVO.class);
|
||||
when(jobVO.getStatus()).thenReturn(AsyncJobVO.Status.SUCCEEDED.ordinal());
|
||||
when(jobVO.getId()).thenReturn(5L);
|
||||
|
||||
AsyncJobVO job = mock(AsyncJobVO.class);
|
||||
when(job.getStatus()).thenReturn(AsyncJobVO.Status.SUCCEEDED);
|
||||
when(asyncJobDao.findById(5L)).thenReturn(job);
|
||||
|
||||
serverAdapter.waitForJobCompletion(jobVO);
|
||||
|
||||
verify(asyncJobDao).findById(5L);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetOwnerDetailsForInstanceCreation_NullAccount_ReturnsAllNulls() {
|
||||
Ternary<Long, String, Long> result = serverAdapter.getOwnerDetailsForInstanceCreation(null);
|
||||
assertNull(result.first());
|
||||
assertNull(result.second());
|
||||
assertNull(result.third());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOwnerDetailsForInstanceCreation_NormalAccount_ReturnsDomainAndName() {
|
||||
Account account = mock(Account.class);
|
||||
when(account.getType()).thenReturn(Account.Type.NORMAL);
|
||||
when(account.getDomainId()).thenReturn(2L);
|
||||
when(account.getAccountName()).thenReturn("myaccount");
|
||||
|
||||
Ternary<Long, String, Long> result = serverAdapter.getOwnerDetailsForInstanceCreation(account);
|
||||
|
||||
assertEquals(Long.valueOf(2L), result.first());
|
||||
assertEquals("myaccount", result.second());
|
||||
assertNull(result.third());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOwnerDetailsForInstanceCreation_ProjectAccount_ProjectNotFound_ReturnsAllNulls() {
|
||||
Account account = mock(Account.class);
|
||||
when(account.getType()).thenReturn(Account.Type.PROJECT);
|
||||
when(account.getId()).thenReturn(10L);
|
||||
when(projectManager.findByProjectAccountId(10L)).thenReturn(null);
|
||||
|
||||
Ternary<Long, String, Long> result = serverAdapter.getOwnerDetailsForInstanceCreation(account);
|
||||
|
||||
assertNull(result.first());
|
||||
assertNull(result.second());
|
||||
assertNull(result.third());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOwnerDetailsForInstanceCreation_ProjectAccount_ProjectFound_ReturnsProjectId() {
|
||||
Account account = mock(Account.class);
|
||||
when(account.getType()).thenReturn(Account.Type.PROJECT);
|
||||
when(account.getId()).thenReturn(10L);
|
||||
when(account.getDomainId()).thenReturn(1L);
|
||||
|
||||
Project project = mock(Project.class);
|
||||
when(project.getId()).thenReturn(5L);
|
||||
when(projectManager.findByProjectAccountId(10L)).thenReturn(project);
|
||||
|
||||
Ternary<Long, String, Long> result = serverAdapter.getOwnerDetailsForInstanceCreation(account);
|
||||
|
||||
assertEquals(Long.valueOf(1L), result.first());
|
||||
assertNull(result.second());
|
||||
assertEquals(Long.valueOf(5L), result.third());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetServiceOfferingFromRequest_BlankUuid_ReturnsNull() {
|
||||
assertNull(serverAdapter.getServiceOfferingFromRequest(null, null, "", 2, 1024));
|
||||
assertNull(serverAdapter.getServiceOfferingFromRequest(null, null, null, 2, 1024));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetServiceOfferingFromRequest_OfferingNotFound_ReturnsNull() {
|
||||
when(serviceOfferingDao.findByUuid("uuid1")).thenReturn(null);
|
||||
assertNull(serverAdapter.getServiceOfferingFromRequest(null, null, "uuid1", 2, 1024));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetServiceOfferingFromRequest_AccessDenied_ReturnsNull() {
|
||||
ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
|
||||
Account account = mock(Account.class);
|
||||
when(serviceOfferingDao.findByUuid("uuid2")).thenReturn(offering);
|
||||
doThrow(new PermissionDeniedException("denied"))
|
||||
.when(accountService).checkAccess(eq(account), eq(offering), any());
|
||||
|
||||
assertNull(serverAdapter.getServiceOfferingFromRequest(null, account, "uuid2", 2, 1024));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetServiceOfferingFromRequest_NotCustomized_CpuMemoryMismatch_ReturnsNull() {
|
||||
ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
|
||||
Account account = mock(Account.class);
|
||||
when(serviceOfferingDao.findByUuid("uuid3")).thenReturn(offering);
|
||||
doNothing().when(accountService).checkAccess(eq(account), eq(offering), any());
|
||||
when(offering.isCustomized()).thenReturn(false);
|
||||
when(offering.getCpu()).thenReturn(4);
|
||||
|
||||
assertNull(serverAdapter.getServiceOfferingFromRequest(null, account, "uuid3", 2, 1024));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetServiceOfferingFromRequest_NotCustomized_CpuMemoryMatch_ReturnsOffering() {
|
||||
ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
|
||||
Account account = mock(Account.class);
|
||||
when(serviceOfferingDao.findByUuid("uuid4")).thenReturn(offering);
|
||||
doNothing().when(accountService).checkAccess(eq(account), eq(offering), any());
|
||||
when(offering.isCustomized()).thenReturn(false);
|
||||
when(offering.getCpu()).thenReturn(2);
|
||||
when(offering.getRamSize()).thenReturn(1024);
|
||||
|
||||
assertEquals(offering, serverAdapter.getServiceOfferingFromRequest(null, account, "uuid4", 2, 1024));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetServiceOfferingFromRequest_Customized_ValidParams_SetsAndReturnsOffering() {
|
||||
ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
|
||||
Account account = mock(Account.class);
|
||||
when(serviceOfferingDao.findByUuid("uuid5")).thenReturn(offering);
|
||||
doNothing().when(accountService).checkAccess(eq(account), eq(offering), any());
|
||||
when(offering.isCustomized()).thenReturn(true);
|
||||
doNothing().when(userVmManager).validateCustomParameters(eq(offering), any());
|
||||
|
||||
ServiceOfferingVO result = serverAdapter.getServiceOfferingFromRequest(null, account, "uuid5", 2, 1024);
|
||||
|
||||
assertEquals(offering, result);
|
||||
verify(offering).setCpu(2);
|
||||
verify(offering).setRamSize(1024);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetServiceOfferingFromRequest_Customized_InvalidParams_ReturnsNull() {
|
||||
ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
|
||||
Account account = mock(Account.class);
|
||||
when(serviceOfferingDao.findByUuid("uuid6")).thenReturn(offering);
|
||||
doNothing().when(accountService).checkAccess(eq(account), eq(offering), any());
|
||||
when(offering.isCustomized()).thenReturn(true);
|
||||
doThrow(new InvalidParameterValueException("invalid"))
|
||||
.when(userVmManager).validateCustomParameters(eq(offering), any());
|
||||
|
||||
assertNull(serverAdapter.getServiceOfferingFromRequest(null, account, "uuid6", 2, 1024));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testAccountCannotAccessNetwork_CanAccess_ReturnsFalse() {
|
||||
NetworkVO network = mock(NetworkVO.class);
|
||||
Account account = mock(Account.class);
|
||||
when(accountService.getActiveAccountById(1L)).thenReturn(account);
|
||||
doNothing().when(networkModel).checkNetworkPermissions(account, network);
|
||||
|
||||
assertFalse(serverAdapter.accountCannotAccessNetwork(network, 1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccountCannotAccessNetwork_CannotAccess_ReturnsTrue() {
|
||||
NetworkVO network = mock(NetworkVO.class);
|
||||
Account account = mock(Account.class);
|
||||
when(accountService.getActiveAccountById(1L)).thenReturn(account);
|
||||
doThrow(new CloudRuntimeException("Access denied"))
|
||||
.when(networkModel).checkNetworkPermissions(account, network);
|
||||
|
||||
assertTrue(serverAdapter.accountCannotAccessNetwork(network, 1L));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testValidateInstanceStorage_SupportedStorageType_NoException() {
|
||||
UserVmVO vm = mock(UserVmVO.class);
|
||||
when(vm.getId()).thenReturn(1L);
|
||||
VolumeVO vol = mock(VolumeVO.class);
|
||||
when(vol.getPoolId()).thenReturn(10L);
|
||||
when(volumeDao.findUsableVolumesForInstance(1L)).thenReturn(List.of(vol));
|
||||
StoragePoolVO pool = mock(StoragePoolVO.class);
|
||||
when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(primaryDataStoreDao.listByIds(anyList())).thenReturn(List.of(pool));
|
||||
|
||||
serverAdapter.validateInstanceStorage(vm);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testValidateInstanceStorage_UnsupportedStorageType_Throws() {
|
||||
UserVmVO vm = mock(UserVmVO.class);
|
||||
when(vm.getId()).thenReturn(2L);
|
||||
VolumeVO vol = mock(VolumeVO.class);
|
||||
when(vol.getPoolId()).thenReturn(20L);
|
||||
when(volumeDao.findUsableVolumesForInstance(2L)).thenReturn(List.of(vol));
|
||||
StoragePoolVO pool = mock(StoragePoolVO.class);
|
||||
when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.RBD);
|
||||
when(pool.getName()).thenReturn("ceph-pool");
|
||||
when(primaryDataStoreDao.listByIds(anyList())).thenReturn(List.of(pool));
|
||||
|
||||
serverAdapter.validateInstanceStorage(vm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateInstanceStorage_NoVolumes_NoException() {
|
||||
UserVmVO vm = mock(UserVmVO.class);
|
||||
when(vm.getId()).thenReturn(3L);
|
||||
when(volumeDao.findUsableVolumesForInstance(3L)).thenReturn(Collections.emptyList());
|
||||
when(primaryDataStoreDao.listByIds(anyList())).thenReturn(Collections.emptyList());
|
||||
|
||||
serverAdapter.validateInstanceStorage(vm);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetBackupDisks_NullVolumeInfos_ReturnsEmptyList() {
|
||||
BackupVO backup = mock(BackupVO.class);
|
||||
when(backup.getBackedUpVolumes()).thenReturn(null);
|
||||
|
||||
assertTrue(serverAdapter.getBackupDisks(backup).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackupDisks_EmptyVolumeInfos_ReturnsEmptyList() {
|
||||
BackupVO backup = mock(BackupVO.class);
|
||||
when(backup.getBackedUpVolumes()).thenReturn(Collections.emptyList());
|
||||
|
||||
assertTrue(serverAdapter.getBackupDisks(backup).isEmpty());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetResourceOwnerFiltersWithDomainIds_NullDomainPath_ReturnsNullDomainIds() {
|
||||
ServerAdapter spyAdapter = spy(serverAdapter);
|
||||
doReturn(new Pair<>(List.of(1L, 2L), (String) null)).when(spyAdapter).getResourceOwnerFilters();
|
||||
|
||||
Pair<List<Long>, List<Long>> result = spyAdapter.getResourceOwnerFiltersWithDomainIds();
|
||||
|
||||
assertEquals(List.of(1L, 2L), result.first());
|
||||
assertNull(result.second());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceOwnerFiltersWithDomainIds_WithDomainPath_ReturnsDomainIds() {
|
||||
ServerAdapter spyAdapter = spy(serverAdapter);
|
||||
doReturn(new Pair<>(List.of(1L), "ROOT/subdomain")).when(spyAdapter).getResourceOwnerFilters();
|
||||
when(domainDao.getDomainChildrenIds("ROOT/subdomain")).thenReturn(List.of(10L, 11L));
|
||||
|
||||
Pair<List<Long>, List<Long>> result = spyAdapter.getResourceOwnerFiltersWithDomainIds();
|
||||
|
||||
assertEquals(List.of(10L, 11L), result.second());
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testGetDataCenter_NotFound_Throws() {
|
||||
when(dataCenterJoinDao.findByUuid("dc-uuid")).thenReturn(null);
|
||||
serverAdapter.getDataCenter("dc-uuid");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDataCenter_Found_ReturnsDataCenter() {
|
||||
DataCenterJoinVO vo = mock(DataCenterJoinVO.class);
|
||||
when(dataCenterJoinDao.findByUuid("dc-uuid")).thenReturn(vo);
|
||||
|
||||
DataCenter result = serverAdapter.getDataCenter("dc-uuid");
|
||||
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testListAllDataCenters_ReturnsConvertedList() {
|
||||
DataCenterJoinVO vo = mock(DataCenterJoinVO.class);
|
||||
when(dataCenterJoinDao.listAll(any())).thenReturn(List.of(vo));
|
||||
|
||||
List<DataCenter> result = serverAdapter.listAllDataCenters(0L, 10L);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(1, result.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListAllDataCenters_EmptyList_ReturnsEmpty() {
|
||||
when(dataCenterJoinDao.listAll(any())).thenReturn(Collections.emptyList());
|
||||
|
||||
assertTrue(serverAdapter.listAllDataCenters(0L, 10L).isEmpty());
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testListStorageDomainsByDcId_DataCenterNotFound_Throws() {
|
||||
when(dataCenterDao.findByUuid("dc-uuid")).thenReturn(null);
|
||||
serverAdapter.listStorageDomainsByDcId("dc-uuid", 0L, 10L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListStorageDomainsByDcId_Found_ReturnsList() {
|
||||
DataCenterVO dcVO = mock(DataCenterVO.class);
|
||||
when(dcVO.getId()).thenReturn(1L);
|
||||
when(dataCenterDao.findByUuid("dc-uuid")).thenReturn(dcVO);
|
||||
StoragePoolJoinVO poolVO = mock(StoragePoolJoinVO.class);
|
||||
when(storagePoolJoinDao.listByZoneAndType(eq(1L), any(), any())).thenReturn(List.of(poolVO));
|
||||
|
||||
assertNotNull(serverAdapter.listStorageDomainsByDcId("dc-uuid", 0L, 10L));
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testListNetworksByDcId_DataCenterNotFound_Throws() {
|
||||
when(dataCenterJoinDao.findByUuid("dc-uuid")).thenReturn(null);
|
||||
serverAdapter.listNetworksByDcId("dc-uuid", 0L, 10L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListNetworksByDcId_Found_ReturnsEmptyListWhenNoNetworks() {
|
||||
DataCenterJoinVO dcVO = mock(DataCenterJoinVO.class);
|
||||
when(dcVO.getId()).thenReturn(1L);
|
||||
when(dataCenterJoinDao.findByUuid("dc-uuid")).thenReturn(dcVO);
|
||||
when(networkDao.listByZoneAndTrafficType(eq(1L), eq(Networks.TrafficType.Guest), any()))
|
||||
.thenReturn(Collections.emptyList());
|
||||
assertTrue(serverAdapter.listNetworksByDcId("dc-uuid", 0L, 10L).isEmpty());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testListAllClusters_ReturnsEmptyListWhenNoClusters() {
|
||||
when(clusterDao.listByHypervisorType(any(), any())).thenReturn(Collections.emptyList());
|
||||
assertTrue(serverAdapter.listAllClusters(0L, 10L).isEmpty());
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testGetCluster_NotFound_Throws() {
|
||||
when(clusterDao.findByUuid("cl-uuid")).thenReturn(null);
|
||||
serverAdapter.getCluster("cl-uuid");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testListAllHosts_ReturnsList() {
|
||||
HostJoinVO hostVO = mock(HostJoinVO.class);
|
||||
when(hostJoinDao.listRoutingHostsByHypervisor(any(), any())).thenReturn(List.of(hostVO));
|
||||
|
||||
assertEquals(1, serverAdapter.listAllHosts(0L, 10L).size());
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testGetHost_NotFound_Throws() {
|
||||
when(hostJoinDao.findByUuid("host-uuid")).thenReturn(null);
|
||||
serverAdapter.getHost("host-uuid");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHost_Found_ReturnsHost() {
|
||||
HostJoinVO vo = mock(HostJoinVO.class);
|
||||
when(hostJoinDao.findByUuid("host-uuid")).thenReturn(vo);
|
||||
|
||||
assertNotNull(serverAdapter.getHost("host-uuid"));
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testGetVnicProfile_NotFound_Throws() {
|
||||
when(networkDao.findByUuid("net-uuid")).thenReturn(null);
|
||||
serverAdapter.getVnicProfile("net-uuid");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testGetInstance_NotFound_Throws() {
|
||||
when(userVmJoinDao.findByUuid("vm-uuid")).thenReturn(null);
|
||||
serverAdapter.getInstance("vm-uuid", false, false, false, false);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testDeleteInstance_VmNotFound_Throws() {
|
||||
when(userVmDao.findByUuid("vm-uuid")).thenReturn(null);
|
||||
serverAdapter.deleteInstance("vm-uuid", false);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testStartInstance_VmNotFound_Throws() {
|
||||
when(userVmDao.findByUuid("vm-uuid")).thenReturn(null);
|
||||
serverAdapter.startInstance("vm-uuid", false);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testStopInstance_VmNotFound_Throws() {
|
||||
when(userVmDao.findByUuid("vm-uuid")).thenReturn(null);
|
||||
serverAdapter.stopInstance("vm-uuid", false);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testShutdownInstance_VmNotFound_Throws() {
|
||||
when(userVmDao.findByUuid("vm-uuid")).thenReturn(null);
|
||||
serverAdapter.shutdownInstance("vm-uuid", false);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testGetDisk_NotFound_Throws() {
|
||||
when(volumeDao.findByUuid("vol-uuid")).thenReturn(null);
|
||||
serverAdapter.getDisk("vol-uuid");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testDeleteDisk_NotFound_Throws() {
|
||||
when(volumeDao.findByUuid("vol-uuid")).thenReturn(null);
|
||||
serverAdapter.deleteDisk("vol-uuid");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteDisk_Found_DeletesVolume() {
|
||||
VolumeVO vo = mock(VolumeVO.class);
|
||||
when(vo.getId()).thenReturn(10L);
|
||||
when(volumeDao.findByUuid("vol-uuid")).thenReturn(vo);
|
||||
Account sysAccount = mock(Account.class);
|
||||
when(accountService.getSystemAccount()).thenReturn(sysAccount);
|
||||
|
||||
serverAdapter.deleteDisk("vol-uuid");
|
||||
|
||||
verify(volumeApiService).deleteVolume(10L, sysAccount);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testCopyDisk_AlwaysThrows() {
|
||||
serverAdapter.copyDisk("any-uuid");
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testReduceDisk_AlwaysThrows() {
|
||||
serverAdapter.reduceDisk("any-uuid");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testListNicsByInstanceUuid_VmNotFound_Throws() {
|
||||
when(userVmDao.findByUuid("vm-uuid")).thenReturn(null);
|
||||
serverAdapter.listNicsByInstanceUuid("vm-uuid");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testListDiskAttachmentsByInstanceUuid_VmNotFound_Throws() {
|
||||
when(userVmDao.findByUuid("vm-uuid")).thenReturn(null);
|
||||
serverAdapter.listDiskAttachmentsByInstanceUuid("vm-uuid");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testListSnapshotsByInstanceUuid_VmNotFound_Throws() {
|
||||
when(userVmDao.findByUuid("vm-uuid")).thenReturn(null);
|
||||
serverAdapter.listSnapshotsByInstanceUuid("vm-uuid");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListSnapshotsByInstanceUuid_Found_ReturnsEmptyList() {
|
||||
UserVmVO vmVO = mock(UserVmVO.class);
|
||||
when(vmVO.getId()).thenReturn(1L);
|
||||
when(vmVO.getUuid()).thenReturn("vm-uuid");
|
||||
when(userVmDao.findByUuid("vm-uuid")).thenReturn(vmVO);
|
||||
when(vmSnapshotDao.findByVm(1L)).thenReturn(Collections.emptyList());
|
||||
|
||||
assertTrue(serverAdapter.listSnapshotsByInstanceUuid("vm-uuid").isEmpty());
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testGetSnapshot_NotFound_Throws() {
|
||||
when(vmSnapshotDao.findByUuid("snap-uuid")).thenReturn(null);
|
||||
serverAdapter.getSnapshot("snap-uuid");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testDeleteSnapshot_NotFound_Throws() {
|
||||
when(vmSnapshotDao.findByUuid("snap-uuid")).thenReturn(null);
|
||||
serverAdapter.deleteSnapshot("snap-uuid", false);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testRevertInstanceToSnapshot_NotFound_Throws() {
|
||||
when(vmSnapshotDao.findByUuid("snap-uuid")).thenReturn(null);
|
||||
serverAdapter.revertInstanceToSnapshot("snap-uuid", false);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testListBackupsByInstanceUuid_VmNotFound_Throws() {
|
||||
when(userVmDao.findByUuid("vm-uuid")).thenReturn(null);
|
||||
serverAdapter.listBackupsByInstanceUuid("vm-uuid");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListBackupsByInstanceUuid_Found_ReturnsEmptyList() {
|
||||
UserVmVO vmVO = mock(UserVmVO.class);
|
||||
when(vmVO.getId()).thenReturn(1L);
|
||||
when(userVmDao.findByUuid("vm-uuid")).thenReturn(vmVO);
|
||||
when(backupDao.searchByVmIds(anyList())).thenReturn(Collections.emptyList());
|
||||
|
||||
assertTrue(serverAdapter.listBackupsByInstanceUuid("vm-uuid").isEmpty());
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testGetBackup_NotFound_Throws() {
|
||||
when(backupDao.findByUuidIncludingRemoved("backup-uuid")).thenReturn(null);
|
||||
serverAdapter.getBackup("backup-uuid");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testFinalizeBackup_VmNotFound_Throws() {
|
||||
when(userVmDao.findByUuid("vm-uuid")).thenReturn(null);
|
||||
serverAdapter.finalizeBackup("vm-uuid", "backup-uuid");
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testFinalizeBackup_BackupNotFound_Throws() {
|
||||
UserVmVO vmVO = mock(UserVmVO.class);
|
||||
when(userVmDao.findByUuid("vm-uuid")).thenReturn(vmVO);
|
||||
when(backupDao.findByUuid("backup-uuid")).thenReturn(null);
|
||||
serverAdapter.finalizeBackup("vm-uuid", "backup-uuid");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testListCheckpointsByInstanceUuid_VmNotFound_Throws() {
|
||||
when(userVmDao.findByUuid("vm-uuid")).thenReturn(null);
|
||||
serverAdapter.listCheckpointsByInstanceUuid("vm-uuid");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testDeleteCheckpoint_VmNotFound_Throws() {
|
||||
when(userVmDao.findByUuid("vm-uuid")).thenReturn(null);
|
||||
serverAdapter.deleteCheckpoint("vm-uuid", "chk-001");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testGetJob_NotFound_Throws() {
|
||||
when(asyncJobJoinDao.findByUuidIncludingRemoved("job-uuid")).thenReturn(null);
|
||||
serverAdapter.getJob("job-uuid");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testGetImageTransfer_NotFound_Throws() {
|
||||
when(imageTransferDao.findByUuidIncludingRemoved("transfer-uuid")).thenReturn(null);
|
||||
serverAdapter.getImageTransfer("transfer-uuid");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testCancelImageTransfer_NotFound_Throws() {
|
||||
when(imageTransferDao.findByUuid("transfer-uuid")).thenReturn(null);
|
||||
serverAdapter.cancelImageTransfer("transfer-uuid");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testFinalizeImageTransfer_NotFound_Throws() {
|
||||
when(imageTransferDao.findByUuid("transfer-uuid")).thenReturn(null);
|
||||
serverAdapter.finalizeImageTransfer("transfer-uuid");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testUpdateInstance_VmNotFound_Throws() {
|
||||
when(userVmJoinDao.findByUuid("vm-uuid")).thenReturn(null);
|
||||
serverAdapter.updateInstance("vm-uuid", new Vm());
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testUpdateDisk_NotFound_Throws() {
|
||||
when(volumeDao.findByUuid("vol-uuid")).thenReturn(null);
|
||||
serverAdapter.updateDisk("vol-uuid", new Disk());
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testListDisksByBackupUuid_AlwaysThrows() {
|
||||
serverAdapter.listDisksByBackupUuid("backup-uuid");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// 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.cloudstack.veeam.api;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.utils.Pair;
|
||||
import org.apache.cloudstack.veeam.VeeamControlService;
|
||||
import org.apache.cloudstack.veeam.api.dto.Api;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ApiRouteHandlerTest extends RouteHandlerTestSupport {
|
||||
|
||||
@Test
|
||||
public void testCanHandleAndHandleRootApiRequest() throws Exception {
|
||||
final ApiRouteHandler handler = new ApiRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
handler.veeamControlService = mock(VeeamControlService.class);
|
||||
|
||||
final User user = mock(User.class);
|
||||
when(user.getUuid()).thenReturn("user-1");
|
||||
when(handler.serverAdapter.getServiceAccount()).thenReturn(new Pair<>(user, mock(Account.class)));
|
||||
when(handler.veeamControlService.getInstanceId()).thenReturn("instance-1");
|
||||
|
||||
final ResponseCapture response = newResponse();
|
||||
handler.handle(newRequest("GET"), response.response, "/api", Negotiation.OutFormat.JSON, newServlet());
|
||||
|
||||
assertTrue(handler.canHandle("GET", "/api?x=1"));
|
||||
verify(response.response).setStatus(200);
|
||||
assertContains(response.body(), "\"instance_id\":\"instance-1\"");
|
||||
assertContains(response.body(), "\"authenticated_user\"");
|
||||
assertContains(response.body(), "clusters/search");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateApiObjectBuildsLinksAndUserReferences() {
|
||||
final ApiRouteHandler handler = new ApiRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
handler.veeamControlService = mock(VeeamControlService.class);
|
||||
|
||||
final User user = mock(User.class);
|
||||
when(user.getUuid()).thenReturn("service-user");
|
||||
when(handler.serverAdapter.getServiceAccount()).thenReturn(new Pair<>(user, mock(Account.class)));
|
||||
when(handler.veeamControlService.getInstanceId()).thenReturn("instance-2");
|
||||
|
||||
final Api api = handler.createApiObject("/ctx/api");
|
||||
|
||||
assertNotNull(api.getLink());
|
||||
assertTrue(!api.getLink().isEmpty());
|
||||
assertEquals("instance-2", api.getProductInfo().getInstanceId());
|
||||
assertEquals("service-user", api.getAuthenticatedUser().getId());
|
||||
assertEquals("service-user", api.getEffectiveUser().getId());
|
||||
assertNotNull(api.getTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleUnknownPathReturnsNotFound() throws Exception {
|
||||
final ApiRouteHandler handler = new ApiRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
handler.veeamControlService = mock(VeeamControlService.class);
|
||||
|
||||
final ResponseCapture response = newResponse();
|
||||
handler.handle(newRequest("GET"), response.response, "/api/unknown", Negotiation.OutFormat.JSON, newServlet());
|
||||
|
||||
verify(response.response).setStatus(404);
|
||||
assertContains(response.body(), "\"reason\":\"Not found\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
// 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.cloudstack.veeam.api;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import org.apache.cloudstack.veeam.api.dto.Cluster;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ClustersRouteHandlerTest extends RouteHandlerTestSupport {
|
||||
|
||||
@Test
|
||||
public void testHandleGetListReturnsNamedClusterList() throws Exception {
|
||||
final ClustersRouteHandler handler = new ClustersRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listAllClusters(null, 25L)).thenReturn(List.of(withId(new Cluster(), "cl-1")));
|
||||
|
||||
final ResponseCapture response = newResponse();
|
||||
handler.handle(newRequest("GET", Map.of("max", "25"), null, null), response.response,
|
||||
"/api/clusters", Negotiation.OutFormat.JSON, newServlet());
|
||||
|
||||
verify(handler.serverAdapter).listAllClusters(null, 25L);
|
||||
verify(response.response).setStatus(200);
|
||||
assertContains(response.body(), "\"cluster\":[");
|
||||
assertContains(response.body(), "\"id\":\"cl-1\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleGetByIdReturnsClusterAndMissingClusterIsNotFound() throws Exception {
|
||||
final ClustersRouteHandler handler = new ClustersRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.getCluster("cl-1")).thenReturn(withId(new Cluster(), "cl-1"));
|
||||
when(handler.serverAdapter.getCluster("missing")).thenThrow(new InvalidParameterValueException("missing"));
|
||||
|
||||
final ResponseCapture ok = newResponse();
|
||||
handler.handle(newRequest("GET"), ok.response, "/api/clusters/cl-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(ok.response).setStatus(200);
|
||||
assertContains(ok.body(), "\"id\":\"cl-1\"");
|
||||
|
||||
final ResponseCapture missing = newResponse();
|
||||
handler.handle(newRequest("GET"), missing.response, "/api/clusters/missing", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(missing.response).setStatus(404);
|
||||
assertContains(missing.body(), "missing");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleRejectsUnsupportedMethod() throws Exception {
|
||||
final ClustersRouteHandler handler = new ClustersRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
|
||||
final ResponseCapture response = newResponse();
|
||||
handler.handle(newRequest("POST"), response.response, "/api/clusters", Negotiation.OutFormat.JSON, newServlet());
|
||||
|
||||
verify(response.response).setHeader("Allow", "GET");
|
||||
verify(response.response).setStatus(405);
|
||||
assertContains(response.body(), "Method Not Allowed");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// 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.cloudstack.veeam.api;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import org.apache.cloudstack.veeam.api.dto.DataCenter;
|
||||
import org.apache.cloudstack.veeam.api.dto.Network;
|
||||
import org.apache.cloudstack.veeam.api.dto.StorageDomain;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DataCentersRouteHandlerTest extends RouteHandlerTestSupport {
|
||||
|
||||
@Test
|
||||
public void testHandleGetListAndById() throws Exception {
|
||||
final DataCentersRouteHandler handler = new DataCentersRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listAllDataCenters(null, 20L)).thenReturn(List.of(withId(new DataCenter(), "dc-1")));
|
||||
when(handler.serverAdapter.getDataCenter("dc-1")).thenReturn(withId(new DataCenter(), "dc-1"));
|
||||
|
||||
final ResponseCapture list = newResponse();
|
||||
handler.handle(newRequest("GET", Map.of("max", "20"), null, null), list.response,
|
||||
"/api/datacenters", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(list.response).setStatus(200);
|
||||
assertContains(list.body(), "\"data_center\":[");
|
||||
|
||||
final ResponseCapture item = newResponse();
|
||||
handler.handle(newRequest("GET"), item.response, "/api/datacenters/dc-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(item.response).setStatus(200);
|
||||
assertContains(item.body(), "\"id\":\"dc-1\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleGetStorageDomainsAndNetworksByDataCenterId() throws Exception {
|
||||
final DataCentersRouteHandler handler = new DataCentersRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listStorageDomainsByDcId("dc-1", null, 15L))
|
||||
.thenReturn(List.of(withId(new StorageDomain(), "sd-1")));
|
||||
when(handler.serverAdapter.listNetworksByDcId("dc-1", null, 15L))
|
||||
.thenReturn(List.of(withId(new Network(), "net-1")));
|
||||
|
||||
final ResponseCapture storageDomains = newResponse();
|
||||
handler.handle(newRequest("GET", Map.of("max", "15"), null, null), storageDomains.response,
|
||||
"/api/datacenters/dc-1/storagedomains", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(storageDomains.response).setStatus(200);
|
||||
assertContains(storageDomains.body(), "\"storage_domain\":[");
|
||||
assertContains(storageDomains.body(), "sd-1");
|
||||
|
||||
final ResponseCapture networks = newResponse();
|
||||
handler.handle(newRequest("GET", Map.of("max", "15"), null, null), networks.response,
|
||||
"/api/datacenters/dc-1/networks", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(networks.response).setStatus(200);
|
||||
assertContains(networks.body(), "\"network\":[");
|
||||
assertContains(networks.body(), "net-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleMissingDataCenterReturnsNotFound() throws Exception {
|
||||
final DataCentersRouteHandler handler = new DataCentersRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.getDataCenter("missing")).thenThrow(new InvalidParameterValueException("missing dc"));
|
||||
|
||||
final ResponseCapture response = newResponse();
|
||||
handler.handle(newRequest("GET"), response.response, "/api/datacenters/missing", Negotiation.OutFormat.JSON, newServlet());
|
||||
|
||||
verify(response.response).setStatus(404);
|
||||
assertContains(response.body(), "missing dc");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
// 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.cloudstack.veeam.api;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.veeam.api.dto.Disk;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
public class DisksRouteHandlerTest extends RouteHandlerTestSupport {
|
||||
|
||||
@Test
|
||||
public void testHandleGetListAndGetById() throws Exception {
|
||||
final DisksRouteHandler handler = new DisksRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listAllDisks(null, 10L)).thenReturn(List.of(withId(new Disk(), "disk-1")));
|
||||
when(handler.serverAdapter.getDisk("disk-1")).thenReturn(withId(new Disk(), "disk-1"));
|
||||
|
||||
final ResponseCapture list = newResponse();
|
||||
handler.handle(newRequest("GET", Map.of("max", "10"), null, null), list.response,
|
||||
"/api/disks", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(list.response).setStatus(200);
|
||||
assertContains(list.body(), "\"disk\":[");
|
||||
|
||||
final ResponseCapture item = newResponse();
|
||||
handler.handle(newRequest("GET"), item.response, "/api/disks/disk-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(item.response).setStatus(200);
|
||||
assertContains(item.body(), "\"id\":\"disk-1\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePostAndPutParseDiskJson() throws Exception {
|
||||
final DisksRouteHandler handler = new DisksRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
|
||||
final ArgumentCaptor<Disk> createCaptor = ArgumentCaptor.forClass(Disk.class);
|
||||
final Disk created = withId(new Disk(), "disk-created");
|
||||
created.setName("created-disk");
|
||||
when(handler.serverAdapter.createDisk(createCaptor.capture())).thenReturn(created);
|
||||
|
||||
final ResponseCapture post = newResponse();
|
||||
handler.handle(newRequest("POST", Map.of(), "application/json", "{\"name\":\"created-disk\"}"), post.response,
|
||||
"/api/disks", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(post.response).setStatus(201);
|
||||
assertEquals("created-disk", createCaptor.getValue().getName());
|
||||
assertContains(post.body(), "disk-created");
|
||||
|
||||
final ArgumentCaptor<Disk> updateCaptor = ArgumentCaptor.forClass(Disk.class);
|
||||
final Disk updated = withId(new Disk(), "disk-1");
|
||||
updated.setName("updated-disk");
|
||||
when(handler.serverAdapter.updateDisk(org.mockito.ArgumentMatchers.eq("disk-1"), updateCaptor.capture())).thenReturn(updated);
|
||||
|
||||
final ResponseCapture put = newResponse();
|
||||
handler.handle(newRequest("PUT", Map.of(), "application/json", "{\"name\":\"updated-disk\"}"), put.response,
|
||||
"/api/disks/disk-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(put.response).setStatus(200);
|
||||
assertEquals("updated-disk", updateCaptor.getValue().getName());
|
||||
assertContains(put.body(), "updated-disk");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleDeleteCopyAndReduceRoutes() throws Exception {
|
||||
final DisksRouteHandler handler = new DisksRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.copyDisk("disk-1")).thenReturn(withId(new Disk(), "copy-1"));
|
||||
when(handler.serverAdapter.reduceDisk("disk-1")).thenReturn(withId(new Disk(), "reduced-1"));
|
||||
|
||||
final ResponseCapture delete = newResponse();
|
||||
handler.handle(newRequest("DELETE"), delete.response, "/api/disks/disk-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(handler.serverAdapter).deleteDisk("disk-1");
|
||||
verify(delete.response).setStatus(200);
|
||||
assertContains(delete.body(), "Deleted disk ID: disk-1");
|
||||
|
||||
final ResponseCapture copy = newResponse();
|
||||
handler.handle(newRequest("POST", Map.of(), "application/json", "{}"), copy.response,
|
||||
"/api/disks/disk-1/copy", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(copy.response).setStatus(200);
|
||||
assertContains(copy.body(), "copy-1");
|
||||
|
||||
final ResponseCapture reduce = newResponse();
|
||||
handler.handle(newRequest("POST", Map.of(), "application/json", "{}"), reduce.response,
|
||||
"/api/disks/disk-1/reduce", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(reduce.response).setStatus(200);
|
||||
assertContains(reduce.body(), "reduced-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleCopyRejectsUnsupportedMethod() throws Exception {
|
||||
final DisksRouteHandler handler = new DisksRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
|
||||
final ResponseCapture response = newResponse();
|
||||
handler.handle(newRequest("GET"), response.response, "/api/disks/disk-1/copy", Negotiation.OutFormat.JSON, newServlet());
|
||||
|
||||
verify(response.response).setHeader("Allow", "POST");
|
||||
verify(response.response).setStatus(405);
|
||||
assertContains(response.body(), "Method Not Allowed");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
// 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.cloudstack.veeam.api;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import org.apache.cloudstack.veeam.api.dto.Host;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HostsRouteHandlerTest extends RouteHandlerTestSupport {
|
||||
|
||||
@Test
|
||||
public void testHandleGetListAndGetById() throws Exception {
|
||||
final HostsRouteHandler handler = new HostsRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listAllHosts(null, null)).thenReturn(List.of(withId(new Host(), "host-1")));
|
||||
when(handler.serverAdapter.getHost("host-1")).thenReturn(withId(new Host(), "host-1"));
|
||||
|
||||
final ResponseCapture list = newResponse();
|
||||
handler.handle(newRequest("GET"), list.response, "/api/hosts", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(list.response).setStatus(200);
|
||||
assertContains(list.body(), "\"host\":[");
|
||||
assertContains(list.body(), "host-1");
|
||||
|
||||
final ResponseCapture item = newResponse();
|
||||
handler.handle(newRequest("GET"), item.response, "/api/hosts/host-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(item.response).setStatus(200);
|
||||
assertContains(item.body(), "\"id\":\"host-1\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleGetByIdNotFound() throws Exception {
|
||||
final HostsRouteHandler handler = new HostsRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.getHost("missing")).thenThrow(new InvalidParameterValueException("missing host"));
|
||||
|
||||
final ResponseCapture response = newResponse();
|
||||
handler.handle(newRequest("GET"), response.response, "/api/hosts/missing", Negotiation.OutFormat.JSON, newServlet());
|
||||
|
||||
verify(response.response).setStatus(404);
|
||||
assertContains(response.body(), "missing host");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// 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.cloudstack.veeam.api;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import org.apache.cloudstack.veeam.api.dto.ImageTransfer;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
public class ImageTransfersRouteHandlerTest extends RouteHandlerTestSupport {
|
||||
|
||||
@Test
|
||||
public void testHandleGetListAndById() throws Exception {
|
||||
final ImageTransfersRouteHandler handler = new ImageTransfersRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listAllImageTransfers(null, 5L)).thenReturn(List.of(withId(new ImageTransfer(), "transfer-1")));
|
||||
when(handler.serverAdapter.getImageTransfer("transfer-1")).thenReturn(withId(new ImageTransfer(), "transfer-1"));
|
||||
|
||||
final ResponseCapture list = newResponse();
|
||||
handler.handle(newRequest("GET", Map.of("max", "5"), null, null), list.response,
|
||||
"/api/imagetransfers", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(list.response).setStatus(200);
|
||||
assertContains(list.body(), "\"image_transfer\":[");
|
||||
|
||||
final ResponseCapture item = newResponse();
|
||||
handler.handle(newRequest("GET"), item.response, "/api/imagetransfers/transfer-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(item.response).setStatus(200);
|
||||
assertContains(item.body(), "\"id\":\"transfer-1\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePostParsesRequestAndCancelFinalizeActions() throws Exception {
|
||||
final ImageTransfersRouteHandler handler = new ImageTransfersRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
|
||||
final ArgumentCaptor<ImageTransfer> captor = ArgumentCaptor.forClass(ImageTransfer.class);
|
||||
final ImageTransfer created = withId(new ImageTransfer(), "transfer-created");
|
||||
created.setPhase("transferring");
|
||||
when(handler.serverAdapter.createImageTransfer(captor.capture())).thenReturn(created);
|
||||
|
||||
final ResponseCapture post = newResponse();
|
||||
handler.handle(newRequest("POST", Map.of(), "application/json", "{\"phase\":\"transferring\"}"), post.response,
|
||||
"/api/imagetransfers", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(post.response).setStatus(201);
|
||||
assertEquals("transferring", captor.getValue().getPhase());
|
||||
assertContains(post.body(), "transfer-created");
|
||||
|
||||
final ResponseCapture cancel = newResponse();
|
||||
handler.handle(newRequest("POST"), cancel.response, "/api/imagetransfers/transfer-1/cancel", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(handler.serverAdapter).cancelImageTransfer("transfer-1");
|
||||
verify(cancel.response).setStatus(200);
|
||||
assertContains(cancel.body(), "cancelled successfully");
|
||||
|
||||
final ResponseCapture finalize = newResponse();
|
||||
handler.handle(newRequest("POST"), finalize.response, "/api/imagetransfers/transfer-1/finalize", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(handler.serverAdapter).finalizeImageTransfer("transfer-1");
|
||||
verify(finalize.response).setStatus(200);
|
||||
assertContains(finalize.body(), "finalized successfully");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleMissingTransferAndUnsupportedActionMethod() throws Exception {
|
||||
final ImageTransfersRouteHandler handler = new ImageTransfersRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.getImageTransfer("missing")).thenThrow(new InvalidParameterValueException("missing transfer"));
|
||||
|
||||
final ResponseCapture missing = newResponse();
|
||||
handler.handle(newRequest("GET"), missing.response, "/api/imagetransfers/missing", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(missing.response).setStatus(404);
|
||||
assertContains(missing.body(), "missing transfer");
|
||||
|
||||
final ResponseCapture wrongMethod = newResponse();
|
||||
handler.handle(newRequest("GET"), wrongMethod.response, "/api/imagetransfers/transfer-1/cancel", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(wrongMethod.response).setHeader("Allow", "POST");
|
||||
verify(wrongMethod.response).setStatus(405);
|
||||
assertContains(wrongMethod.body(), "Method Not Allowed");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// 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.cloudstack.veeam.api;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import org.apache.cloudstack.veeam.api.dto.Job;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JobsRouteHandlerTest extends RouteHandlerTestSupport {
|
||||
|
||||
@Test
|
||||
public void testHandleGetListAndGetById() throws Exception {
|
||||
final JobsRouteHandler handler = new JobsRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listPendingJobs()).thenReturn(List.of(withId(new Job(), "job-1")));
|
||||
when(handler.serverAdapter.getJob("job-1")).thenReturn(withId(new Job(), "job-1"));
|
||||
|
||||
final ResponseCapture list = newResponse();
|
||||
handler.handle(newRequest("GET"), list.response, "/api/jobs", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(list.response).setStatus(200);
|
||||
assertContains(list.body(), "\"job\":[");
|
||||
|
||||
final ResponseCapture item = newResponse();
|
||||
handler.handle(newRequest("GET"), item.response, "/api/jobs/job-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(item.response).setStatus(200);
|
||||
assertContains(item.body(), "\"id\":\"job-1\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleMissingJobAndUnsupportedMethod() throws Exception {
|
||||
final JobsRouteHandler handler = new JobsRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.getJob("missing")).thenThrow(new InvalidParameterValueException("missing job"));
|
||||
|
||||
final ResponseCapture missing = newResponse();
|
||||
handler.handle(newRequest("GET"), missing.response, "/api/jobs/missing", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(missing.response).setStatus(404);
|
||||
assertContains(missing.body(), "missing job");
|
||||
|
||||
final ResponseCapture wrongMethod = newResponse();
|
||||
handler.handle(newRequest("POST"), wrongMethod.response, "/api/jobs", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(wrongMethod.response).setStatus(405);
|
||||
assertContains(wrongMethod.body(), "Method Not Allowed");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// 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.cloudstack.veeam.api;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import org.apache.cloudstack.veeam.api.dto.Network;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.junit.Test;
|
||||
|
||||
public class NetworksRouteHandlerTest extends RouteHandlerTestSupport {
|
||||
|
||||
@Test
|
||||
public void testHandleGetListAndById() throws Exception {
|
||||
final NetworksRouteHandler handler = new NetworksRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listAllNetworks(null, null)).thenReturn(List.of(withId(new Network(), "net-1")));
|
||||
when(handler.serverAdapter.getNetwork("net-1")).thenReturn(withId(new Network(), "net-1"));
|
||||
|
||||
final ResponseCapture list = newResponse();
|
||||
handler.handle(newRequest("GET"), list.response, "/api/networks", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(list.response).setStatus(200);
|
||||
assertContains(list.body(), "\"network\":[");
|
||||
|
||||
final ResponseCapture item = newResponse();
|
||||
handler.handle(newRequest("GET"), item.response, "/api/networks/net-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(item.response).setStatus(200);
|
||||
assertContains(item.body(), "\"id\":\"net-1\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleMissingNetworkReturnsNotFound() throws Exception {
|
||||
final NetworksRouteHandler handler = new NetworksRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.getNetwork("missing")).thenThrow(new InvalidParameterValueException("missing network"));
|
||||
|
||||
final ResponseCapture response = newResponse();
|
||||
handler.handle(newRequest("GET"), response.response, "/api/networks/missing", Negotiation.OutFormat.JSON, newServlet());
|
||||
|
||||
verify(response.response).setStatus(404);
|
||||
assertContains(response.body(), "missing network");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// 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.cloudstack.veeam.api;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.cloudstack.veeam.VeeamControlServlet;
|
||||
import org.apache.cloudstack.veeam.api.dto.BaseDto;
|
||||
|
||||
class RouteHandlerTestSupport {
|
||||
|
||||
protected VeeamControlServlet newServlet() {
|
||||
return new VeeamControlServlet(Collections.emptyList());
|
||||
}
|
||||
|
||||
protected HttpServletRequest newRequest(final String method) throws Exception {
|
||||
return newRequest(method, Collections.emptyMap(), null, null);
|
||||
}
|
||||
|
||||
protected HttpServletRequest newRequest(final String method, final Map<String, String> params,
|
||||
final String contentType, final String body) throws Exception {
|
||||
final HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
final Map<String, String> safeParams = params == null ? Collections.emptyMap() : params;
|
||||
|
||||
when(request.getMethod()).thenReturn(method);
|
||||
when(request.getContentType()).thenReturn(contentType);
|
||||
when(request.getParameterMap()).thenReturn(toParameterMap(safeParams));
|
||||
when(request.getParameter(org.mockito.ArgumentMatchers.anyString())).thenAnswer(invocation -> safeParams.get(invocation.getArgument(0)));
|
||||
when(request.getReader()).thenReturn(new BufferedReader(new StringReader(body == null ? "" : body)));
|
||||
return request;
|
||||
}
|
||||
|
||||
protected ResponseCapture newResponse() throws Exception {
|
||||
final HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
final StringWriter sink = new StringWriter();
|
||||
final PrintWriter writer = new PrintWriter(sink);
|
||||
when(response.getWriter()).thenReturn(writer);
|
||||
return new ResponseCapture(response, sink, writer);
|
||||
}
|
||||
|
||||
protected static <T extends BaseDto> T withId(final T dto, final String id) {
|
||||
dto.setId(id);
|
||||
dto.setHref("/api/test/" + id);
|
||||
return dto;
|
||||
}
|
||||
|
||||
protected static void assertContains(final String actual, final String expected) {
|
||||
assertTrue("Expected body to contain: " + expected + " but was: " + actual, actual.contains(expected));
|
||||
}
|
||||
|
||||
private static Map<String, String[]> toParameterMap(final Map<String, String> params) {
|
||||
final java.util.LinkedHashMap<String, String[]> result = new java.util.LinkedHashMap<>();
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
result.put(entry.getKey(), new String[]{entry.getValue()});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected static class ResponseCapture {
|
||||
final HttpServletResponse response;
|
||||
private final StringWriter sink;
|
||||
private final PrintWriter writer;
|
||||
|
||||
ResponseCapture(final HttpServletResponse response, final StringWriter sink, final PrintWriter writer) {
|
||||
this.response = response;
|
||||
this.sink = sink;
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
String body() {
|
||||
writer.flush();
|
||||
return sink.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// 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.cloudstack.veeam.api;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import org.apache.cloudstack.veeam.api.dto.Tag;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TagsRouteHandlerTest extends RouteHandlerTestSupport {
|
||||
|
||||
@Test
|
||||
public void testHandleGetListAndById() throws Exception {
|
||||
final TagsRouteHandler handler = new TagsRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listAllTags(null, null)).thenReturn(List.of(withId(new Tag(), "tag-1")));
|
||||
when(handler.serverAdapter.getTag("tag-1")).thenReturn(withId(new Tag(), "tag-1"));
|
||||
|
||||
final ResponseCapture list = newResponse();
|
||||
handler.handle(newRequest("GET"), list.response, "/api/tags", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(list.response).setStatus(200);
|
||||
assertContains(list.body(), "\"tag\":[");
|
||||
|
||||
final ResponseCapture item = newResponse();
|
||||
handler.handle(newRequest("GET"), item.response, "/api/tags/tag-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(item.response).setStatus(200);
|
||||
assertContains(item.body(), "\"id\":\"tag-1\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleMissingTagReturnsNotFound() throws Exception {
|
||||
final TagsRouteHandler handler = new TagsRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.getTag("missing")).thenThrow(new InvalidParameterValueException("missing tag"));
|
||||
|
||||
final ResponseCapture response = newResponse();
|
||||
handler.handle(newRequest("GET"), response.response, "/api/tags/missing", Negotiation.OutFormat.JSON, newServlet());
|
||||
|
||||
verify(response.response).setStatus(404);
|
||||
assertContains(response.body(), "missing tag");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,297 @@
|
|||
// 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.cloudstack.veeam.api;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.veeam.api.dto.Backup;
|
||||
import org.apache.cloudstack.veeam.api.dto.Checkpoint;
|
||||
import org.apache.cloudstack.veeam.api.dto.Disk;
|
||||
import org.apache.cloudstack.veeam.api.dto.DiskAttachment;
|
||||
import org.apache.cloudstack.veeam.api.dto.Nic;
|
||||
import org.apache.cloudstack.veeam.api.dto.ResourceAction;
|
||||
import org.apache.cloudstack.veeam.api.dto.Snapshot;
|
||||
import org.apache.cloudstack.veeam.api.dto.Vm;
|
||||
import org.apache.cloudstack.veeam.api.dto.VmAction;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
public class VmsRouteHandlerTest extends RouteHandlerTestSupport {
|
||||
|
||||
@Test
|
||||
public void testHandleGetListUsesFollowFlags() throws Exception {
|
||||
final VmsRouteHandler handler = new VmsRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listAllInstances(true, true, true, true, null, 10L))
|
||||
.thenReturn(List.of(withId(new Vm(), "vm-1")));
|
||||
|
||||
final ResponseCapture response = newResponse();
|
||||
handler.handle(newRequest("GET", Map.of(
|
||||
"max", "10",
|
||||
"all_content", "true",
|
||||
"follow", "tags,disk_attachments.disk,nics.reporteddevices"), null, null),
|
||||
response.response, "/api/vms", Negotiation.OutFormat.JSON, newServlet());
|
||||
|
||||
verify(handler.serverAdapter).listAllInstances(true, true, true, true, null, 10L);
|
||||
verify(response.response).setStatus(200);
|
||||
assertContains(response.body(), "\"vm\":[");
|
||||
assertContains(response.body(), "vm-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePostAndUpdateParseVmJson() throws Exception {
|
||||
final VmsRouteHandler handler = new VmsRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
|
||||
final ArgumentCaptor<Vm> createCaptor = ArgumentCaptor.forClass(Vm.class);
|
||||
final Vm created = withId(new Vm(), "vm-created");
|
||||
created.setName("vm-created");
|
||||
when(handler.serverAdapter.createInstance(createCaptor.capture())).thenReturn(created);
|
||||
|
||||
final ResponseCapture post = newResponse();
|
||||
handler.handle(newRequest("POST", Map.of(), "application/json", "{\"name\":\"vm-created\"}"), post.response,
|
||||
"/api/vms", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(post.response).setStatus(201);
|
||||
assertEquals("vm-created", createCaptor.getValue().getName());
|
||||
assertContains(post.body(), "vm-created");
|
||||
|
||||
final ArgumentCaptor<Vm> updateCaptor = ArgumentCaptor.forClass(Vm.class);
|
||||
final Vm updated = withId(new Vm(), "vm-1");
|
||||
updated.setName("vm-updated");
|
||||
when(handler.serverAdapter.updateInstance(eq("vm-1"), updateCaptor.capture())).thenReturn(updated);
|
||||
|
||||
final ResponseCapture put = newResponse();
|
||||
handler.handle(newRequest("PUT", Map.of(), "application/json", "{\"name\":\"vm-updated\"}"), put.response,
|
||||
"/api/vms/vm-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(put.response).setStatus(200);
|
||||
assertEquals("vm-updated", updateCaptor.getValue().getName());
|
||||
assertContains(put.body(), "vm-updated");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleGetByIdDeleteAndPowerActions() throws Exception {
|
||||
final VmsRouteHandler handler = new VmsRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.getInstance("vm-1", true, true, true, true)).thenReturn(withId(new Vm(), "vm-1"));
|
||||
|
||||
final VmAction deleteAction = new VmAction();
|
||||
deleteAction.setStatus("deleted");
|
||||
final VmAction startAction = new VmAction();
|
||||
startAction.setStatus("starting");
|
||||
final VmAction stopAction = new VmAction();
|
||||
stopAction.setStatus("stopping");
|
||||
final VmAction shutdownAction = new VmAction();
|
||||
shutdownAction.setStatus("shutting_down");
|
||||
when(handler.serverAdapter.deleteInstance("vm-1", true)).thenReturn(deleteAction);
|
||||
when(handler.serverAdapter.startInstance("vm-1", false)).thenReturn(startAction);
|
||||
when(handler.serverAdapter.stopInstance("vm-1", false)).thenReturn(stopAction);
|
||||
when(handler.serverAdapter.shutdownInstance("vm-1", false)).thenReturn(shutdownAction);
|
||||
|
||||
final ResponseCapture get = newResponse();
|
||||
handler.handle(newRequest("GET", Map.of(
|
||||
"all_content", "true",
|
||||
"follow", "tags,disk_attachments.disk,nics.reporteddevices"), null, null),
|
||||
get.response, "/api/vms/vm-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(get.response).setStatus(200);
|
||||
assertContains(get.body(), "\"id\":\"vm-1\"");
|
||||
|
||||
final ResponseCapture delete = newResponse();
|
||||
handler.handle(newRequest("DELETE", Map.of("async", "true"), null, null), delete.response,
|
||||
"/api/vms/vm-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(handler.serverAdapter).deleteInstance("vm-1", true);
|
||||
verify(delete.response).setStatus(200);
|
||||
assertContains(delete.body(), "deleted");
|
||||
|
||||
final ResponseCapture start = newResponse();
|
||||
handler.handle(newRequest("POST"), start.response, "/api/vms/vm-1/start", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(start.response).setStatus(202);
|
||||
assertContains(start.body(), "starting");
|
||||
|
||||
final ResponseCapture stop = newResponse();
|
||||
handler.handle(newRequest("POST"), stop.response, "/api/vms/vm-1/stop", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(stop.response).setStatus(202);
|
||||
assertContains(stop.body(), "stopping");
|
||||
|
||||
final ResponseCapture shutdown = newResponse();
|
||||
handler.handle(newRequest("POST"), shutdown.response, "/api/vms/vm-1/shutdown", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(shutdown.response).setStatus(202);
|
||||
assertContains(shutdown.body(), "shutting_down");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleDiskAttachmentAndNicRoutes() throws Exception {
|
||||
final VmsRouteHandler handler = new VmsRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listDiskAttachmentsByInstanceUuid("vm-1"))
|
||||
.thenReturn(List.of(withId(new DiskAttachment(), "attach-1")));
|
||||
when(handler.serverAdapter.listNicsByInstanceUuid("vm-1"))
|
||||
.thenReturn(List.of(withId(new Nic(), "nic-1")));
|
||||
|
||||
final ArgumentCaptor<DiskAttachment> diskAttachmentCaptor = ArgumentCaptor.forClass(DiskAttachment.class);
|
||||
final DiskAttachment createdAttachment = withId(new DiskAttachment(), "attach-created");
|
||||
createdAttachment.setActive("true");
|
||||
when(handler.serverAdapter.attachInstanceDisk(eq("vm-1"), diskAttachmentCaptor.capture())).thenReturn(createdAttachment);
|
||||
|
||||
final ArgumentCaptor<Nic> nicCaptor = ArgumentCaptor.forClass(Nic.class);
|
||||
final Nic createdNic = withId(new Nic(), "nic-created");
|
||||
createdNic.setName("nic-created");
|
||||
when(handler.serverAdapter.attachInstanceNic(eq("vm-1"), nicCaptor.capture())).thenReturn(createdNic);
|
||||
|
||||
final ResponseCapture attachments = newResponse();
|
||||
handler.handle(newRequest("GET"), attachments.response, "/api/vms/vm-1/diskattachments", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(attachments.response).setStatus(200);
|
||||
assertContains(attachments.body(), "\"disk_attachment\":[");
|
||||
|
||||
final ResponseCapture postAttachment = newResponse();
|
||||
handler.handle(newRequest("POST", Map.of(), "application/json", "{\"active\":\"true\"}"), postAttachment.response,
|
||||
"/api/vms/vm-1/diskattachments", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(postAttachment.response).setStatus(201);
|
||||
assertEquals("true", diskAttachmentCaptor.getValue().getActive());
|
||||
assertContains(postAttachment.body(), "attach-created");
|
||||
|
||||
final ResponseCapture nics = newResponse();
|
||||
handler.handle(newRequest("GET"), nics.response, "/api/vms/vm-1/nics", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(nics.response).setStatus(200);
|
||||
assertContains(nics.body(), "\"nic\":[");
|
||||
|
||||
final ResponseCapture postNic = newResponse();
|
||||
handler.handle(newRequest("POST", Map.of(), "application/json", "{\"name\":\"nic-created\"}"), postNic.response,
|
||||
"/api/vms/vm-1/nics", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(postNic.response).setStatus(201);
|
||||
assertEquals("nic-created", nicCaptor.getValue().getName());
|
||||
assertContains(postNic.body(), "nic-created");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleSnapshotRoutes() throws Exception {
|
||||
final VmsRouteHandler handler = new VmsRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listSnapshotsByInstanceUuid("vm-1")).thenReturn(List.of(withId(new Snapshot(), "snap-1")));
|
||||
when(handler.serverAdapter.getSnapshot("snap-1")).thenReturn(withId(new Snapshot(), "snap-1"));
|
||||
|
||||
final ArgumentCaptor<Snapshot> createCaptor = ArgumentCaptor.forClass(Snapshot.class);
|
||||
final Snapshot createdSnapshot = withId(new Snapshot(), "snap-created");
|
||||
createdSnapshot.setDescription("created snapshot");
|
||||
when(handler.serverAdapter.createInstanceSnapshot(eq("vm-1"), createCaptor.capture())).thenReturn(createdSnapshot);
|
||||
|
||||
final ResourceAction deleteAction = new ResourceAction();
|
||||
deleteAction.setStatus("deleted");
|
||||
final ResourceAction restoreAction = new ResourceAction();
|
||||
restoreAction.setStatus("restored");
|
||||
when(handler.serverAdapter.deleteSnapshot("snap-1", true)).thenReturn(deleteAction);
|
||||
when(handler.serverAdapter.revertInstanceToSnapshot("snap-1", false)).thenReturn(restoreAction);
|
||||
|
||||
final ResponseCapture list = newResponse();
|
||||
handler.handle(newRequest("GET"), list.response, "/api/vms/vm-1/snapshots", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(list.response).setStatus(200);
|
||||
assertContains(list.body(), "\"snapshot\":[");
|
||||
|
||||
final ResponseCapture post = newResponse();
|
||||
handler.handle(newRequest("POST", Map.of(), "application/json", "{\"description\":\"created snapshot\"}"), post.response,
|
||||
"/api/vms/vm-1/snapshots", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(post.response).setStatus(202);
|
||||
assertEquals("created snapshot", createCaptor.getValue().getDescription());
|
||||
assertContains(post.body(), "snap-created");
|
||||
|
||||
final ResponseCapture get = newResponse();
|
||||
handler.handle(newRequest("GET"), get.response, "/api/vms/vm-1/snapshots/snap-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(get.response).setStatus(200);
|
||||
assertContains(get.body(), "snap-1");
|
||||
|
||||
final ResponseCapture delete = newResponse();
|
||||
handler.handle(newRequest("DELETE", Map.of("async", "true"), null, null), delete.response,
|
||||
"/api/vms/vm-1/snapshots/snap-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(delete.response).setStatus(202);
|
||||
assertContains(delete.body(), "deleted");
|
||||
|
||||
final ResponseCapture restore = newResponse();
|
||||
handler.handle(newRequest("POST"), restore.response, "/api/vms/vm-1/snapshots/snap-1/restore", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(restore.response).setStatus(202);
|
||||
assertContains(restore.body(), "restored");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleBackupRoutes() throws Exception {
|
||||
final VmsRouteHandler handler = new VmsRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listBackupsByInstanceUuid("vm-1")).thenReturn(List.of(withId(new Backup(), "backup-1")));
|
||||
when(handler.serverAdapter.getBackup("backup-1")).thenReturn(withId(new Backup(), "backup-1"));
|
||||
when(handler.serverAdapter.listDisksByBackupUuid("backup-1")).thenReturn(List.of(withId(new Disk(), "disk-1")));
|
||||
|
||||
final ArgumentCaptor<Backup> createCaptor = ArgumentCaptor.forClass(Backup.class);
|
||||
final Backup createdBackup = withId(new Backup(), "backup-created");
|
||||
createdBackup.setName("backup-created");
|
||||
when(handler.serverAdapter.createInstanceBackup(eq("vm-1"), createCaptor.capture())).thenReturn(createdBackup);
|
||||
|
||||
final Backup finalizedBackup = withId(new Backup(), "backup-1");
|
||||
finalizedBackup.setPhase("finalized");
|
||||
when(handler.serverAdapter.finalizeBackup("vm-1", "backup-1")).thenReturn(finalizedBackup);
|
||||
|
||||
final ResponseCapture list = newResponse();
|
||||
handler.handle(newRequest("GET"), list.response, "/api/vms/vm-1/backups", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(list.response).setStatus(200);
|
||||
assertContains(list.body(), "\"backup\":[");
|
||||
|
||||
final ResponseCapture post = newResponse();
|
||||
handler.handle(newRequest("POST", Map.of(), "application/json", "{\"name\":\"backup-created\"}"), post.response,
|
||||
"/api/vms/vm-1/backups", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(post.response).setStatus(200);
|
||||
assertEquals("backup-created", createCaptor.getValue().getName());
|
||||
assertContains(post.body(), "backup-created");
|
||||
|
||||
final ResponseCapture get = newResponse();
|
||||
handler.handle(newRequest("GET"), get.response, "/api/vms/vm-1/backups/backup-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(get.response).setStatus(200);
|
||||
assertContains(get.body(), "backup-1");
|
||||
|
||||
final ResponseCapture disks = newResponse();
|
||||
handler.handle(newRequest("GET"), disks.response, "/api/vms/vm-1/backups/backup-1/disks", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(disks.response).setStatus(200);
|
||||
assertContains(disks.body(), "\"disk\":[");
|
||||
|
||||
final ResponseCapture finalize = newResponse();
|
||||
handler.handle(newRequest("POST"), finalize.response, "/api/vms/vm-1/backups/backup-1/finalize", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(finalize.response).setStatus(200);
|
||||
assertContains(finalize.body(), "finalized");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleCheckpointRoutes() throws Exception {
|
||||
final VmsRouteHandler handler = new VmsRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listCheckpointsByInstanceUuid("vm-1")).thenReturn(List.of(withId(new Checkpoint(), "chk-1")));
|
||||
|
||||
final ResponseCapture list = newResponse();
|
||||
handler.handle(newRequest("GET"), list.response, "/api/vms/vm-1/checkpoints", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(list.response).setStatus(200);
|
||||
assertContains(list.body(), "\"checkpoints\":[");
|
||||
|
||||
final ResponseCapture delete = newResponse();
|
||||
handler.handle(newRequest("DELETE"), delete.response, "/api/vms/vm-1/checkpoints/chk-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(handler.serverAdapter).deleteCheckpoint("vm-1", "chk-1");
|
||||
verify(delete.response).setStatus(200);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// 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.cloudstack.veeam.api;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import org.apache.cloudstack.veeam.api.dto.VnicProfile;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.junit.Test;
|
||||
|
||||
public class VnicProfilesRouteHandlerTest extends RouteHandlerTestSupport {
|
||||
|
||||
@Test
|
||||
public void testHandleGetListAndById() throws Exception {
|
||||
final VnicProfilesRouteHandler handler = new VnicProfilesRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.listAllVnicProfiles(null, null)).thenReturn(List.of(withId(new VnicProfile(), "vnic-1")));
|
||||
when(handler.serverAdapter.getVnicProfile("vnic-1")).thenReturn(withId(new VnicProfile(), "vnic-1"));
|
||||
|
||||
final ResponseCapture list = newResponse();
|
||||
handler.handle(newRequest("GET"), list.response, "/api/vnicprofiles", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(list.response).setStatus(200);
|
||||
assertContains(list.body(), "\"vnic_profile\":[");
|
||||
|
||||
final ResponseCapture item = newResponse();
|
||||
handler.handle(newRequest("GET"), item.response, "/api/vnicprofiles/vnic-1", Negotiation.OutFormat.JSON, newServlet());
|
||||
verify(item.response).setStatus(200);
|
||||
assertContains(item.body(), "\"id\":\"vnic-1\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleMissingVnicProfileReturnsNotFound() throws Exception {
|
||||
final VnicProfilesRouteHandler handler = new VnicProfilesRouteHandler();
|
||||
handler.serverAdapter = mock(org.apache.cloudstack.veeam.adapter.ServerAdapter.class);
|
||||
when(handler.serverAdapter.getVnicProfile("missing")).thenThrow(new InvalidParameterValueException("missing vnic"));
|
||||
|
||||
final ResponseCapture response = newResponse();
|
||||
handler.handle(newRequest("GET"), response.response, "/api/vnicprofiles/missing", Negotiation.OutFormat.JSON, newServlet());
|
||||
|
||||
verify(response.response).setStatus(404);
|
||||
assertContains(response.body(), "missing vnic");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.jobs.JobInfo;
|
||||
import org.apache.cloudstack.veeam.api.dto.Job;
|
||||
import org.apache.cloudstack.veeam.api.dto.ResourceAction;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.cloud.api.query.vo.AsyncJobJoinVO;
|
||||
|
||||
public class AsyncJobJoinVOToJobConverterTest {
|
||||
|
||||
@Test
|
||||
public void testToJob_MapsSucceededStatusAndOwnerRef() {
|
||||
final AsyncJobJoinVO vo = mock(AsyncJobJoinVO.class);
|
||||
when(vo.getUuid()).thenReturn("job-1");
|
||||
when(vo.getUserUuid()).thenReturn("user-1");
|
||||
when(vo.getCreated()).thenReturn(new Date(1000L));
|
||||
when(vo.getStatus()).thenReturn(JobInfo.Status.SUCCEEDED.ordinal());
|
||||
|
||||
final Job job = AsyncJobJoinVOToJobConverter.toJob(vo);
|
||||
|
||||
assertEquals("job-1", job.getId());
|
||||
assertEquals("finished", job.getStatus());
|
||||
assertEquals(Long.valueOf(1000L), job.getStartTime());
|
||||
assertNotNull(job.getEndTime());
|
||||
assertEquals("user-1", job.getOwner().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToJob_MapsInProgressToStartedAndNoEndTime() {
|
||||
final AsyncJobJoinVO vo = mock(AsyncJobJoinVO.class);
|
||||
when(vo.getUuid()).thenReturn("job-2");
|
||||
when(vo.getUserUuid()).thenReturn("user-2");
|
||||
when(vo.getCreated()).thenReturn(new Date(2000L));
|
||||
when(vo.getStatus()).thenReturn(JobInfo.Status.IN_PROGRESS.ordinal());
|
||||
|
||||
final Job job = AsyncJobJoinVOToJobConverter.toJob(vo);
|
||||
|
||||
assertEquals("started", job.getStatus());
|
||||
assertNull(job.getEndTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToActionAndToJobList() {
|
||||
final AsyncJobJoinVO vo = mock(AsyncJobJoinVO.class);
|
||||
when(vo.getUuid()).thenReturn("job-3");
|
||||
when(vo.getUserUuid()).thenReturn("user-3");
|
||||
when(vo.getCreated()).thenReturn(new Date(3000L));
|
||||
when(vo.getStatus()).thenReturn(JobInfo.Status.CANCELLED.ordinal());
|
||||
|
||||
final ResourceAction action = AsyncJobJoinVOToJobConverter.toAction(vo);
|
||||
assertEquals("complete", action.getStatus());
|
||||
assertEquals("job-3", action.getJob().getId());
|
||||
|
||||
final List<Job> jobs = AsyncJobJoinVOToJobConverter.toJobList(List.of(vo));
|
||||
assertEquals(1, jobs.size());
|
||||
assertEquals("aborted", jobs.get(0).getStatus());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.backup.Backup;
|
||||
import org.apache.cloudstack.backup.BackupVO;
|
||||
import org.apache.cloudstack.veeam.api.dto.Disk;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.cloud.api.query.vo.HostJoinVO;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
|
||||
public class BackupVOToBackupConverterTest {
|
||||
|
||||
@Test
|
||||
public void testToBackup_MapsCoreFieldsAndResolvedRefs() {
|
||||
final BackupVO backupVO = mock(BackupVO.class);
|
||||
when(backupVO.getUuid()).thenReturn("bkp-1");
|
||||
when(backupVO.getName()).thenReturn("backup-1");
|
||||
when(backupVO.getDescription()).thenReturn("desc-1");
|
||||
when(backupVO.getDate()).thenReturn(new Date(1000L));
|
||||
when(backupVO.getStatus()).thenReturn(Backup.Status.ReadyForTransfer);
|
||||
when(backupVO.getFromCheckpointId()).thenReturn("cp-1");
|
||||
when(backupVO.getToCheckpointId()).thenReturn("cp-2");
|
||||
when(backupVO.getVmId()).thenReturn(101L);
|
||||
when(backupVO.getHostId()).thenReturn(201L);
|
||||
|
||||
final UserVmVO vmVO = mock(UserVmVO.class);
|
||||
when(vmVO.getUuid()).thenReturn("vm-1");
|
||||
final HostJoinVO hostVO = mock(HostJoinVO.class);
|
||||
when(hostVO.getUuid()).thenReturn("host-1");
|
||||
|
||||
final org.apache.cloudstack.veeam.api.dto.Backup backup = BackupVOToBackupConverter.toBackup(
|
||||
backupVO,
|
||||
id -> vmVO,
|
||||
id -> hostVO,
|
||||
vo -> List.of(new Disk())
|
||||
);
|
||||
|
||||
assertEquals("bkp-1", backup.getId());
|
||||
assertEquals("backup-1", backup.getName());
|
||||
assertEquals("desc-1", backup.getDescription());
|
||||
assertEquals(Long.valueOf(1000L), backup.getCreationDate());
|
||||
assertEquals("ready", backup.getPhase());
|
||||
assertEquals("cp-1", backup.getFromCheckpointId());
|
||||
assertEquals("cp-2", backup.getToCheckpointId());
|
||||
assertEquals("vm-1", backup.getVm().getId());
|
||||
assertEquals("host-1", backup.getHost().getId());
|
||||
assertNotNull(backup.getDisks());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToBackup_PhaseMappingForDifferentStatuses() {
|
||||
final BackupVO queued = mock(BackupVO.class);
|
||||
when(queued.getUuid()).thenReturn("b1");
|
||||
when(queued.getDate()).thenReturn(new Date(1L));
|
||||
when(queued.getStatus()).thenReturn(Backup.Status.Queued);
|
||||
when(queued.getVmId()).thenReturn(1L);
|
||||
|
||||
final BackupVO finalizing = mock(BackupVO.class);
|
||||
when(finalizing.getUuid()).thenReturn("b2");
|
||||
when(finalizing.getDate()).thenReturn(new Date(2L));
|
||||
when(finalizing.getStatus()).thenReturn(Backup.Status.FinalizingTransfer);
|
||||
when(finalizing.getVmId()).thenReturn(2L);
|
||||
|
||||
final BackupVO failed = mock(BackupVO.class);
|
||||
when(failed.getUuid()).thenReturn("b3");
|
||||
when(failed.getDate()).thenReturn(new Date(3L));
|
||||
when(failed.getStatus()).thenReturn(Backup.Status.Failed);
|
||||
when(failed.getVmId()).thenReturn(3L);
|
||||
|
||||
assertEquals("initializing", BackupVOToBackupConverter.toBackup(queued, null, null, null).getPhase());
|
||||
assertEquals("finalizing", BackupVOToBackupConverter.toBackup(finalizing, null, null, null).getPhase());
|
||||
assertEquals("failed", BackupVOToBackupConverter.toBackup(failed, null, null, null).getPhase());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.veeam.api.dto.Cluster;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.cloud.api.query.vo.DataCenterJoinVO;
|
||||
import com.cloud.cpu.CPU;
|
||||
import com.cloud.dc.ClusterVO;
|
||||
|
||||
public class ClusterVOToClusterConverterTest {
|
||||
|
||||
@Test
|
||||
public void testToCluster_MapsDefaultsAndResolvedDataCenter() {
|
||||
final ClusterVO vo = mock(ClusterVO.class);
|
||||
when(vo.getUuid()).thenReturn("cluster-1");
|
||||
when(vo.getName()).thenReturn("cluster-a");
|
||||
when(vo.getArch()).thenReturn(CPU.CPUArch.amd64);
|
||||
when(vo.getDataCenterId()).thenReturn(11L);
|
||||
|
||||
final DataCenterJoinVO zone = mock(DataCenterJoinVO.class);
|
||||
when(zone.getUuid()).thenReturn("dc-1");
|
||||
|
||||
final Cluster cluster = ClusterVOToClusterConverter.toCluster(vo, id -> zone);
|
||||
|
||||
assertEquals("cluster-1", cluster.getId());
|
||||
assertEquals("cluster-a", cluster.getName());
|
||||
assertEquals("x86_64", cluster.getCpu().getArchitecture());
|
||||
assertEquals("dc-1", cluster.getDataCenter().getId());
|
||||
assertEquals("urandom", cluster.getRequiredRngSources().requiredRngSource.get(0));
|
||||
assertEquals("networks", cluster.getLink().get(0).getRel());
|
||||
assertNotNull(cluster.getSchedulingPolicy());
|
||||
assertNotNull(cluster.getMacPool());
|
||||
assertTrue(cluster.getHref().contains("/api/clusters/cluster-1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToClusterList_ConvertsAllItems() {
|
||||
final ClusterVO first = mock(ClusterVO.class);
|
||||
when(first.getUuid()).thenReturn("c1");
|
||||
when(first.getName()).thenReturn("c1");
|
||||
when(first.getArch()).thenReturn(CPU.CPUArch.x86);
|
||||
when(first.getDataCenterId()).thenReturn(1L);
|
||||
|
||||
final ClusterVO second = mock(ClusterVO.class);
|
||||
when(second.getUuid()).thenReturn("c2");
|
||||
when(second.getName()).thenReturn("c2");
|
||||
when(second.getArch()).thenReturn(CPU.CPUArch.amd64);
|
||||
when(second.getDataCenterId()).thenReturn(2L);
|
||||
|
||||
final List<Cluster> clusters = ClusterVOToClusterConverter.toClusterList(List.of(first, second), id -> null);
|
||||
|
||||
assertEquals(2, clusters.size());
|
||||
assertEquals("c1", clusters.get(0).getId());
|
||||
assertEquals("c2", clusters.get(1).getId());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.veeam.api.dto.DataCenter;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.cloud.api.query.vo.DataCenterJoinVO;
|
||||
import com.cloud.org.Grouping;
|
||||
|
||||
public class DataCenterJoinVOToDataCenterConverterTest {
|
||||
|
||||
@Test
|
||||
public void testToDataCenter_MapsIdentityStatusAndLinks() {
|
||||
final DataCenterJoinVO zone = mock(DataCenterJoinVO.class);
|
||||
when(zone.getUuid()).thenReturn("dc-1");
|
||||
when(zone.getName()).thenReturn("zone-a");
|
||||
when(zone.getDescription()).thenReturn("desc-a");
|
||||
when(zone.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
|
||||
|
||||
final DataCenter dc = DataCenterJoinVOToDataCenterConverter.toDataCenter(zone);
|
||||
|
||||
assertEquals("dc-1", dc.getId());
|
||||
assertEquals("zone-a", dc.getName());
|
||||
assertEquals("desc-a", dc.getDescription());
|
||||
assertEquals("up", dc.getStatus());
|
||||
assertNotNull(dc.getVersion());
|
||||
assertNotNull(dc.getSupportedVersions());
|
||||
assertEquals(3, dc.getLink().size());
|
||||
assertEquals("cluster", dc.getLink().get(0).getRel());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToDataCenter_DisabledZoneMapsToDown() {
|
||||
final DataCenterJoinVO zone = mock(DataCenterJoinVO.class);
|
||||
when(zone.getUuid()).thenReturn("dc-2");
|
||||
when(zone.getName()).thenReturn("zone-b");
|
||||
when(zone.getDescription()).thenReturn("desc-b");
|
||||
when(zone.getAllocationState()).thenReturn(Grouping.AllocationState.Disabled);
|
||||
|
||||
final DataCenter dc = DataCenterJoinVOToDataCenterConverter.toDataCenter(zone);
|
||||
|
||||
assertEquals("down", dc.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToDcList_ConvertsAllItems() {
|
||||
final DataCenterJoinVO first = mock(DataCenterJoinVO.class);
|
||||
when(first.getUuid()).thenReturn("dc-1");
|
||||
when(first.getName()).thenReturn("zone-a");
|
||||
when(first.getDescription()).thenReturn("desc-a");
|
||||
when(first.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
|
||||
|
||||
final DataCenterJoinVO second = mock(DataCenterJoinVO.class);
|
||||
when(second.getUuid()).thenReturn("dc-2");
|
||||
when(second.getName()).thenReturn("zone-b");
|
||||
when(second.getDescription()).thenReturn("desc-b");
|
||||
when(second.getAllocationState()).thenReturn(Grouping.AllocationState.Disabled);
|
||||
|
||||
final List<DataCenter> result = DataCenterJoinVOToDataCenterConverter.toDCList(List.of(first, second));
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertEquals("dc-1", result.get(0).getId());
|
||||
assertEquals("dc-2", result.get(1).getId());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.veeam.api.dto.Host;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.cloud.api.query.vo.HostJoinVO;
|
||||
import com.cloud.host.Status;
|
||||
import com.cloud.resource.ResourceState;
|
||||
|
||||
public class HostJoinVOToHostConverterTest {
|
||||
|
||||
@Test
|
||||
public void testToHost_MapsUpHostAndCoreFields() {
|
||||
final HostJoinVO vo = mock(HostJoinVO.class);
|
||||
when(vo.getUuid()).thenReturn("host-1");
|
||||
when(vo.getName()).thenReturn("kvm-1");
|
||||
when(vo.getPrivateIpAddress()).thenReturn("10.10.10.11");
|
||||
when(vo.isInMaintenanceStates()).thenReturn(false);
|
||||
when(vo.getStatus()).thenReturn(Status.Up);
|
||||
when(vo.getResourceState()).thenReturn(ResourceState.Enabled);
|
||||
when(vo.getClusterUuid()).thenReturn("cl-1");
|
||||
when(vo.getSpeed()).thenReturn(2400L);
|
||||
when(vo.getCpuSockets()).thenReturn(2);
|
||||
when(vo.getCpus()).thenReturn(8);
|
||||
when(vo.getTotalMemory()).thenReturn(16000L);
|
||||
when(vo.getMemUsedCapacity()).thenReturn(4000L);
|
||||
|
||||
final Host host = HostJoinVOToHostConverter.toHost(vo);
|
||||
|
||||
assertEquals("host-1", host.getId());
|
||||
assertEquals("kvm-1", host.getName());
|
||||
assertEquals("10.10.10.11", host.getAddress());
|
||||
assertEquals("up", host.getStatus());
|
||||
assertEquals("cl-1", host.getCluster().getId());
|
||||
assertEquals("2400", host.getCpu().getSpeed());
|
||||
assertEquals("16000", host.getMemory());
|
||||
assertEquals("12000", host.getMaxSchedulingMemory());
|
||||
assertTrue(host.getHref().contains("/api/hosts/host-1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToHost_MapsMaintenanceAndFallbackName() {
|
||||
final HostJoinVO vo = mock(HostJoinVO.class);
|
||||
when(vo.getUuid()).thenReturn("host-2");
|
||||
when(vo.getName()).thenReturn(null);
|
||||
when(vo.getPrivateIpAddress()).thenReturn("10.10.10.12");
|
||||
when(vo.isInMaintenanceStates()).thenReturn(true);
|
||||
when(vo.getStatus()).thenReturn(Status.Down);
|
||||
when(vo.getResourceState()).thenReturn(ResourceState.Disabled);
|
||||
when(vo.getClusterUuid()).thenReturn("cl-2");
|
||||
when(vo.getSpeed()).thenReturn(2200L);
|
||||
when(vo.getCpuSockets()).thenReturn(1);
|
||||
when(vo.getCpus()).thenReturn(4);
|
||||
when(vo.getTotalMemory()).thenReturn(8000L);
|
||||
when(vo.getMemUsedCapacity()).thenReturn(2000L);
|
||||
|
||||
final Host host = HostJoinVOToHostConverter.toHost(vo);
|
||||
|
||||
assertEquals("host-host-2", host.getName());
|
||||
assertEquals("maintenance", host.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToHostList_ConvertsAllEntries() {
|
||||
final HostJoinVO first = mock(HostJoinVO.class);
|
||||
when(first.getUuid()).thenReturn("h1");
|
||||
when(first.getName()).thenReturn("h1");
|
||||
when(first.getPrivateIpAddress()).thenReturn("1.1.1.1");
|
||||
when(first.isInMaintenanceStates()).thenReturn(false);
|
||||
when(first.getStatus()).thenReturn(Status.Up);
|
||||
when(first.getResourceState()).thenReturn(ResourceState.Enabled);
|
||||
when(first.getClusterUuid()).thenReturn("c1");
|
||||
when(first.getSpeed()).thenReturn(1000L);
|
||||
when(first.getCpuSockets()).thenReturn(1);
|
||||
when(first.getCpus()).thenReturn(1);
|
||||
when(first.getTotalMemory()).thenReturn(1024L);
|
||||
when(first.getMemUsedCapacity()).thenReturn(24L);
|
||||
|
||||
final HostJoinVO second = mock(HostJoinVO.class);
|
||||
when(second.getUuid()).thenReturn("h2");
|
||||
when(second.getName()).thenReturn("h2");
|
||||
when(second.getPrivateIpAddress()).thenReturn("2.2.2.2");
|
||||
when(second.isInMaintenanceStates()).thenReturn(false);
|
||||
when(second.getStatus()).thenReturn(Status.Down);
|
||||
when(second.getResourceState()).thenReturn(ResourceState.Disabled);
|
||||
when(second.getClusterUuid()).thenReturn("c2");
|
||||
when(second.getSpeed()).thenReturn(2000L);
|
||||
when(second.getCpuSockets()).thenReturn(1);
|
||||
when(second.getCpus()).thenReturn(2);
|
||||
when(second.getTotalMemory()).thenReturn(2048L);
|
||||
when(second.getMemUsedCapacity()).thenReturn(48L);
|
||||
|
||||
final List<Host> hosts = HostJoinVOToHostConverter.toHostList(List.of(first, second));
|
||||
|
||||
assertEquals(2, hosts.size());
|
||||
assertEquals("h1", hosts.get(0).getId());
|
||||
assertEquals("h2", hosts.get(1).getId());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ImageTransferVOToImageTransferConverterTest {
|
||||
|
||||
@Test
|
||||
public void testConverterExposesExpectedApiMethods() {
|
||||
final java.lang.reflect.Method[] methods = ImageTransferVOToImageTransferConverter.class.getDeclaredMethods();
|
||||
boolean hasSingle = false;
|
||||
boolean hasList = false;
|
||||
for (java.lang.reflect.Method method : methods) {
|
||||
if ("toImageTransfer".equals(method.getName())) {
|
||||
hasSingle = true;
|
||||
}
|
||||
if ("toImageTransferList".equals(method.getName())) {
|
||||
hasList = true;
|
||||
}
|
||||
}
|
||||
assertTrue(hasSingle);
|
||||
assertTrue(hasList);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.veeam.api.dto.Network;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.cloud.api.query.vo.DataCenterJoinVO;
|
||||
import com.cloud.network.Networks;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
|
||||
public class NetworkVOToNetworkConverterTest {
|
||||
|
||||
@Test
|
||||
public void testToNetwork_MapsFieldsAndDataCenterRef() {
|
||||
final NetworkVO vo = mock(NetworkVO.class);
|
||||
when(vo.getUuid()).thenReturn("net-1");
|
||||
when(vo.getName()).thenReturn("guest-net");
|
||||
when(vo.getDisplayText()).thenReturn("Guest network");
|
||||
when(vo.getPrivateMtu()).thenReturn(1450);
|
||||
when(vo.getDataCenterId()).thenReturn(10L);
|
||||
|
||||
final DataCenterJoinVO dc = mock(DataCenterJoinVO.class);
|
||||
when(dc.getUuid()).thenReturn("dc-1");
|
||||
|
||||
final Network dto = NetworkVOToNetworkConverter.toNetwork(vo, id -> dc);
|
||||
|
||||
assertEquals("net-1", dto.getId());
|
||||
assertEquals("guest-net", dto.getName());
|
||||
assertEquals("Guest network", dto.getDescription());
|
||||
assertEquals("", dto.getComment());
|
||||
assertEquals("1450", dto.getMtu());
|
||||
assertEquals("false", dto.getPortIsolation());
|
||||
assertEquals("false", dto.getStp());
|
||||
assertEquals("guest-net", dto.getVdsmName());
|
||||
assertNotNull(dto.getUsages());
|
||||
assertEquals(1, dto.getUsages().getItems().size());
|
||||
assertEquals("vm", dto.getUsages().getItems().get(0));
|
||||
assertNotNull(dto.getLink());
|
||||
assertTrue(dto.getLink().isEmpty());
|
||||
assertEquals("dc-1", dto.getDataCenter().getId());
|
||||
assertTrue(dto.getHref().contains("/api/networks/net-1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToNetwork_UsesFallbackNameAndSkipsBlankDataCenterUuid() {
|
||||
final NetworkVO vo = mock(NetworkVO.class);
|
||||
when(vo.getUuid()).thenReturn("net-2");
|
||||
when(vo.getName()).thenReturn(null);
|
||||
when(vo.getTrafficType()).thenReturn(Networks.TrafficType.Guest);
|
||||
when(vo.getDisplayText()).thenReturn("Fallback network");
|
||||
when(vo.getPrivateMtu()).thenReturn(null);
|
||||
when(vo.getDataCenterId()).thenReturn(20L);
|
||||
|
||||
final DataCenterJoinVO dc = mock(DataCenterJoinVO.class);
|
||||
when(dc.getUuid()).thenReturn("");
|
||||
|
||||
final Network dto = NetworkVOToNetworkConverter.toNetwork(vo, id -> dc);
|
||||
|
||||
assertEquals("Guest-net-2", dto.getName());
|
||||
assertEquals("0", dto.getMtu());
|
||||
assertEquals("Guest-net-2", dto.getVdsmName());
|
||||
assertNull(dto.getDataCenter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToNetworkList_ConvertsAllItemsInOrder() {
|
||||
final NetworkVO first = mock(NetworkVO.class);
|
||||
when(first.getUuid()).thenReturn("net-1");
|
||||
when(first.getName()).thenReturn("first-net");
|
||||
when(first.getDisplayText()).thenReturn("First network");
|
||||
when(first.getPrivateMtu()).thenReturn(1400);
|
||||
|
||||
final NetworkVO second = mock(NetworkVO.class);
|
||||
when(second.getUuid()).thenReturn("net-2");
|
||||
when(second.getName()).thenReturn(null);
|
||||
when(second.getTrafficType()).thenReturn(Networks.TrafficType.Control);
|
||||
when(second.getDisplayText()).thenReturn("Second network");
|
||||
when(second.getPrivateMtu()).thenReturn(null);
|
||||
|
||||
final List<Network> result = NetworkVOToNetworkConverter.toNetworkList(List.of(first, second), null);
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertEquals("net-1", result.get(0).getId());
|
||||
assertEquals("first-net", result.get(0).getName());
|
||||
assertEquals("net-2", result.get(1).getId());
|
||||
assertEquals("Control-net-2", result.get(1).getName());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.veeam.api.dto.VnicProfile;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.cloud.api.query.vo.DataCenterJoinVO;
|
||||
import com.cloud.network.Networks;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
|
||||
public class NetworkVOToVnicProfileConverterTest {
|
||||
|
||||
@Test
|
||||
public void testToVnicProfile_MapsNetworkAndDataCenterRefs() {
|
||||
final NetworkVO vo = mock(NetworkVO.class);
|
||||
when(vo.getUuid()).thenReturn("net-3");
|
||||
when(vo.getName()).thenReturn("profile-net");
|
||||
when(vo.getDisplayText()).thenReturn("Profile network");
|
||||
when(vo.getDataCenterId()).thenReturn(30L);
|
||||
|
||||
final DataCenterJoinVO dc = mock(DataCenterJoinVO.class);
|
||||
when(dc.getUuid()).thenReturn("dc-3");
|
||||
|
||||
final VnicProfile profile = NetworkVOToVnicProfileConverter.toVnicProfile(vo, id -> dc);
|
||||
|
||||
assertEquals("net-3", profile.getId());
|
||||
assertEquals("profile-net", profile.getName());
|
||||
assertEquals("Profile network", profile.getDescription());
|
||||
assertEquals("net-3", profile.getNetwork().getId());
|
||||
assertEquals("dc-3", profile.getDataCenter().getId());
|
||||
assertTrue(profile.getHref().contains("/api/vnicprofiles/net-3"));
|
||||
assertTrue(profile.getNetwork().getHref().contains("/api/networks/net-3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToVnicProfile_UsesFallbackNameAndOmitsBlankDataCenterUuid() {
|
||||
final NetworkVO vo = mock(NetworkVO.class);
|
||||
when(vo.getUuid()).thenReturn("net-4");
|
||||
when(vo.getName()).thenReturn(null);
|
||||
when(vo.getTrafficType()).thenReturn(Networks.TrafficType.Management);
|
||||
when(vo.getDisplayText()).thenReturn("Mgmt network");
|
||||
when(vo.getDataCenterId()).thenReturn(40L);
|
||||
|
||||
final DataCenterJoinVO dc = mock(DataCenterJoinVO.class);
|
||||
when(dc.getUuid()).thenReturn("");
|
||||
|
||||
final VnicProfile profile = NetworkVOToVnicProfileConverter.toVnicProfile(vo, id -> dc);
|
||||
|
||||
assertEquals("Management-net-4", profile.getName());
|
||||
assertEquals("Mgmt network", profile.getDescription());
|
||||
assertEquals("net-4", profile.getNetwork().getId());
|
||||
assertNull(profile.getDataCenter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToVnicProfileList_ConvertsAllItemsInOrder() {
|
||||
final NetworkVO first = mock(NetworkVO.class);
|
||||
when(first.getUuid()).thenReturn("net-1");
|
||||
when(first.getName()).thenReturn("profile-a");
|
||||
when(first.getDisplayText()).thenReturn("Profile A");
|
||||
|
||||
final NetworkVO second = mock(NetworkVO.class);
|
||||
when(second.getUuid()).thenReturn("net-2");
|
||||
when(second.getName()).thenReturn(null);
|
||||
when(second.getTrafficType()).thenReturn(Networks.TrafficType.Control);
|
||||
when(second.getDisplayText()).thenReturn("Profile B");
|
||||
|
||||
final List<VnicProfile> result = NetworkVOToVnicProfileConverter.toVnicProfileList(List.of(first, second), null);
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertEquals("net-1", result.get(0).getId());
|
||||
assertEquals("profile-a", result.get(0).getName());
|
||||
assertEquals("net-2", result.get(1).getId());
|
||||
assertEquals("Control-net-2", result.get(1).getName());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.apache.cloudstack.veeam.api.dto.Nic;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
|
||||
public class NicVOToNicConverterTest {
|
||||
|
||||
@Test
|
||||
public void testToNic_MapsVmHrefIpAndVnicProfile() {
|
||||
final com.cloud.vm.NicVO vo = mock(com.cloud.vm.NicVO.class);
|
||||
when(vo.getUuid()).thenReturn("nic-1");
|
||||
when(vo.getReserver()).thenReturn("eth0");
|
||||
when(vo.getMacAddress()).thenReturn("02:00:00:00:00:01");
|
||||
when(vo.getIPv4Address()).thenReturn("10.1.1.10");
|
||||
when(vo.getIPv4Gateway()).thenReturn("10.1.1.1");
|
||||
when(vo.getNetworkId()).thenReturn(10L);
|
||||
|
||||
final NetworkVO network = mock(NetworkVO.class);
|
||||
when(network.getUuid()).thenReturn("net-1");
|
||||
|
||||
final Nic nic = NicVOToNicConverter.toNic(vo, "vm-1", id -> network);
|
||||
|
||||
assertEquals("nic-1", nic.getId());
|
||||
assertEquals("virtio", nic.getInterfaceType());
|
||||
assertEquals("vm-1", nic.getVm().getId());
|
||||
assertTrue(nic.getHref().contains("/api/vms/vm-1/nics/nic-1"));
|
||||
assertEquals("net-1", nic.getVnicProfile().getId());
|
||||
assertNotNull(nic.getReportedDevices());
|
||||
assertEquals("v4", nic.getReportedDevices().getItems().get(0).getIps().getItems().get(0).getVersion());
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testToNic_BlankVmUuidCurrentBehaviorThrowsNpe() {
|
||||
final com.cloud.vm.NicVO vo = mock(com.cloud.vm.NicVO.class);
|
||||
when(vo.getUuid()).thenReturn("nic-2");
|
||||
when(vo.getReserver()).thenReturn("eth1");
|
||||
when(vo.getMacAddress()).thenReturn("02:00:00:00:00:02");
|
||||
|
||||
NicVOToNicConverter.toNic(vo, "", null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.veeam.api.dto.BaseDto;
|
||||
import org.apache.cloudstack.veeam.api.dto.Tag;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.cloud.server.ResourceTag;
|
||||
import com.cloud.tags.ResourceTagVO;
|
||||
|
||||
public class ResourceTagVOToTagConverterTest {
|
||||
|
||||
@Test
|
||||
public void testGetRootTagAndRootRef() {
|
||||
final Tag root = ResourceTagVOToTagConverter.getRootTag();
|
||||
|
||||
assertEquals(BaseDto.ZERO_UUID, root.getId());
|
||||
assertEquals("root", root.getName());
|
||||
assertNotNull(ResourceTagVOToTagConverter.getRootTagRef().getHref());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToTag_FromResourceTagVoWithVmReference() {
|
||||
final ResourceTagVO vo = mock(ResourceTagVO.class);
|
||||
when(vo.getKey()).thenReturn("env");
|
||||
when(vo.getValue()).thenReturn("prod");
|
||||
when(vo.getResourceType()).thenReturn(ResourceTag.ResourceObjectType.UserVm);
|
||||
when(vo.getResourceUuid()).thenReturn("vm-1");
|
||||
|
||||
final Tag tag = ResourceTagVOToTagConverter.toTag(vo);
|
||||
|
||||
assertEquals("prod", tag.getId());
|
||||
assertEquals("prod", tag.getName());
|
||||
assertNotNull(tag.getParent());
|
||||
assertNotNull(tag.getVm());
|
||||
assertEquals("vm-1", tag.getVm().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToTag_FromResourceTagVoWithoutVmReference() {
|
||||
final ResourceTagVO vo = mock(ResourceTagVO.class);
|
||||
when(vo.getKey()).thenReturn("scope");
|
||||
when(vo.getValue()).thenReturn("global");
|
||||
when(vo.getResourceType()).thenReturn(ResourceTag.ResourceObjectType.Volume);
|
||||
|
||||
final Tag tag = ResourceTagVOToTagConverter.toTag(vo);
|
||||
|
||||
assertEquals("global", tag.getId());
|
||||
assertNull(tag.getVm());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToTagsAndToTagsFromValues() {
|
||||
final ResourceTagVO vo = mock(ResourceTagVO.class);
|
||||
when(vo.getKey()).thenReturn("team");
|
||||
when(vo.getValue()).thenReturn("ops");
|
||||
when(vo.getResourceType()).thenReturn(ResourceTag.ResourceObjectType.UserVm);
|
||||
when(vo.getResourceUuid()).thenReturn("vm-2");
|
||||
|
||||
assertEquals(1, ResourceTagVOToTagConverter.toTags(List.of(vo)).size());
|
||||
assertEquals("ops", ResourceTagVOToTagConverter.toTagsFromValues(List.of("ops")).get(0).getId());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.veeam.api.dto.StorageDomain;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.cloud.api.query.vo.ImageStoreJoinVO;
|
||||
import com.cloud.api.query.vo.StoragePoolJoinVO;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.StoragePoolStatus;
|
||||
|
||||
public class StoreVOToStorageDomainConverterTest {
|
||||
|
||||
@Test
|
||||
public void testToStorageDomain_FromPrimaryPool() {
|
||||
final StoragePoolJoinVO pool = mock(StoragePoolJoinVO.class);
|
||||
when(pool.getUuid()).thenReturn("pool-1");
|
||||
when(pool.getName()).thenReturn("Primary-1");
|
||||
when(pool.getCapacityBytes()).thenReturn(1000L);
|
||||
when(pool.getUsedBytes()).thenReturn(250L);
|
||||
when(pool.getStatus()).thenReturn(StoragePoolStatus.Up);
|
||||
when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(pool.getZoneUuid()).thenReturn("dc-1");
|
||||
|
||||
final StorageDomain sd = StoreVOToStorageDomainConverter.toStorageDomain(pool);
|
||||
|
||||
assertEquals("pool-1", sd.getId());
|
||||
assertEquals("data", sd.getType());
|
||||
assertEquals("active", sd.getStatus());
|
||||
assertEquals("750", sd.getAvailable());
|
||||
assertEquals("250", sd.getUsed());
|
||||
assertEquals("1000", sd.getCommitted());
|
||||
assertEquals("nfs", sd.getStorage().getType());
|
||||
assertEquals("dc-1", sd.getDataCenters().getItems().get(0).getId());
|
||||
assertTrue(sd.getLink().stream().anyMatch(l -> "disks".equals(l.getRel())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToStorageDomain_FromImageStore() {
|
||||
final ImageStoreJoinVO store = mock(ImageStoreJoinVO.class);
|
||||
when(store.getUuid()).thenReturn("img-1");
|
||||
when(store.getName()).thenReturn("Secondary-1");
|
||||
when(store.getProviderName()).thenReturn("glance");
|
||||
when(store.getZoneUuid()).thenReturn("dc-2");
|
||||
|
||||
final StorageDomain sd = StoreVOToStorageDomainConverter.toStorageDomain(store);
|
||||
|
||||
assertEquals("img-1", sd.getId());
|
||||
assertEquals("image", sd.getType());
|
||||
assertEquals("unattached", sd.getStatus());
|
||||
assertEquals("glance", sd.getStorage().getType());
|
||||
assertEquals("dc-2", sd.getDataCenters().getItems().get(0).getId());
|
||||
assertTrue(sd.getLink().stream().anyMatch(l -> "images".equals(l.getRel())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListConverters() {
|
||||
final StoragePoolJoinVO pool = mock(StoragePoolJoinVO.class);
|
||||
when(pool.getUuid()).thenReturn("p");
|
||||
when(pool.getName()).thenReturn("P");
|
||||
when(pool.getCapacityBytes()).thenReturn(10L);
|
||||
when(pool.getUsedBytes()).thenReturn(2L);
|
||||
when(pool.getStatus()).thenReturn(StoragePoolStatus.Up);
|
||||
when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.Filesystem);
|
||||
when(pool.getZoneUuid()).thenReturn("z");
|
||||
|
||||
final ImageStoreJoinVO store = mock(ImageStoreJoinVO.class);
|
||||
when(store.getUuid()).thenReturn("s");
|
||||
when(store.getName()).thenReturn("S");
|
||||
when(store.getProviderName()).thenReturn("glance");
|
||||
when(store.getZoneUuid()).thenReturn("z");
|
||||
|
||||
assertEquals(1, StoreVOToStorageDomainConverter.toStorageDomainListFromPools(List.of(pool)).size());
|
||||
assertEquals(1, StoreVOToStorageDomainConverter.toStorageDomainListFromStores(List.of(store)).size());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.veeam.api.dto.DiskAttachment;
|
||||
import org.apache.cloudstack.veeam.api.dto.Nic;
|
||||
import org.apache.cloudstack.veeam.api.dto.Tag;
|
||||
import org.apache.cloudstack.veeam.api.dto.Vm;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import com.cloud.api.query.vo.HostJoinVO;
|
||||
import com.cloud.api.query.vo.UserVmJoinVO;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
|
||||
public class UserVmJoinVOToVmConverterTest {
|
||||
|
||||
@Test
|
||||
public void testToVm_MapsUpStateWithBasicFields() {
|
||||
final UserVmJoinVO src = mock(UserVmJoinVO.class);
|
||||
when(src.getId()).thenReturn(101L);
|
||||
when(src.getUuid()).thenReturn("vm-1");
|
||||
when(src.getName()).thenReturn("vm-1-name");
|
||||
when(src.getDisplayName()).thenReturn("vm-1-display");
|
||||
when(src.getInstanceName()).thenReturn("i-101");
|
||||
when(src.getState()).thenReturn(VirtualMachine.State.Running);
|
||||
when(src.getCreated()).thenReturn(new Date(1000L));
|
||||
when(src.getLastUpdated()).thenReturn(new Date(2000L));
|
||||
when(src.getTemplateUuid()).thenReturn("tmpl-1");
|
||||
when(src.getHostUuid()).thenReturn("host-1");
|
||||
when(src.getRamSize()).thenReturn(512);
|
||||
when(src.getArch()).thenReturn("x86_64");
|
||||
when(src.getCpu()).thenReturn(2);
|
||||
when(src.getGuestOsDisplayName()).thenReturn("Linux");
|
||||
when(src.getServiceOfferingUuid()).thenReturn("offering-1");
|
||||
when(src.getAccountUuid()).thenReturn("acct-1");
|
||||
when(src.getAffinityGroupUuid()).thenReturn("ag-1");
|
||||
when(src.getUserDataUuid()).thenReturn("ud-1");
|
||||
|
||||
final Vm vm = UserVmJoinVOToVmConverter.toVm(src, null, null, null, null, null, false);
|
||||
|
||||
assertEquals("vm-1", vm.getId());
|
||||
assertEquals("vm-1-name", vm.getName());
|
||||
assertEquals("vm-1-display", vm.getDescription());
|
||||
assertEquals("up", vm.getStatus());
|
||||
assertEquals(Long.valueOf(1000L), vm.getCreationTime());
|
||||
assertEquals(Long.valueOf(2000L), vm.getStartTime());
|
||||
assertNull(vm.getStopTime());
|
||||
assertEquals("536870912", vm.getMemory());
|
||||
assertEquals("x86_64", vm.getCpu().getArchitecture());
|
||||
assertEquals("host-1", vm.getHost().getId());
|
||||
assertNotNull(vm.getActions());
|
||||
assertEquals(3, vm.getActions().getItems().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToVm_UsesResolversAndMapsDownState() {
|
||||
final UserVmJoinVO src = mock(UserVmJoinVO.class);
|
||||
when(src.getId()).thenReturn(202L);
|
||||
when(src.getUuid()).thenReturn("vm-2");
|
||||
when(src.getName()).thenReturn("vm-2-name");
|
||||
when(src.getDisplayName()).thenReturn("vm-2-display");
|
||||
when(src.getInstanceName()).thenReturn("i-202");
|
||||
when(src.getState()).thenReturn(VirtualMachine.State.Stopped);
|
||||
when(src.getCreated()).thenReturn(new Date(3000L));
|
||||
when(src.getLastUpdated()).thenReturn(new Date(4000L));
|
||||
when(src.getTemplateUuid()).thenReturn("tmpl-2");
|
||||
when(src.getHostUuid()).thenReturn(null);
|
||||
when(src.getHostId()).thenReturn(22L);
|
||||
when(src.getLastHostId()).thenReturn(null);
|
||||
when(src.getRamSize()).thenReturn(1024);
|
||||
when(src.getArch()).thenReturn("x86_64");
|
||||
when(src.getCpu()).thenReturn(4);
|
||||
when(src.getGuestOsDisplayName()).thenReturn("Linux");
|
||||
when(src.getServiceOfferingUuid()).thenReturn("offering-2");
|
||||
when(src.getAccountUuid()).thenReturn("acct-2");
|
||||
when(src.getAffinityGroupUuid()).thenReturn("ag-2");
|
||||
when(src.getUserDataUuid()).thenReturn("ud-2");
|
||||
|
||||
final HostJoinVO hostVo = mock(HostJoinVO.class);
|
||||
when(hostVo.getUuid()).thenReturn("host-2");
|
||||
when(hostVo.getClusterUuid()).thenReturn("cluster-2");
|
||||
|
||||
final Tag tag = new Tag();
|
||||
tag.setId("tag-1");
|
||||
|
||||
final DiskAttachment disk = new DiskAttachment();
|
||||
disk.setId("da-1");
|
||||
|
||||
final Nic nic = new Nic();
|
||||
nic.setId("nic-1");
|
||||
|
||||
final Vm vm = UserVmJoinVOToVmConverter.toVm(
|
||||
src,
|
||||
id -> hostVo,
|
||||
id -> Map.of(ApiConstants.BootType.UEFI.toString(), "true"),
|
||||
id -> List.of(tag),
|
||||
id -> List.of(disk),
|
||||
ignored -> List.of(nic),
|
||||
false);
|
||||
|
||||
assertEquals("down", vm.getStatus());
|
||||
assertEquals(Long.valueOf(4000L), vm.getStopTime());
|
||||
assertEquals("host-2", vm.getHost().getId());
|
||||
assertEquals("cluster-2", vm.getCluster().getId());
|
||||
assertEquals(1, vm.getTags().getItems().size());
|
||||
assertEquals(1, vm.getDiskAttachments().getItems().size());
|
||||
assertEquals(1, vm.getNics().getItems().size());
|
||||
assertEquals("acct-2", vm.getAccountId());
|
||||
assertEquals("ag-2", vm.getAffinityGroupId());
|
||||
assertEquals("ud-2", vm.getUserDataId());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.apache.cloudstack.veeam.api.dto.Checkpoint;
|
||||
import org.junit.Test;
|
||||
|
||||
public class UserVmVOToCheckpointConverterTest {
|
||||
|
||||
@Test
|
||||
public void testToCheckpoint_ReturnsNullWhenCheckpointIdMissing() {
|
||||
assertNull(UserVmVOToCheckpointConverter.toCheckpoint(null, "10"));
|
||||
assertNull(UserVmVOToCheckpointConverter.toCheckpoint("", "10"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToCheckpoint_ParsesCreationTimeWhenProvided() {
|
||||
final Checkpoint checkpoint = UserVmVOToCheckpointConverter.toCheckpoint("chk-1", "1700000000");
|
||||
|
||||
assertNotNull(checkpoint);
|
||||
assertEquals("chk-1", checkpoint.getId());
|
||||
assertEquals("chk-1", checkpoint.getName());
|
||||
assertEquals(String.valueOf(1700000000L * 1000L), checkpoint.getCreationDate());
|
||||
assertEquals("created", checkpoint.getState());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToCheckpoint_UsesNowWhenCreateTimeInvalid() {
|
||||
final long before = System.currentTimeMillis();
|
||||
final Checkpoint checkpoint = UserVmVOToCheckpointConverter.toCheckpoint("chk-2", "not-a-number");
|
||||
final long after = System.currentTimeMillis();
|
||||
|
||||
final long creation = Long.parseLong(checkpoint.getCreationDate());
|
||||
assertTrue(creation >= before && creation <= after);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.veeam.api.dto.Snapshot;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.cloud.vm.snapshot.VMSnapshot;
|
||||
import com.cloud.vm.snapshot.VMSnapshotVO;
|
||||
|
||||
public class VmSnapshotVOToSnapshotConverterTest {
|
||||
|
||||
@Test
|
||||
public void testToSnapshot_MapsReadyDiskAndMemorySnapshot() {
|
||||
final VMSnapshotVO vo = mock(VMSnapshotVO.class);
|
||||
when(vo.getUuid()).thenReturn("snap-1");
|
||||
when(vo.getDescription()).thenReturn("desc");
|
||||
when(vo.getCreated()).thenReturn(new Date(1234L));
|
||||
when(vo.getType()).thenReturn(VMSnapshotVO.Type.DiskAndMemory);
|
||||
when(vo.getState()).thenReturn(VMSnapshot.State.Ready);
|
||||
|
||||
final Snapshot snapshot = VmSnapshotVOToSnapshotConverter.toSnapshot(vo, "vm-1");
|
||||
|
||||
assertEquals("snap-1", snapshot.getId());
|
||||
assertTrue(snapshot.getHref().contains("/api/vms/vm-1/snapshots/snap-1"));
|
||||
assertEquals("vm-1", snapshot.getVm().getId());
|
||||
assertEquals("desc", snapshot.getDescription());
|
||||
assertEquals(Long.valueOf(1234L), snapshot.getDate());
|
||||
assertEquals("true", snapshot.getPersistMemorystate());
|
||||
assertEquals("ok", snapshot.getSnapshotStatus());
|
||||
assertEquals("link", snapshot.getActions().asMap().keySet().iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToSnapshot_MapsNonReadyToLockedAndToSnapshotList() {
|
||||
final VMSnapshotVO vo = mock(VMSnapshotVO.class);
|
||||
when(vo.getUuid()).thenReturn("snap-2");
|
||||
when(vo.getDescription()).thenReturn("desc2");
|
||||
when(vo.getCreated()).thenReturn(new Date(5678L));
|
||||
when(vo.getType()).thenReturn(VMSnapshotVO.Type.Disk);
|
||||
when(vo.getState()).thenReturn(VMSnapshot.State.Creating);
|
||||
|
||||
final Snapshot snapshot = VmSnapshotVOToSnapshotConverter.toSnapshot(vo, "vm-2");
|
||||
assertEquals("false", snapshot.getPersistMemorystate());
|
||||
assertEquals("locked", snapshot.getSnapshotStatus());
|
||||
|
||||
final List<Snapshot> list = VmSnapshotVOToSnapshotConverter.toSnapshotList(List.of(vo), "vm-2");
|
||||
assertEquals(1, list.size());
|
||||
assertEquals("snap-2", list.get(0).getId());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
// 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.cloudstack.veeam.api.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.backup.Backup;
|
||||
import org.apache.cloudstack.veeam.api.dto.Disk;
|
||||
import org.apache.cloudstack.veeam.api.dto.DiskAttachment;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.cloud.api.query.vo.VolumeJoinVO;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.Volume;
|
||||
|
||||
public class VolumeJoinVOToDiskConverterTest {
|
||||
|
||||
@Test
|
||||
public void testToDisk_MapsCoreFieldsAndResolverOverridesActualSize() {
|
||||
final VolumeJoinVO vol = mock(VolumeJoinVO.class);
|
||||
when(vol.getUuid()).thenReturn("vol-1");
|
||||
when(vol.getVolumeType()).thenReturn(Volume.Type.ROOT);
|
||||
when(vol.getName()).thenReturn("root-disk");
|
||||
when(vol.getSize()).thenReturn(1000L);
|
||||
when(vol.getVolumeStoreSize()).thenReturn(500L);
|
||||
when(vol.getFormat()).thenReturn(Storage.ImageFormat.RAW);
|
||||
when(vol.getState()).thenReturn(Volume.State.Ready);
|
||||
when(vol.getPath()).thenReturn("path-1");
|
||||
when(vol.getDiskOfferingUuid()).thenReturn("do-1");
|
||||
when(vol.getPoolUuid()).thenReturn("pool-1");
|
||||
|
||||
final Disk disk = VolumeJoinVOToDiskConverter.toDisk(vol, v -> 700L);
|
||||
|
||||
assertEquals("vol-1", disk.getId());
|
||||
assertEquals("true", disk.getBootable());
|
||||
assertEquals("raw", disk.getFormat());
|
||||
assertEquals("ok", disk.getStatus());
|
||||
assertEquals("700", disk.getActualSize());
|
||||
assertNotNull(disk.getStorageDomains());
|
||||
assertTrue(disk.getHref().contains("/api/disks/vol-1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToDiskAttachment_MapsVmAndDisk() {
|
||||
final VolumeJoinVO vol = mock(VolumeJoinVO.class);
|
||||
when(vol.getUuid()).thenReturn("vol-2");
|
||||
when(vol.getVmUuid()).thenReturn("vm-2");
|
||||
when(vol.getVolumeType()).thenReturn(Volume.Type.DATADISK);
|
||||
when(vol.getName()).thenReturn("data-disk");
|
||||
when(vol.getSize()).thenReturn(2048L);
|
||||
when(vol.getVolumeStoreSize()).thenReturn(1024L);
|
||||
when(vol.getFormat()).thenReturn(Storage.ImageFormat.QCOW2);
|
||||
when(vol.getState()).thenReturn(Volume.State.Allocated);
|
||||
when(vol.getPath()).thenReturn("path-2");
|
||||
when(vol.getDiskOfferingUuid()).thenReturn("do-2");
|
||||
when(vol.getPoolUuid()).thenReturn("pool-2");
|
||||
|
||||
final DiskAttachment da = VolumeJoinVOToDiskConverter.toDiskAttachment(vol, null);
|
||||
|
||||
assertEquals("vol-2", da.getId());
|
||||
assertEquals("vm-2", da.getVm().getId());
|
||||
assertEquals("false", da.getBootable());
|
||||
assertEquals("virtio_scsi", da.getIface());
|
||||
assertEquals("vol-2", da.getDisk().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToDiskListFromVolumeInfos_MapsBootableByVolumeType() {
|
||||
final Backup.VolumeInfo root = new Backup.VolumeInfo("root-id", "p1", Volume.Type.ROOT, 10L, 0L, "do", 0L, 0L);
|
||||
final Backup.VolumeInfo data = new Backup.VolumeInfo("data-id", "p2", Volume.Type.DATADISK, 20L, 1L, "do", 0L, 0L);
|
||||
|
||||
final List<Disk> result = VolumeJoinVOToDiskConverter.toDiskListFromVolumeInfos(List.of(root, data));
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertEquals("true", result.get(0).getBootable());
|
||||
assertEquals("false", result.get(1).getBootable());
|
||||
assertEquals("20", result.get(1).getTotalSize());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// 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.cloudstack.veeam.api.request;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ListQueryTest {
|
||||
|
||||
@Test
|
||||
public void testFromRequest_WithNoParametersReturnsDefaults() {
|
||||
final HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
when(request.getParameterMap()).thenReturn(Collections.emptyMap());
|
||||
|
||||
final ListQuery query = ListQuery.fromRequest(request);
|
||||
|
||||
assertFalse(query.isAllContent());
|
||||
assertNull(query.getLimit());
|
||||
assertNull(query.getOffset());
|
||||
assertFalse(query.followContains("tags"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromRequest_ParsesAllContentMaxAndFollow() {
|
||||
final HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
when(request.getParameterMap()).thenReturn(Map.of(
|
||||
"all_content", new String[]{"true"},
|
||||
"max", new String[]{"25"},
|
||||
"follow", new String[]{"tags, disk_attachments.disk, ,nics.reporteddevices"}
|
||||
));
|
||||
when(request.getParameter("all_content")).thenReturn("true");
|
||||
when(request.getParameter("max")).thenReturn("25");
|
||||
when(request.getParameter("follow")).thenReturn("tags, disk_attachments.disk, ,nics.reporteddevices");
|
||||
when(request.getParameter("search")).thenReturn(null);
|
||||
|
||||
final ListQuery query = ListQuery.fromRequest(request);
|
||||
|
||||
assertTrue(query.isAllContent());
|
||||
assertTrue(query.followContains("tags"));
|
||||
assertTrue(query.followContains("disk_attachments.disk"));
|
||||
assertTrue(query.followContains("nics.reporteddevices"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromRequest_SearchParserIgnoresNonEqualsAndUsesPageValueAsMaxCurrentBehavior() {
|
||||
final HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
when(request.getParameterMap()).thenReturn(Map.of("search", new String[]{"name=vm and page=3 and status!=down and x>=1"}));
|
||||
when(request.getParameter("all_content")).thenReturn(null);
|
||||
when(request.getParameter("max")).thenReturn(null);
|
||||
when(request.getParameter("follow")).thenReturn(null);
|
||||
when(request.getParameter("search")).thenReturn("name=vm and page=3 and status!=down and x>=1");
|
||||
|
||||
final ListQuery query = ListQuery.fromRequest(request);
|
||||
|
||||
// Document existing behavior: when search contains page=..., max is set from it.
|
||||
org.junit.Assert.assertEquals(Long.valueOf(3L), query.getLimit());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOffset_UsesPageAndMax() {
|
||||
final ListQuery query = new ListQuery();
|
||||
query.page = 3L;
|
||||
query.max = 10L;
|
||||
|
||||
org.junit.Assert.assertEquals(Long.valueOf(20L), query.getOffset());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
// 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.cloudstack.veeam.filter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.apache.cloudstack.veeam.VeeamControlService;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AllowedClientCidrsFilterTest {
|
||||
|
||||
@Mock
|
||||
private VeeamControlService veeamControlService;
|
||||
|
||||
@Mock
|
||||
private FilterChain chain;
|
||||
|
||||
@Test
|
||||
public void testDoFilterAllowsNonHttpRequestsToPassThrough() throws Exception {
|
||||
final AllowedClientCidrsFilter filter = new AllowedClientCidrsFilter(veeamControlService);
|
||||
final ServletRequest request = mock(ServletRequest.class);
|
||||
final ServletResponse response = mock(ServletResponse.class);
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
verify(chain).doFilter(request, response);
|
||||
verifyNoInteractions(veeamControlService);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoFilterRejectsWhenServiceIsUnavailable() throws Exception {
|
||||
final AllowedClientCidrsFilter filter = new AllowedClientCidrsFilter(null);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
assertEquals(503, response.getStatus());
|
||||
assertEquals("Service Unavailable", response.getErrorMessage());
|
||||
verifyNoInteractions(chain);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoFilterAllowsRequestWhenNoCidrsConfigured() throws Exception {
|
||||
final AllowedClientCidrsFilter filter = new AllowedClientCidrsFilter(veeamControlService);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
when(veeamControlService.getAllowedClientCidrs()).thenReturn(Collections.emptyList());
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
verify(chain).doFilter(request, response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoFilterAllowsRequestFromConfiguredCidr() throws Exception {
|
||||
final AllowedClientCidrsFilter filter = new AllowedClientCidrsFilter(veeamControlService);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setRemoteAddr("192.168.10.25");
|
||||
when(veeamControlService.getAllowedClientCidrs()).thenReturn(List.of("192.168.10.0/24"));
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
verify(chain).doFilter(request, response);
|
||||
assertEquals(200, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoFilterRejectsRequestOutsideConfiguredCidrs() throws Exception {
|
||||
final AllowedClientCidrsFilter filter = new AllowedClientCidrsFilter(veeamControlService);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setRemoteAddr("10.10.10.10");
|
||||
when(veeamControlService.getAllowedClientCidrs()).thenReturn(List.of("192.168.10.0/24"));
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
assertEquals(403, response.getStatus());
|
||||
assertEquals("Forbidden", response.getErrorMessage());
|
||||
verifyNoInteractions(chain);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoFilterRejectsMalformedRemoteAddress() throws Exception {
|
||||
final AllowedClientCidrsFilter filter = new AllowedClientCidrsFilter(veeamControlService);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setRemoteAddr("not-a-valid-ip-address");
|
||||
when(veeamControlService.getAllowedClientCidrs()).thenReturn(List.of("192.168.10.0/24"));
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
assertEquals(403, response.getStatus());
|
||||
assertEquals("Forbidden", response.getErrorMessage());
|
||||
verifyNoInteractions(chain);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
// 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.cloudstack.veeam.filter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
|
||||
import org.apache.cloudstack.veeam.VeeamControlService;
|
||||
import org.apache.cloudstack.veeam.utils.JwtUtil;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class BearerOrBasicAuthFilterTest {
|
||||
|
||||
private static final String SECRET = "very-secret";
|
||||
|
||||
@Mock
|
||||
private VeeamControlService veeamControlService;
|
||||
|
||||
@Mock
|
||||
private FilterChain chain;
|
||||
|
||||
@Test
|
||||
public void testDoFilterRejectsMissingAuthorizationWithJsonPayload() throws Exception {
|
||||
final BearerOrBasicAuthFilter filter = new BearerOrBasicAuthFilter(veeamControlService);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.addHeader("Accept", "application/json");
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
assertEquals(401, response.getStatus());
|
||||
assertEquals("application/json; charset=UTF-8", response.getContentType());
|
||||
assertNotNull(response.getHeader("WWW-Authenticate"));
|
||||
assertTrue(response.getHeader("WWW-Authenticate").contains("error=\"invalid_token\""));
|
||||
assertTrue(response.getContentAsString().contains("\"error\":\"invalid_token\""));
|
||||
assertTrue(response.getContentAsString().contains("Missing Authorization"));
|
||||
verifyNoInteractions(chain);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoFilterRejectsInvalidBearerToken() throws Exception {
|
||||
final BearerOrBasicAuthFilter filter = new BearerOrBasicAuthFilter(veeamControlService);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.addHeader("Accept", "application/json");
|
||||
request.addHeader("Authorization", "Bearer not-a-jwt");
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
assertEquals(401, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("Invalid or expired token"));
|
||||
verifyNoInteractions(chain);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoFilterAllowsValidBearerTokenWithRequiredScopes() throws Exception {
|
||||
final BearerOrBasicAuthFilter filter = new BearerOrBasicAuthFilter(veeamControlService);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
final String token = JwtUtil.issueHs256Jwt("service-user", "ovirt-app-admin ovirt-app-portal", 60L, SECRET);
|
||||
request.addHeader("Authorization", "Bearer " + token);
|
||||
when(veeamControlService.getHmacSecret()).thenReturn(SECRET);
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
verify(chain).doFilter(request, response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoFilterAllowsValidBasicCredentials() throws Exception {
|
||||
final BearerOrBasicAuthFilter filter = new BearerOrBasicAuthFilter(veeamControlService);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
final String credentials = Base64.getEncoder().encodeToString("veeam:secret".getBytes(StandardCharsets.UTF_8));
|
||||
request.addHeader("Authorization", "Basic " + credentials);
|
||||
when(veeamControlService.validateCredentials("veeam", "secret")).thenReturn(true);
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
verify(chain).doFilter(request, response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoFilterRejectsInvalidBasicCredentialsWithHtmlPayload() throws Exception {
|
||||
final BearerOrBasicAuthFilter filter = new BearerOrBasicAuthFilter(veeamControlService);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.addHeader("Authorization", "Basic !!!not-base64!!!");
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
assertEquals(401, response.getStatus());
|
||||
assertEquals("text/html; charset=UTF-8", response.getContentType());
|
||||
assertTrue(response.getContentAsString().contains("Unauthorized"));
|
||||
assertNotNull(response.getHeader("WWW-Authenticate"));
|
||||
assertTrue(response.getHeader("WWW-Authenticate").contains("invalid_client"));
|
||||
verifyNoInteractions(chain);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
// 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.cloudstack.veeam.services;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.cloudstack.ca.CAManager;
|
||||
import org.apache.cloudstack.veeam.VeeamControlServlet;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PkiResourceRouteHandlerTest {
|
||||
|
||||
@Test
|
||||
public void testCanHandleSanitizesQueryParameters() {
|
||||
final PkiResourceRouteHandler handler = new PkiResourceRouteHandler();
|
||||
|
||||
assertTrue(handler.canHandle("GET", "/services/pki-resource?resource=ca-certificate"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleReturnsCertificateDownload() throws Exception {
|
||||
final PkiResourceRouteHandler handler = new PkiResourceRouteHandler();
|
||||
final CAManager caManager = mock(CAManager.class);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/services/pki-resource");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
final String certificate = "-----BEGIN CERTIFICATE-----\nveeam\n-----END CERTIFICATE-----\n";
|
||||
handler.caManager = caManager;
|
||||
when(caManager.getCaCertificate(null)).thenReturn(certificate);
|
||||
|
||||
handler.handle(request, response, "/services/pki-resource", Negotiation.OutFormat.JSON,
|
||||
new VeeamControlServlet(Collections.emptyList()));
|
||||
|
||||
assertEquals(200, response.getStatus());
|
||||
assertEquals("no-store", response.getHeader("Cache-Control"));
|
||||
assertEquals("attachment; filename=\"pki-resource.cer\"", response.getHeader("Content-Disposition"));
|
||||
assertEquals("application/x-x509-ca-cert; charset=ISO-8859-1", response.getContentType());
|
||||
assertArrayEquals(certificate.getBytes(StandardCharsets.ISO_8859_1), response.getContentAsByteArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleRejectsUnsupportedResource() throws Exception {
|
||||
final PkiResourceRouteHandler handler = new PkiResourceRouteHandler();
|
||||
handler.caManager = mock(CAManager.class);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/services/pki-resource");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setParameter("resource", "unsupported");
|
||||
|
||||
handler.handle(request, response, "/services/pki-resource", Negotiation.OutFormat.JSON,
|
||||
new VeeamControlServlet(Collections.emptyList()));
|
||||
|
||||
assertEquals(400, response.getStatus());
|
||||
assertEquals("Unsupported resource", response.getErrorMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleRejectsUnsupportedFormat() throws Exception {
|
||||
final PkiResourceRouteHandler handler = new PkiResourceRouteHandler();
|
||||
handler.caManager = mock(CAManager.class);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/services/pki-resource");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setParameter("format", "PEM");
|
||||
|
||||
handler.handle(request, response, "/services/pki-resource", Negotiation.OutFormat.JSON,
|
||||
new VeeamControlServlet(Collections.emptyList()));
|
||||
|
||||
assertEquals(400, response.getStatus());
|
||||
assertEquals("Unsupported format", response.getErrorMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleRejectsEmptyCertificateData() throws Exception {
|
||||
final PkiResourceRouteHandler handler = new PkiResourceRouteHandler();
|
||||
final CAManager caManager = mock(CAManager.class);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/services/pki-resource");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
handler.caManager = caManager;
|
||||
when(caManager.getCaCertificate(null)).thenReturn("");
|
||||
|
||||
handler.handle(request, response, "/services/pki-resource", Negotiation.OutFormat.JSON,
|
||||
new VeeamControlServlet(Collections.emptyList()));
|
||||
|
||||
assertEquals(500, response.getStatus());
|
||||
assertEquals("No certificate data available", response.getErrorMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleReturnsNotFoundForUnknownPath() throws Exception {
|
||||
final PkiResourceRouteHandler handler = new PkiResourceRouteHandler();
|
||||
handler.caManager = mock(CAManager.class);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/services/pki-resource/unknown");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
handler.handle(request, response, "/services/pki-resource/unknown", Negotiation.OutFormat.JSON,
|
||||
new VeeamControlServlet(Collections.emptyList()));
|
||||
|
||||
assertEquals(404, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("\"reason\":\"Not found\""));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
// 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.cloudstack.veeam.sso;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.veeam.VeeamControlService;
|
||||
import org.apache.cloudstack.veeam.VeeamControlServlet;
|
||||
import org.apache.cloudstack.veeam.utils.Mapper;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SsoServiceTest {
|
||||
|
||||
private final Mapper mapper = new Mapper();
|
||||
|
||||
@Test
|
||||
public void testCanHandleSanitizesQueryParameters() {
|
||||
final SsoService service = new SsoService();
|
||||
|
||||
assertTrue(service.canHandle("POST", "/sso/oauth/token?scope=abc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleReturnsNotFoundForUnknownPath() throws Exception {
|
||||
final SsoService service = new SsoService();
|
||||
service.veeamControlService = mock(VeeamControlService.class);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/sso/unknown");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
service.handle(request, response, "/sso/unknown", Negotiation.OutFormat.JSON,
|
||||
new VeeamControlServlet(Collections.emptyList()));
|
||||
|
||||
assertEquals(404, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("\"reason\":\"Not found\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleTokenRejectsNonPostMethod() throws Exception {
|
||||
final SsoService service = new SsoService();
|
||||
service.veeamControlService = mock(VeeamControlService.class);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/sso/oauth/token");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
service.handle(request, response, "/sso/oauth/token", Negotiation.OutFormat.JSON,
|
||||
new VeeamControlServlet(Collections.emptyList()));
|
||||
|
||||
assertEquals(405, response.getStatus());
|
||||
assertEquals("POST", response.getHeader("Allow"));
|
||||
assertTrue(response.getContentAsString().contains("method_not_allowed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleTokenRejectsMissingGrantType() throws Exception {
|
||||
final SsoService service = new SsoService();
|
||||
service.veeamControlService = mock(VeeamControlService.class);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("POST", "/sso/oauth/token");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
service.handle(request, response, "/sso/oauth/token", Negotiation.OutFormat.JSON,
|
||||
new VeeamControlServlet(Collections.emptyList()));
|
||||
|
||||
assertEquals(400, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("Missing parameter: grant_type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleTokenRejectsUnsupportedGrantType() throws Exception {
|
||||
final SsoService service = new SsoService();
|
||||
service.veeamControlService = mock(VeeamControlService.class);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("POST", "/sso/oauth/token");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setParameter("grant_type", "client_credentials");
|
||||
|
||||
service.handle(request, response, "/sso/oauth/token", Negotiation.OutFormat.JSON,
|
||||
new VeeamControlServlet(Collections.emptyList()));
|
||||
|
||||
assertEquals(400, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("unsupported_grant_type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleTokenRejectsMissingCredentials() throws Exception {
|
||||
final SsoService service = new SsoService();
|
||||
service.veeamControlService = mock(VeeamControlService.class);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("POST", "/sso/oauth/token");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setParameter("grant_type", "password");
|
||||
request.setParameter("username", "veeam");
|
||||
|
||||
service.handle(request, response, "/sso/oauth/token", Negotiation.OutFormat.JSON,
|
||||
new VeeamControlServlet(Collections.emptyList()));
|
||||
|
||||
assertEquals(400, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("Missing username/password"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleTokenRejectsInvalidCredentials() throws Exception {
|
||||
final SsoService service = new SsoService();
|
||||
final VeeamControlService controlService = mock(VeeamControlService.class);
|
||||
service.veeamControlService = controlService;
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("POST", "/sso/oauth/token");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setParameter("grant_type", "password");
|
||||
request.setParameter("username", "veeam");
|
||||
request.setParameter("password", "wrong");
|
||||
when(controlService.validateCredentials("veeam", "wrong")).thenReturn(false);
|
||||
|
||||
service.handle(request, response, "/sso/oauth/token", Negotiation.OutFormat.JSON,
|
||||
new VeeamControlServlet(Collections.emptyList()));
|
||||
|
||||
assertEquals(401, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("invalid_grant"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleTokenReturnsServerErrorWhenTokenIssuanceFails() throws Exception {
|
||||
final SsoService service = new SsoService();
|
||||
final VeeamControlService controlService = mock(VeeamControlService.class);
|
||||
service.veeamControlService = controlService;
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("POST", "/sso/oauth/token");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setParameter("grant_type", "password");
|
||||
request.setParameter("username", "veeam");
|
||||
request.setParameter("password", "secret");
|
||||
when(controlService.validateCredentials("veeam", "secret")).thenReturn(true);
|
||||
when(controlService.getHmacSecret()).thenReturn(null);
|
||||
|
||||
service.handle(request, response, "/sso/oauth/token", Negotiation.OutFormat.JSON,
|
||||
new VeeamControlServlet(Collections.emptyList()));
|
||||
|
||||
assertEquals(500, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("Failed to issue token"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleTokenIssuesTokenWithDefaultScopes() throws Exception {
|
||||
final SsoService service = new SsoService();
|
||||
final VeeamControlService controlService = mock(VeeamControlService.class);
|
||||
service.veeamControlService = controlService;
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("POST", "/sso/oauth/token");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setParameter("grant_type", "password");
|
||||
request.setParameter("username", "veeam");
|
||||
request.setParameter("password", "secret");
|
||||
when(controlService.validateCredentials("veeam", "secret")).thenReturn(true);
|
||||
when(controlService.getHmacSecret()).thenReturn("very-secret");
|
||||
|
||||
service.handle(request, response, "/sso/oauth/token", Negotiation.OutFormat.JSON,
|
||||
new VeeamControlServlet(Collections.emptyList()));
|
||||
|
||||
assertEquals(200, response.getStatus());
|
||||
|
||||
final Map<String, Object> payload = mapper.jsonMapper().readValue(response.getContentAsByteArray(), new TypeReference<>() {
|
||||
});
|
||||
assertEquals("bearer", payload.get("token_type"));
|
||||
assertEquals(3600, ((Number) payload.get("expires_in")).intValue());
|
||||
assertEquals(String.join(" ", SsoService.REQUIRED_SCOPES), payload.get("scope"));
|
||||
|
||||
final String accessToken = (String) payload.get("access_token");
|
||||
final String jwtPayload = new String(Base64.getUrlDecoder().decode(accessToken.split("\\.")[1]), StandardCharsets.UTF_8);
|
||||
final Map<String, Object> jwtClaims = mapper.jsonMapper().readValue(jwtPayload, new TypeReference<>() {
|
||||
});
|
||||
assertEquals("veeam", jwtClaims.get("sub"));
|
||||
assertEquals(String.join(" ", SsoService.REQUIRED_SCOPES), jwtClaims.get("scope"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleTokenHonorsCustomScope() throws Exception {
|
||||
final SsoService service = new SsoService();
|
||||
final VeeamControlService controlService = mock(VeeamControlService.class);
|
||||
service.veeamControlService = controlService;
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest("POST", "/sso/oauth/token");
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setParameter("grant_type", "password");
|
||||
request.setParameter("username", "veeam");
|
||||
request.setParameter("password", "secret");
|
||||
request.setParameter("scope", "custom-scope");
|
||||
when(controlService.validateCredentials("veeam", "secret")).thenReturn(true);
|
||||
when(controlService.getHmacSecret()).thenReturn("very-secret");
|
||||
|
||||
service.handle(request, response, "/sso/oauth/token", Negotiation.OutFormat.JSON,
|
||||
new VeeamControlServlet(Collections.emptyList()));
|
||||
|
||||
assertEquals(200, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("\"scope\":\"custom-scope\""));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// 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.cloudstack.veeam.utils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class DataUtilTest {
|
||||
|
||||
@Test
|
||||
public void testB64Url_UsesUrlSafeAlphabetAndNoPadding() {
|
||||
final String encoded = DataUtil.b64Url(new byte[]{(byte)0xfb, (byte)0xff});
|
||||
assertEquals("-_8", encoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonEscape_NullAndEscapedCharacters() {
|
||||
assertEquals("", DataUtil.jsonEscape(null));
|
||||
assertEquals("a\\\\b\\\"c", DataUtil.jsonEscape("a\\b\"c"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstantTimeEquals_StringOverload() {
|
||||
assertTrue(DataUtil.constantTimeEquals("abc", "abc"));
|
||||
assertFalse(DataUtil.constantTimeEquals("abc", "abd"));
|
||||
assertFalse(DataUtil.constantTimeEquals(null, "abc"));
|
||||
assertFalse(DataUtil.constantTimeEquals("abc", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstantTimeEquals_ByteArrayOverload() {
|
||||
final byte[] left = "sample".getBytes(StandardCharsets.UTF_8);
|
||||
assertTrue(DataUtil.constantTimeEquals(left, "sample".getBytes(StandardCharsets.UTF_8)));
|
||||
assertFalse(DataUtil.constantTimeEquals(left, "samples".getBytes(StandardCharsets.UTF_8)));
|
||||
assertFalse(DataUtil.constantTimeEquals(left, "samplE".getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// 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.cloudstack.veeam.utils;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.util.Base64;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class JwtUtilTest {
|
||||
|
||||
@Test
|
||||
public void testHmacSha256_KnownVector() throws Exception {
|
||||
final byte[] actual = JwtUtil.hmacSha256("data".getBytes(StandardCharsets.UTF_8), "key".getBytes(StandardCharsets.UTF_8));
|
||||
final byte[] expected = hexToBytes("5031fe3d989c6d1537a013fa6e739da23463fdaec3b70137d828e36ace221bd0");
|
||||
assertArrayEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIssueHs256Jwt_BuildsValidTokenAndClaims() throws Exception {
|
||||
final long before = Instant.now().getEpochSecond();
|
||||
final String token = JwtUtil.issueHs256Jwt("sub-1", "scope-a", 120L, "very-secret");
|
||||
final long after = Instant.now().getEpochSecond();
|
||||
|
||||
final String[] parts = token.split("\\.");
|
||||
assertEquals(3, parts.length);
|
||||
|
||||
final JsonObject header = JsonParser.parseString(new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8)).getAsJsonObject();
|
||||
final JsonObject payload = JsonParser.parseString(new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8)).getAsJsonObject();
|
||||
|
||||
assertEquals("HS256", header.get("alg").getAsString());
|
||||
assertEquals("JWT", header.get("typ").getAsString());
|
||||
assertEquals(JwtUtil.ISSUER, payload.get("iss").getAsString());
|
||||
assertEquals("sub-1", payload.get("sub").getAsString());
|
||||
assertEquals("scope-a", payload.get("scope").getAsString());
|
||||
|
||||
final long iat = payload.get("iat").getAsLong();
|
||||
final long exp = payload.get("exp").getAsLong();
|
||||
assertTrue(iat >= before && iat <= after);
|
||||
assertEquals(120L, exp - iat);
|
||||
|
||||
final byte[] expectedSig = JwtUtil.hmacSha256((parts[0] + "." + parts[1]).getBytes(StandardCharsets.UTF_8), "very-secret".getBytes(StandardCharsets.UTF_8));
|
||||
final byte[] actualSig = Base64.getUrlDecoder().decode(parts[2]);
|
||||
assertArrayEquals(expectedSig, actualSig);
|
||||
}
|
||||
|
||||
private static byte[] hexToBytes(final String hex) {
|
||||
final byte[] out = new byte[hex.length() / 2];
|
||||
for (int i = 0; i < out.length; i++) {
|
||||
final int hi = Character.digit(hex.charAt(i * 2), 16);
|
||||
final int lo = Character.digit(hex.charAt(i * 2 + 1), 16);
|
||||
out[i] = (byte)((hi << 4) + lo);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// 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.cloudstack.veeam.utils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class MapperTest {
|
||||
|
||||
@Test
|
||||
public void testToJson_UsesSnakeCaseAndOmitsNulls() throws Exception {
|
||||
final Mapper mapper = new Mapper();
|
||||
final String json = mapper.toJson(new Sample("John", "Doe", null));
|
||||
|
||||
assertTrue(json.contains("\"first_name\":\"John\""));
|
||||
assertTrue(json.contains("\"last_name\":\"Doe\""));
|
||||
assertFalse(json.contains("optional_field"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToXml_UsesSnakeCaseAndOmitsNulls() throws Exception {
|
||||
final Mapper mapper = new Mapper();
|
||||
final String xml = mapper.toXml(new Sample("John", "Doe", null));
|
||||
|
||||
assertTrue(xml.contains("<first_name>John</first_name>"));
|
||||
assertTrue(xml.contains("<last_name>Doe</last_name>"));
|
||||
assertFalse(xml.contains("optional_field"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonMapper_IgnoresUnknownProperties() throws Exception {
|
||||
final Mapper mapper = new Mapper();
|
||||
final Sample sample = mapper.jsonMapper().readValue("{\"first_name\":\"Alice\",\"unknown\":\"x\"}", Sample.class);
|
||||
|
||||
assertNotNull(sample);
|
||||
assertEquals("Alice", sample.firstName);
|
||||
}
|
||||
|
||||
static class Sample {
|
||||
public String firstName;
|
||||
public String lastName;
|
||||
public String optionalField;
|
||||
|
||||
Sample() {
|
||||
}
|
||||
|
||||
Sample(final String firstName, final String lastName, final String optionalField) {
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
this.optionalField = optionalField;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// 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.cloudstack.veeam.utils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class NegotiationTest {
|
||||
|
||||
@Test
|
||||
public void testResponseFormat_DefaultsToXmlForNullBlankWildcardAndUnknown() {
|
||||
final HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
|
||||
when(request.getHeader("Accept")).thenReturn(null);
|
||||
assertEquals(Negotiation.OutFormat.XML, Negotiation.responseFormat(request));
|
||||
|
||||
when(request.getHeader("Accept")).thenReturn(" ");
|
||||
assertEquals(Negotiation.OutFormat.XML, Negotiation.responseFormat(request));
|
||||
|
||||
when(request.getHeader("Accept")).thenReturn("*/*");
|
||||
assertEquals(Negotiation.OutFormat.XML, Negotiation.responseFormat(request));
|
||||
|
||||
when(request.getHeader("Accept")).thenReturn("application/octet-stream");
|
||||
assertEquals(Negotiation.OutFormat.XML, Negotiation.responseFormat(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseFormat_ResolvesJsonAndXmlMediaTypesCaseInsensitively() {
|
||||
final HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
|
||||
when(request.getHeader("Accept")).thenReturn("Application/JSON");
|
||||
assertEquals(Negotiation.OutFormat.JSON, Negotiation.responseFormat(request));
|
||||
|
||||
when(request.getHeader("Accept")).thenReturn("application/xml");
|
||||
assertEquals(Negotiation.OutFormat.XML, Negotiation.responseFormat(request));
|
||||
|
||||
when(request.getHeader("Accept")).thenReturn("text/xml");
|
||||
assertEquals(Negotiation.OutFormat.XML, Negotiation.responseFormat(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContentType_ReturnsMimeTypeForEachFormat() {
|
||||
assertEquals("application/json", Negotiation.contentType(Negotiation.OutFormat.JSON));
|
||||
assertEquals("application/xml", Negotiation.contentType(Negotiation.OutFormat.XML));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// 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.cloudstack.veeam.utils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class PathUtilTest {
|
||||
|
||||
@Test
|
||||
public void testExtractIdAndSubPath_ReturnsNullForBlankOrInvalidPath() {
|
||||
assertNull(PathUtil.extractIdAndSubPath(null, "/api/datacenters"));
|
||||
assertNull(PathUtil.extractIdAndSubPath(" ", "/api/datacenters"));
|
||||
assertNull(PathUtil.extractIdAndSubPath("api/datacenters/123", "/api/datacenters"));
|
||||
assertNull(PathUtil.extractIdAndSubPath("/api/datacenters", "/api/datacenters"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractIdAndSubPath_RemovesBaseRouteAndReturnsSegments() {
|
||||
final List<String> parts = PathUtil.extractIdAndSubPath("/api/datacenters/123/sub/path", "/api/datacenters");
|
||||
assertEquals(List.of("123", "sub", "path"), parts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractIdAndSubPath_HandlesTrailingSlashBaseAndRepeatedSlashes() {
|
||||
final List<String> parts = PathUtil.extractIdAndSubPath("/api/datacenters//123///child/", "/api/datacenters/");
|
||||
assertEquals(List.of("123", "child"), parts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractIdAndSubPath_WithoutBaseRouteUsesPathDirectly() {
|
||||
final List<String> parts = PathUtil.extractIdAndSubPath("/id-1/sub", "");
|
||||
assertEquals(List.of("id-1", "sub"), parts);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
// 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.cloudstack.veeam.utils;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ResponseWriterTest {
|
||||
|
||||
@Test
|
||||
public void testWrite_NullBodySetsStatusAndZeroContentLength() throws Exception {
|
||||
final Mapper mapper = new Mapper();
|
||||
final ResponseWriter responseWriter = new ResponseWriter(mapper);
|
||||
final HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
|
||||
responseWriter.write(response, 204, null, Negotiation.OutFormat.XML);
|
||||
|
||||
verify(response).setStatus(204);
|
||||
verify(response).setContentLength(0);
|
||||
verify(response, never()).getWriter();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrite_JsonBodyWritesPayloadAndHeaders() throws Exception {
|
||||
final ResponseWriter responseWriter = new ResponseWriter(new Mapper());
|
||||
final HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
final StringWriter sink = new StringWriter();
|
||||
when(response.getWriter()).thenReturn(new PrintWriter(sink));
|
||||
|
||||
responseWriter.write(response, 200, new Payload("item-1"), Negotiation.OutFormat.JSON);
|
||||
|
||||
verify(response).setStatus(200);
|
||||
verify(response).setHeader("Content-Type", "application/json");
|
||||
assertTrue(sink.toString().contains("\"name\":\"item-1\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrite_XmlBodyWritesPayloadAndHeaders() throws Exception {
|
||||
final ResponseWriter responseWriter = new ResponseWriter(new Mapper());
|
||||
final HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
final StringWriter sink = new StringWriter();
|
||||
when(response.getWriter()).thenReturn(new PrintWriter(sink));
|
||||
|
||||
responseWriter.write(response, 200, new Payload("item-2"), Negotiation.OutFormat.XML);
|
||||
|
||||
verify(response).setHeader("Content-Type", "application/xml");
|
||||
assertTrue(sink.toString().contains("<name>item-2</name>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrite_WhenMappingFailsReturnsInternalServerError() throws Exception {
|
||||
final Mapper mapper = mock(Mapper.class);
|
||||
doThrow(new RuntimeException("boom")).when(mapper).toJson(org.mockito.ArgumentMatchers.any());
|
||||
|
||||
final ResponseWriter responseWriter = new ResponseWriter(mapper);
|
||||
final HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
final StringWriter sink = new StringWriter();
|
||||
when(response.getWriter()).thenReturn(new PrintWriter(sink));
|
||||
|
||||
responseWriter.write(response, 200, new Payload("ignored"), Negotiation.OutFormat.JSON);
|
||||
|
||||
verify(response).setStatus(500);
|
||||
verify(response).setHeader("Content-Type", "text/plain");
|
||||
assertTrue(sink.toString().contains("Internal Server Error"));
|
||||
verify(response, never()).setContentLength(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteFault_JsonWritesFaultStructure() throws Exception {
|
||||
final ResponseWriter responseWriter = new ResponseWriter(new Mapper());
|
||||
final HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
final StringWriter sink = new StringWriter();
|
||||
when(response.getWriter()).thenReturn(new PrintWriter(sink));
|
||||
|
||||
responseWriter.writeFault(response, 404, "Not Found", "missing vm", Negotiation.OutFormat.JSON);
|
||||
|
||||
verify(response).setStatus(404);
|
||||
assertTrue(sink.toString().contains("\"reason\":\"Not Found\""));
|
||||
assertTrue(sink.toString().contains("\"detail\":\"missing vm\""));
|
||||
}
|
||||
|
||||
static class Payload {
|
||||
public String name;
|
||||
|
||||
Payload(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue