Add names to subscription levels
This commit is contained in:
parent
94bf3a3902
commit
a52c91a665
|
@ -52,10 +52,10 @@ import java.util.concurrent.TimeUnit;
|
||||||
import javax.servlet.DispatcherType;
|
import javax.servlet.DispatcherType;
|
||||||
import javax.servlet.FilterRegistration;
|
import javax.servlet.FilterRegistration;
|
||||||
import javax.servlet.ServletRegistration;
|
import javax.servlet.ServletRegistration;
|
||||||
import javax.ws.rs.ext.ExceptionMapper;
|
|
||||||
import org.eclipse.jetty.servlets.CrossOriginFilter;
|
import org.eclipse.jetty.servlets.CrossOriginFilter;
|
||||||
import org.glassfish.jersey.server.ServerProperties;
|
import org.glassfish.jersey.server.ServerProperties;
|
||||||
import org.jdbi.v3.core.Jdbi;
|
import org.jdbi.v3.core.Jdbi;
|
||||||
|
import org.signal.i18n.HeaderControlledResourceBundleLookup;
|
||||||
import org.signal.zkgroup.ServerSecretParams;
|
import org.signal.zkgroup.ServerSecretParams;
|
||||||
import org.signal.zkgroup.auth.ServerZkAuthOperations;
|
import org.signal.zkgroup.auth.ServerZkAuthOperations;
|
||||||
import org.signal.zkgroup.profiles.ServerZkProfileOperations;
|
import org.signal.zkgroup.profiles.ServerZkProfileOperations;
|
||||||
|
@ -73,6 +73,7 @@ import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||||
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
||||||
import org.whispersystems.textsecuregcm.auth.WebsocketRefreshApplicationEventListener;
|
import org.whispersystems.textsecuregcm.auth.WebsocketRefreshApplicationEventListener;
|
||||||
import org.whispersystems.textsecuregcm.badges.ConfiguredProfileBadgeConverter;
|
import org.whispersystems.textsecuregcm.badges.ConfiguredProfileBadgeConverter;
|
||||||
|
import org.whispersystems.textsecuregcm.badges.ResourceBundleLevelTranslator;
|
||||||
import org.whispersystems.textsecuregcm.configuration.DirectoryServerConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.DirectoryServerConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.controllers.AccountController;
|
import org.whispersystems.textsecuregcm.controllers.AccountController;
|
||||||
|
@ -299,8 +300,12 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
environment.getObjectMapper().setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
|
environment.getObjectMapper().setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
|
||||||
environment.getObjectMapper().setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
|
environment.getObjectMapper().setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
|
||||||
|
|
||||||
ConfiguredProfileBadgeConverter profileBadgeConverter =
|
HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup =
|
||||||
new ConfiguredProfileBadgeConverter(clock, config.getBadges());
|
new HeaderControlledResourceBundleLookup();
|
||||||
|
ConfiguredProfileBadgeConverter profileBadgeConverter = new ConfiguredProfileBadgeConverter(
|
||||||
|
clock, config.getBadges(), headerControlledResourceBundleLookup);
|
||||||
|
ResourceBundleLevelTranslator resourceBundleLevelTranslator = new ResourceBundleLevelTranslator(
|
||||||
|
headerControlledResourceBundleLookup);
|
||||||
|
|
||||||
JdbiFactory jdbiFactory = new JdbiFactory(DefaultNameStrategy.CHECK_EMPTY);
|
JdbiFactory jdbiFactory = new JdbiFactory(DefaultNameStrategy.CHECK_EMPTY);
|
||||||
Jdbi accountJdbi = jdbiFactory.build(environment, config.getAccountsDatabaseConfiguration(), "accountdb");
|
Jdbi accountJdbi = jdbiFactory.build(environment, config.getAccountsDatabaseConfiguration(), "accountdb");
|
||||||
|
@ -645,7 +650,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
);
|
);
|
||||||
if (config.getSubscription() != null && config.getBoost() != null) {
|
if (config.getSubscription() != null && config.getBoost() != null) {
|
||||||
commonControllers.add(new SubscriptionController(clock, config.getSubscription(), config.getBoost(),
|
commonControllers.add(new SubscriptionController(clock, config.getSubscription(), config.getBoost(),
|
||||||
subscriptionManager, stripeManager, zkReceiptOperations, issuedReceiptsManager, profileBadgeConverter));
|
subscriptionManager, stripeManager, zkReceiptOperations, issuedReceiptsManager, profileBadgeConverter,
|
||||||
|
resourceBundleLevelTranslator));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Object controller : commonControllers) {
|
for (Object controller : commonControllers) {
|
||||||
|
|
|
@ -34,13 +34,6 @@ public class ConfiguredProfileBadgeConverter implements ProfileBadgeConverter, B
|
||||||
private final List<String> badgeIdsEnabledForAll;
|
private final List<String> badgeIdsEnabledForAll;
|
||||||
private final HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup;
|
private final HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup;
|
||||||
|
|
||||||
public ConfiguredProfileBadgeConverter(
|
|
||||||
final Clock clock,
|
|
||||||
final BadgesConfiguration badgesConfiguration) {
|
|
||||||
this(clock, badgesConfiguration, new HeaderControlledResourceBundleLookup());
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public ConfiguredProfileBadgeConverter(
|
public ConfiguredProfileBadgeConverter(
|
||||||
final Clock clock,
|
final Clock clock,
|
||||||
final BadgesConfiguration badgesConfiguration,
|
final BadgesConfiguration badgesConfiguration,
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.badges;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public interface LevelTranslator {
|
||||||
|
String translate(List<Locale> acceptableLanguages, String badgeId);
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.badges;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import org.signal.i18n.HeaderControlledResourceBundleLookup;
|
||||||
|
|
||||||
|
public class ResourceBundleLevelTranslator implements LevelTranslator {
|
||||||
|
|
||||||
|
private static final String BASE_NAME = "org.signal.subscriptions.Subscriptions";
|
||||||
|
|
||||||
|
private final HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup;
|
||||||
|
|
||||||
|
public ResourceBundleLevelTranslator(
|
||||||
|
@Nonnull final HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup) {
|
||||||
|
this.headerControlledResourceBundleLookup = Objects.requireNonNull(headerControlledResourceBundleLookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String translate(final List<Locale> acceptableLanguages, final String badgeId) {
|
||||||
|
final ResourceBundle resourceBundle = headerControlledResourceBundleLookup.getResourceBundle(BASE_NAME,
|
||||||
|
acceptableLanguages);
|
||||||
|
return resourceBundle.getString(badgeId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,6 +64,7 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.badges.BadgeTranslator;
|
import org.whispersystems.textsecuregcm.badges.BadgeTranslator;
|
||||||
|
import org.whispersystems.textsecuregcm.badges.LevelTranslator;
|
||||||
import org.whispersystems.textsecuregcm.configuration.BoostConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.BoostConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SubscriptionConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.SubscriptionConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SubscriptionLevelConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.SubscriptionLevelConfiguration;
|
||||||
|
@ -88,6 +89,7 @@ public class SubscriptionController {
|
||||||
private final ServerZkReceiptOperations zkReceiptOperations;
|
private final ServerZkReceiptOperations zkReceiptOperations;
|
||||||
private final IssuedReceiptsManager issuedReceiptsManager;
|
private final IssuedReceiptsManager issuedReceiptsManager;
|
||||||
private final BadgeTranslator badgeTranslator;
|
private final BadgeTranslator badgeTranslator;
|
||||||
|
private final LevelTranslator levelTranslator;
|
||||||
|
|
||||||
public SubscriptionController(
|
public SubscriptionController(
|
||||||
@Nonnull Clock clock,
|
@Nonnull Clock clock,
|
||||||
|
@ -97,7 +99,8 @@ public class SubscriptionController {
|
||||||
@Nonnull StripeManager stripeManager,
|
@Nonnull StripeManager stripeManager,
|
||||||
@Nonnull ServerZkReceiptOperations zkReceiptOperations,
|
@Nonnull ServerZkReceiptOperations zkReceiptOperations,
|
||||||
@Nonnull IssuedReceiptsManager issuedReceiptsManager,
|
@Nonnull IssuedReceiptsManager issuedReceiptsManager,
|
||||||
@Nonnull BadgeTranslator badgeTranslator) {
|
@Nonnull BadgeTranslator badgeTranslator,
|
||||||
|
@Nonnull LevelTranslator levelTranslator) {
|
||||||
this.clock = Objects.requireNonNull(clock);
|
this.clock = Objects.requireNonNull(clock);
|
||||||
this.subscriptionConfiguration = Objects.requireNonNull(subscriptionConfiguration);
|
this.subscriptionConfiguration = Objects.requireNonNull(subscriptionConfiguration);
|
||||||
this.boostConfiguration = Objects.requireNonNull(boostConfiguration);
|
this.boostConfiguration = Objects.requireNonNull(boostConfiguration);
|
||||||
|
@ -106,6 +109,7 @@ public class SubscriptionController {
|
||||||
this.zkReceiptOperations = Objects.requireNonNull(zkReceiptOperations);
|
this.zkReceiptOperations = Objects.requireNonNull(zkReceiptOperations);
|
||||||
this.issuedReceiptsManager = Objects.requireNonNull(issuedReceiptsManager);
|
this.issuedReceiptsManager = Objects.requireNonNull(issuedReceiptsManager);
|
||||||
this.badgeTranslator = Objects.requireNonNull(badgeTranslator);
|
this.badgeTranslator = Objects.requireNonNull(badgeTranslator);
|
||||||
|
this.levelTranslator = Objects.requireNonNull(levelTranslator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Timed
|
@Timed
|
||||||
|
@ -347,17 +351,24 @@ public class SubscriptionController {
|
||||||
|
|
||||||
public static class Level {
|
public static class Level {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
private final Badge badge;
|
private final Badge badge;
|
||||||
private final Map<String, BigDecimal> currencies;
|
private final Map<String, BigDecimal> currencies;
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public Level(
|
public Level(
|
||||||
|
@JsonProperty("name") String name,
|
||||||
@JsonProperty("badge") Badge badge,
|
@JsonProperty("badge") Badge badge,
|
||||||
@JsonProperty("currencies") Map<String, BigDecimal> currencies) {
|
@JsonProperty("currencies") Map<String, BigDecimal> currencies) {
|
||||||
|
this.name = name;
|
||||||
this.badge = badge;
|
this.badge = badge;
|
||||||
this.currencies = currencies;
|
this.currencies = currencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
public Badge getBadge() {
|
public Badge getBadge() {
|
||||||
return badge;
|
return badge;
|
||||||
}
|
}
|
||||||
|
@ -391,6 +402,7 @@ public class SubscriptionController {
|
||||||
GetLevelsResponse getLevelsResponse = new GetLevelsResponse(
|
GetLevelsResponse getLevelsResponse = new GetLevelsResponse(
|
||||||
subscriptionConfiguration.getLevels().entrySet().stream().collect(Collectors.toMap(Entry::getKey,
|
subscriptionConfiguration.getLevels().entrySet().stream().collect(Collectors.toMap(Entry::getKey,
|
||||||
entry -> new GetLevelsResponse.Level(
|
entry -> new GetLevelsResponse.Level(
|
||||||
|
levelTranslator.translate(acceptableLanguages, entry.getValue().getBadge()),
|
||||||
badgeTranslator.translate(acceptableLanguages, entry.getValue().getBadge()),
|
badgeTranslator.translate(acceptableLanguages, entry.getValue().getBadge()),
|
||||||
entry.getValue().getPrices().entrySet().stream().collect(
|
entry.getValue().getPrices().entrySet().stream().collect(
|
||||||
Collectors.toMap(levelEntry -> levelEntry.getKey().toUpperCase(Locale.ROOT),
|
Collectors.toMap(levelEntry -> levelEntry.getKey().toUpperCase(Locale.ROOT),
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
# First subscription level
|
# First subscription level
|
||||||
SUSTAINER1 = Sustainer 1
|
R_LOW = Sustainer 1
|
||||||
|
|
||||||
# Second subscription level
|
# Second subscription level
|
||||||
SUSTAINER2 = Sustainer 2
|
R_MED = Sustainer 2
|
||||||
|
|
||||||
# Third subscription level
|
# Third subscription level
|
||||||
SUSTAINER3 = Sustainer 3
|
R_HIGH = Sustainer 3
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.signal.zkgroup.receipts.ServerZkReceiptOperations;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.badges.BadgeTranslator;
|
import org.whispersystems.textsecuregcm.badges.BadgeTranslator;
|
||||||
|
import org.whispersystems.textsecuregcm.badges.LevelTranslator;
|
||||||
import org.whispersystems.textsecuregcm.configuration.BoostConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.BoostConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SubscriptionConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.SubscriptionConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SubscriptionLevelConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.SubscriptionLevelConfiguration;
|
||||||
|
@ -53,9 +54,10 @@ class SubscriptionControllerTest {
|
||||||
private static final ServerZkReceiptOperations ZK_OPS = mock(ServerZkReceiptOperations.class);
|
private static final ServerZkReceiptOperations ZK_OPS = mock(ServerZkReceiptOperations.class);
|
||||||
private static final IssuedReceiptsManager ISSUED_RECEIPTS_MANAGER = mock(IssuedReceiptsManager.class);
|
private static final IssuedReceiptsManager ISSUED_RECEIPTS_MANAGER = mock(IssuedReceiptsManager.class);
|
||||||
private static final BadgeTranslator BADGE_TRANSLATOR = mock(BadgeTranslator.class);
|
private static final BadgeTranslator BADGE_TRANSLATOR = mock(BadgeTranslator.class);
|
||||||
|
private static final LevelTranslator LEVEL_TRANSLATOR = mock(LevelTranslator.class);
|
||||||
private static final SubscriptionController SUBSCRIPTION_CONTROLLER = new SubscriptionController(
|
private static final SubscriptionController SUBSCRIPTION_CONTROLLER = new SubscriptionController(
|
||||||
CLOCK, SUBSCRIPTION_CONFIG, BOOST_CONFIG, SUBSCRIPTION_MANAGER, STRIPE_MANAGER, ZK_OPS,
|
CLOCK, SUBSCRIPTION_CONFIG, BOOST_CONFIG, SUBSCRIPTION_MANAGER, STRIPE_MANAGER, ZK_OPS,
|
||||||
ISSUED_RECEIPTS_MANAGER, BADGE_TRANSLATOR);
|
ISSUED_RECEIPTS_MANAGER, BADGE_TRANSLATOR, LEVEL_TRANSLATOR);
|
||||||
private static final ResourceExtension RESOURCE_EXTENSION = ResourceExtension.builder()
|
private static final ResourceExtension RESOURCE_EXTENSION = ResourceExtension.builder()
|
||||||
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
|
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
|
||||||
.addProvider(AuthHelper.getAuthFilter())
|
.addProvider(AuthHelper.getAuthFilter())
|
||||||
|
@ -69,7 +71,7 @@ class SubscriptionControllerTest {
|
||||||
@AfterEach
|
@AfterEach
|
||||||
void tearDown() {
|
void tearDown() {
|
||||||
reset(CLOCK, SUBSCRIPTION_CONFIG, SUBSCRIPTION_MANAGER, STRIPE_MANAGER, ZK_OPS, ISSUED_RECEIPTS_MANAGER,
|
reset(CLOCK, SUBSCRIPTION_CONFIG, SUBSCRIPTION_MANAGER, STRIPE_MANAGER, ZK_OPS, ISSUED_RECEIPTS_MANAGER,
|
||||||
BADGE_TRANSLATOR);
|
BADGE_TRANSLATOR, LEVEL_TRANSLATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -85,6 +87,9 @@ class SubscriptionControllerTest {
|
||||||
List.of("l", "m", "h", "x", "xx", "xxx"), "SVG", List.of(new BadgeSvg("sl", "sd", "st"), new BadgeSvg("ml", "md", "mt"), new BadgeSvg("ll", "ld", "lt"))));
|
List.of("l", "m", "h", "x", "xx", "xxx"), "SVG", List.of(new BadgeSvg("sl", "sd", "st"), new BadgeSvg("ml", "md", "mt"), new BadgeSvg("ll", "ld", "lt"))));
|
||||||
when(BADGE_TRANSLATOR.translate(any(), eq("B3"))).thenReturn(new Badge("B3", "cat3", "name3", "desc3",
|
when(BADGE_TRANSLATOR.translate(any(), eq("B3"))).thenReturn(new Badge("B3", "cat3", "name3", "desc3",
|
||||||
List.of("l", "m", "h", "x", "xx", "xxx"), "SVG", List.of(new BadgeSvg("sl", "sd", "st"), new BadgeSvg("ml", "md", "mt"), new BadgeSvg("ll", "ld", "lt"))));
|
List.of("l", "m", "h", "x", "xx", "xxx"), "SVG", List.of(new BadgeSvg("sl", "sd", "st"), new BadgeSvg("ml", "md", "mt"), new BadgeSvg("ll", "ld", "lt"))));
|
||||||
|
when(LEVEL_TRANSLATOR.translate(any(), eq("B1"))).thenReturn("Z1");
|
||||||
|
when(LEVEL_TRANSLATOR.translate(any(), eq("B2"))).thenReturn("Z2");
|
||||||
|
when(LEVEL_TRANSLATOR.translate(any(), eq("B3"))).thenReturn("Z3");
|
||||||
|
|
||||||
GetLevelsResponse response = RESOURCE_EXTENSION.target("/v1/subscription/levels")
|
GetLevelsResponse response = RESOURCE_EXTENSION.target("/v1/subscription/levels")
|
||||||
.request()
|
.request()
|
||||||
|
@ -92,18 +97,21 @@ class SubscriptionControllerTest {
|
||||||
|
|
||||||
assertThat(response.getLevels()).containsKeys(1L, 2L, 3L).satisfies(longLevelMap -> {
|
assertThat(response.getLevels()).containsKeys(1L, 2L, 3L).satisfies(longLevelMap -> {
|
||||||
assertThat(longLevelMap).extractingByKey(1L).satisfies(level -> {
|
assertThat(longLevelMap).extractingByKey(1L).satisfies(level -> {
|
||||||
|
assertThat(level.getName()).isEqualTo("Z1");
|
||||||
assertThat(level.getBadge().getId()).isEqualTo("B1");
|
assertThat(level.getBadge().getId()).isEqualTo("B1");
|
||||||
assertThat(level.getCurrencies()).containsKeys("USD").extractingByKey("USD").satisfies(price -> {
|
assertThat(level.getCurrencies()).containsKeys("USD").extractingByKey("USD").satisfies(price -> {
|
||||||
assertThat(price).isEqualTo("100");
|
assertThat(price).isEqualTo("100");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
assertThat(longLevelMap).extractingByKey(2L).satisfies(level -> {
|
assertThat(longLevelMap).extractingByKey(2L).satisfies(level -> {
|
||||||
|
assertThat(level.getName()).isEqualTo("Z2");
|
||||||
assertThat(level.getBadge().getId()).isEqualTo("B2");
|
assertThat(level.getBadge().getId()).isEqualTo("B2");
|
||||||
assertThat(level.getCurrencies()).containsKeys("USD").extractingByKey("USD").satisfies(price -> {
|
assertThat(level.getCurrencies()).containsKeys("USD").extractingByKey("USD").satisfies(price -> {
|
||||||
assertThat(price).isEqualTo("200");
|
assertThat(price).isEqualTo("200");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
assertThat(longLevelMap).extractingByKey(3L).satisfies(level -> {
|
assertThat(longLevelMap).extractingByKey(3L).satisfies(level -> {
|
||||||
|
assertThat(level.getName()).isEqualTo("Z3");
|
||||||
assertThat(level.getBadge().getId()).isEqualTo("B3");
|
assertThat(level.getBadge().getId()).isEqualTo("B3");
|
||||||
assertThat(level.getCurrencies()).containsKeys("USD").extractingByKey("USD").satisfies(price -> {
|
assertThat(level.getCurrencies()).containsKeys("USD").extractingByKey("USD").satisfies(price -> {
|
||||||
assertThat(price).isEqualTo("300");
|
assertThat(price).isEqualTo("300");
|
||||||
|
|
Loading…
Reference in New Issue