Skip to content

Commit

Permalink
Improve tests for Registry Management's search entities API
Browse files Browse the repository at this point in the history
Extended the unit and integration tests for the search API to
use a larger set of entities so that paging functionality can
be tested more meaningfully.
  • Loading branch information
sophokles73 committed Sep 24, 2023
1 parent 817db3a commit a7ec5d6
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -178,27 +178,56 @@ default void testSearchDevicesWithPageSize(final VertxTestContext ctx) {
@Test
default void testSearchDevicesWithPageOffset(final VertxTestContext ctx) {
final String tenantId = DeviceRegistryUtils.getUniqueIdentifier();
final int pageSize = 1;
final int pageOffset = 1;
final int pageSize = 6;
final Filter filter = new Filter("/enabled", true);
final Sort sortOption = new Sort("/id");

sortOption.setDirection(Sort.Direction.DESC);
createDevices(tenantId, Map.of(
"testDevice0", new Device().setEnabled(true),
"testDevice1", new Device().setEnabled(true),
"testDevice2", new Device().setEnabled(true)))
.compose(ok -> getDeviceManagementService()
.searchDevices(tenantId, pageSize, pageOffset, List.of(filter),
List.of(sortOption),
NoopSpan.INSTANCE))
"testDevice2", new Device().setEnabled(true),
"testDevice3", new Device().setEnabled(true),
"testDevice4", new Device().setEnabled(true),
"testDevice5", new Device().setEnabled(true),
"testDevice6", new Device().setEnabled(true),
"testDevice7", new Device().setEnabled(true)))
.compose(ok -> getDeviceManagementService().searchDevices(
tenantId,
pageSize,
0,
List.of(filter),
List.of(),
NoopSpan.INSTANCE))
.compose(response -> {
ctx.verify(() -> {
assertThat(response.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK);

final var searchResult = response.getPayload();
assertThat(searchResult.getTotal()).isEqualTo(8);
assertThat(searchResult.getResult()).hasSize(6);
assertThat(searchResult.getResult().get(0).getId()).isEqualTo("testDevice0");
assertThat(searchResult.getResult().get(1).getId()).isEqualTo("testDevice1");
assertThat(searchResult.getResult().get(2).getId()).isEqualTo("testDevice2");
assertThat(searchResult.getResult().get(3).getId()).isEqualTo("testDevice3");
assertThat(searchResult.getResult().get(4).getId()).isEqualTo("testDevice4");
assertThat(searchResult.getResult().get(5).getId()).isEqualTo("testDevice5");
});
return getDeviceManagementService().searchDevices(
tenantId,
pageSize,
1,
List.of(filter),
List.of(),
NoopSpan.INSTANCE);
})
.onComplete(ctx.succeeding(s -> {
ctx.verify(() -> {
assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK);

final SearchResult<DeviceWithId> searchResult = s.getPayload();
assertThat(searchResult.getTotal()).isEqualTo(2);
assertThat(searchResult.getResult()).hasSize(1);
assertThat(searchResult.getResult().get(0).getId()).isEqualTo("testDevice1");
final var searchResult = s.getPayload();
assertThat(searchResult.getTotal()).isEqualTo(8);
assertThat(searchResult.getResult()).hasSize(2);
assertThat(searchResult.getResult().get(0).getId()).isEqualTo("testDevice6");
assertThat(searchResult.getResult().get(1).getId()).isEqualTo("testDevice7");
});
ctx.completeNow();
}));
Expand Down Expand Up @@ -238,6 +267,65 @@ default void testSearchDevicesWithSortOption(final VertxTestContext ctx) {
}));
}

/**
* Verifies that a request to search devices without a sort option succeeds and the result set is sorted
* by device ID.
*
* @param ctx The vert.x test context.
*/
@Test
default void testSearchDevicesSortsResultById(final VertxTestContext ctx) {
final String tenantId = DeviceRegistryUtils.getUniqueIdentifier();
final int pageSize = 4;

createDevices(tenantId, Map.of(
"testDevice1", new Device(),
"testDevice5", new Device(),
"testDevice3", new Device(),
"testDevice4", new Device(),
"testDevice2", new Device(),
"testDevice6", new Device()))
.compose(ok -> getDeviceManagementService().searchDevices(
tenantId,
pageSize,
0,
List.of(),
List.of(),
NoopSpan.INSTANCE))
.compose(response -> {
ctx.verify(() -> {
assertThat(response.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK);

final var searchResult = response.getPayload();
assertThat(searchResult.getTotal()).isEqualTo(6);
assertThat(searchResult.getResult()).hasSize(4);
assertThat(searchResult.getResult().get(0).getId()).isEqualTo("testDevice1");
assertThat(searchResult.getResult().get(1).getId()).isEqualTo("testDevice2");
assertThat(searchResult.getResult().get(2).getId()).isEqualTo("testDevice3");
assertThat(searchResult.getResult().get(3).getId()).isEqualTo("testDevice4");
});
return getDeviceManagementService().searchDevices(
tenantId,
pageSize,
1,
List.of(),
List.of(),
NoopSpan.INSTANCE);
})
.onComplete(ctx.succeeding(s -> {
ctx.verify(() -> {
assertThat(s.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK);

final var searchResult = s.getPayload();
assertThat(searchResult.getTotal()).isEqualTo(6);
assertThat(searchResult.getResult()).hasSize(2);
assertThat(searchResult.getResult().get(0).getId()).isEqualTo("testDevice5");
assertThat(searchResult.getResult().get(1).getId()).isEqualTo("testDevice6");
});
ctx.completeNow();
}));
}

/**
* Verifies that a request to search devices with filters containing the wildcard character '*'
* succeeds and matching devices are found.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020 Contributors to the Eclipse Foundation
* Copyright (c) 2020, 2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
Expand All @@ -15,6 +15,8 @@
import static com.google.common.truth.Truth.assertThat;

import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -227,6 +229,64 @@ tenantId2, new Tenant().setEnabled(true).setExtensions(Map.of("id", "2"))))
}));
}

/**
* Verifies that a request to search tenants without a sort option succeeds and the result set is sorted
* by tenant ID.
*
* @param ctx The vert.x test context.
*/
@Test
default void testSearchTenantsSortsResultById(final VertxTestContext ctx) {
final var tenants = Map.of(
DeviceRegistryUtils.getUniqueIdentifier(), new Tenant(),
DeviceRegistryUtils.getUniqueIdentifier(), new Tenant(),
DeviceRegistryUtils.getUniqueIdentifier(), new Tenant(),
DeviceRegistryUtils.getUniqueIdentifier(), new Tenant(),
DeviceRegistryUtils.getUniqueIdentifier(), new Tenant(),
DeviceRegistryUtils.getUniqueIdentifier(), new Tenant());
final var tenantIds = new ArrayList<String>(tenants.keySet());
Collections.sort(tenantIds);
final int pageSize = 4;

createTenants(tenants)
.onFailure(ctx::failNow)
.compose(ok -> getTenantManagementService().searchTenants(
pageSize,
0,
List.of(),
List.of(),
NoopSpan.INSTANCE))
.compose(response -> {
ctx.verify(() -> {
assertThat(response.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK);

final var payload = response.getPayload();
assertThat(payload.getTotal()).isEqualTo(6);
assertThat(payload.getResult()).hasSize(4);
final var foundTenantIds = payload.getResult().stream().map(TenantWithId::getId).collect(Collectors.toList());
assertThat(foundTenantIds).containsExactlyElementsIn(tenantIds.subList(0, 4));
});
return getTenantManagementService().searchTenants(
pageSize,
1,
List.of(),
List.of(),
NoopSpan.INSTANCE);
})
.onComplete(ctx.succeeding(response -> {
ctx.verify(() -> {
assertThat(response.getStatus()).isEqualTo(HttpURLConnection.HTTP_OK);

final var payload = response.getPayload();
assertThat(payload.getTotal()).isEqualTo(6);
assertThat(payload.getResult()).hasSize(2);
final var foundTenantIds = payload.getResult().stream().map(TenantWithId::getId).collect(Collectors.toList());
assertThat(foundTenantIds).containsExactlyElementsIn(tenantIds.subList(4, 6));
});
ctx.completeNow();
}));
}

/**
* Verifies that a request to search tenants with a sort option succeeds and the result is in accordance with the
* specified sort option.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,14 @@ private void applySearchFilters(final List<Filter> filters, final Function<JsonP
}

private void applySortingOptions(final List<Sort> sortOptions, final Function<JsonPointer, String> fieldMapper) {
sortOptions.forEach(sortOption -> document.put(fieldMapper.apply(sortOption.getField()),
mapSortingDirection(sortOption.getDirection())));
if (sortOptions.isEmpty()) {
// if no fields to sort by have been specified, sort by "/id"
document.put(fieldMapper.apply(FIELD_ID), mapSortingDirection(Sort.Direction.ASC));
} else {
sortOptions.forEach(sortOption -> document.put(
fieldMapper.apply(sortOption.getField()),
mapSortingDirection(sortOption.getDirection())));
}
}

private MongoDbDocumentBuilder withCredentialsPredicate(final String field, final String value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,17 @@
import java.security.PublicKey;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.eclipse.hono.service.management.SearchResult;
import org.eclipse.hono.service.management.device.Device;
Expand All @@ -56,6 +59,7 @@
import com.fasterxml.jackson.core.type.TypeReference;

import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.json.JsonArray;
Expand Down Expand Up @@ -890,29 +894,54 @@ public void testSearchTenantsWithInvalidPageOffsetFails(final VertxTestContext c
*/
@Test
public void testSearchTenantsWithValidPageOffsetSucceeds(final VertxTestContext ctx) {
final String tenantId1 = getHelper().getRandomTenantId();
final String tenantId2 = getHelper().getRandomTenantId();
final Tenant tenant1 = new Tenant().setExtensions(Map.of("id", "aaa"));
final Tenant tenant2 = new Tenant().setExtensions(Map.of("id", "bbb"));
final int pageSize = 1;
final int pageOffset = 1;
final String sortJson = getSortJson("/ext/id", "desc");

CompositeFuture.all(
getHelper().registry.addTenant(tenantId1, tenant1),
getHelper().registry.addTenant(tenantId2, tenant2))
.compose(ok -> getHelper().registry.searchTenants(Optional.of(pageSize), Optional.of(pageOffset),
List.of(), List.of(sortJson), HttpURLConnection.HTTP_OK))
.onComplete(ctx.succeeding(httpResponse -> {
ctx.verify(() -> {
final SearchResult<TenantWithId> searchResult = JacksonCodec
.decodeValue(httpResponse.body(), new TypeReference<>() { });
assertThat(searchResult.getTotal()).isEqualTo(2);
assertThat(searchResult.getResult()).hasSize(1);
assertThat(searchResult.getResult().get(0).getId()).isEqualTo(tenantId1);
});
ctx.completeNow();
}));
final var tenants = Map.of(
getHelper().getRandomTenantId(), new Tenant(),
getHelper().getRandomTenantId(), new Tenant(),
getHelper().getRandomTenantId(), new Tenant(),
getHelper().getRandomTenantId(), new Tenant(),
getHelper().getRandomTenantId(), new Tenant(),
getHelper().getRandomTenantId(), new Tenant());
final var tenantIds = new ArrayList<String>(tenants.keySet());
Collections.sort(tenantIds);
final int pageSize = 4;

createTenants(tenants)
.compose(ok -> getHelper().registry.searchTenants(
Optional.of(pageSize),
Optional.empty(),
List.of(),
List.of(),
HttpURLConnection.HTTP_OK))
.compose(response -> {
ctx.verify(() -> {
final SearchResult<TenantWithId> searchResult = JacksonCodec
.decodeValue(response.body(), new TypeReference<>() { });

assertThat(searchResult.getTotal()).isEqualTo(6);
assertThat(searchResult.getResult()).hasSize(4);
final var foundTenantIds = searchResult.getResult().stream().map(TenantWithId::getId).collect(Collectors.toList());
assertThat(foundTenantIds).containsExactlyElementsIn(tenantIds.subList(0, 4));
});
return getHelper().registry.searchTenants(
Optional.of(pageSize),
Optional.of(1),
List.of(),
List.of(),
HttpURLConnection.HTTP_OK);
})
.onComplete(ctx.succeeding(response -> {
ctx.verify(() -> {
final SearchResult<TenantWithId> searchResult = JacksonCodec
.decodeValue(response.body(), new TypeReference<>() { });

assertThat(searchResult.getTotal()).isEqualTo(6);
assertThat(searchResult.getResult()).hasSize(2);
final var foundTenantIds = searchResult.getResult().stream().map(TenantWithId::getId).collect(Collectors.toList());
assertThat(foundTenantIds).containsExactlyElementsIn(tenantIds.subList(4, 6));
});
ctx.completeNow();
}));
}

/**
Expand Down Expand Up @@ -1111,6 +1140,25 @@ public void testSearchTenantsWithValidSortOptionSucceeds(final VertxTestContext
}));
}

/**
* Creates a set of tenants.
*
* @param tenantsToCreate The tenants to be created. The keys are the tenant identifiers.
* @return A succeeded future if all the tenants have been created successfully.
*/
private Future<Void> createTenants(final Map<String, Tenant> tenantsToCreate) {

@SuppressWarnings("rawtypes")
final List<Future> creationResult = tenantsToCreate.entrySet().stream()
.map(entry -> getHelper().registry.addTenant(entry.getKey(), entry.getValue())
.map(response -> {
assertThat(response.statusCode()).isEqualTo(HttpURLConnection.HTTP_CREATED);
return null;
}))
.collect(Collectors.toList());
return CompositeFuture.all(creationResult).mapEmpty();
}

private <T> String getFilterJson(final String field, final T value, final String operator) {
final JsonObject filterJson = new JsonObject()
.put(RegistryManagementConstants.FIELD_FILTER_FIELD, field)
Expand Down

0 comments on commit a7ec5d6

Please sign in to comment.