Support for setting and looking up usernames

This commit is contained in:
Moxie Marlinspike
2019-08-07 20:22:06 -07:00
parent 10f80f9a4f
commit 99c228dd6d
13 changed files with 920 additions and 12 deletions

View File

@@ -37,6 +37,7 @@ import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.MessagesManager;
import org.whispersystems.textsecuregcm.storage.PendingAccountsManager;
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
import org.whispersystems.textsecuregcm.util.Hex;
import org.whispersystems.textsecuregcm.util.SystemMapper;
@@ -86,6 +87,7 @@ public class AccountControllerTest {
private RateLimiter smsVoiceIpLimiter = mock(RateLimiter.class );
private RateLimiter smsVoicePrefixLimiter = mock(RateLimiter.class);
private RateLimiter autoBlockLimiter = mock(RateLimiter.class);
private RateLimiter usernameSetLimiter = mock(RateLimiter.class);
private SmsSender smsSender = mock(SmsSender.class );
private DirectoryQueue directoryQueue = mock(DirectoryQueue.class);
private MessagesManager storedMessages = mock(MessagesManager.class );
@@ -96,6 +98,7 @@ public class AccountControllerTest {
private RecaptchaClient recaptchaClient = mock(RecaptchaClient.class);
private GCMSender gcmSender = mock(GCMSender.class);
private APNSender apnSender = mock(APNSender.class);
private UsernamesManager usernamesManager = mock(UsernamesManager.class);
private byte[] registration_lock_key = new byte[32];
private ExternalServiceCredentialGenerator storageCredentialGenerator = new ExternalServiceCredentialGenerator(new byte[32], new byte[32], false);
@@ -109,6 +112,7 @@ public class AccountControllerTest {
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new AccountController(pendingAccountsManager,
accountsManager,
usernamesManager,
abusiveHostRules,
rateLimiters,
smsSender,
@@ -135,6 +139,7 @@ public class AccountControllerTest {
when(rateLimiters.getSmsVoiceIpLimiter()).thenReturn(smsVoiceIpLimiter);
when(rateLimiters.getSmsVoicePrefixLimiter()).thenReturn(smsVoicePrefixLimiter);
when(rateLimiters.getAutoBlockLimiter()).thenReturn(autoBlockLimiter);
when(rateLimiters.getUsernameSetLimiter()).thenReturn(usernameSetLimiter);
when(timeProvider.getCurrentTimeMillis()).thenReturn(System.currentTimeMillis());
@@ -160,6 +165,9 @@ public class AccountControllerTest {
when(accountsManager.get(eq(SENDER_OLD))).thenReturn(Optional.empty());
when(accountsManager.get(eq(SENDER_PREAUTH))).thenReturn(Optional.empty());
when(usernamesManager.put(eq(AuthHelper.VALID_UUID), eq("n00bkiller"))).thenReturn(true);
when(usernamesManager.put(eq(AuthHelper.VALID_UUID), eq("takenusername"))).thenReturn(false);
when(abusiveHostRules.getAbusiveHostRulesFor(eq(ABUSIVE_HOST))).thenReturn(Collections.singletonList(new AbusiveHostRule(ABUSIVE_HOST, true, Collections.emptyList())));
when(abusiveHostRules.getAbusiveHostRulesFor(eq(RESTRICTED_HOST))).thenReturn(Collections.singletonList(new AbusiveHostRule(RESTRICTED_HOST, false, Collections.singletonList("+123"))));
when(abusiveHostRules.getAbusiveHostRulesFor(eq(NICE_HOST))).thenReturn(Collections.emptyList());
@@ -820,4 +828,77 @@ public class AccountControllerTest {
assertThat(response.getUuid()).isEqualTo(AuthHelper.VALID_UUID);
}
@Test
public void testSetUsername() {
Response response =
resources.getJerseyTest()
.target("/v1/accounts/username/n00bkiller")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
.put(Entity.text(""));
assertThat(response.getStatus()).isEqualTo(200);
}
@Test
public void testSetTakenUsername() {
Response response =
resources.getJerseyTest()
.target("/v1/accounts/username/takenusername")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
.put(Entity.text(""));
assertThat(response.getStatus()).isEqualTo(409);
}
@Test
public void testSetInvalidUsername() {
Response response =
resources.getJerseyTest()
.target("/v1/accounts/username/pаypal")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
.put(Entity.text(""));
assertThat(response.getStatus()).isEqualTo(400);
}
@Test
public void testSetUsernameBadAuth() {
Response response =
resources.getJerseyTest()
.target("/v1/accounts/username/n00bkiller")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.INVALID_PASSWORD))
.put(Entity.text(""));
assertThat(response.getStatus()).isEqualTo(401);
}
@Test
public void testDeleteUsername() {
Response response =
resources.getJerseyTest()
.target("/v1/accounts/username/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
.delete();
assertThat(response.getStatus()).isEqualTo(204);
verify(usernamesManager, times(1)).delete(eq(AuthHelper.VALID_UUID));
}
@Test
public void testDeleteUsernameBadAuth() {
Response response =
resources.getJerseyTest()
.target("/v1/accounts/username/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.INVALID_PASSWORD))
.delete();
assertThat(response.getStatus()).isEqualTo(401);
}
}

View File

@@ -16,6 +16,7 @@ import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
import org.whispersystems.textsecuregcm.util.SystemMapper;
@@ -29,10 +30,12 @@ import static org.mockito.Mockito.*;
public class ProfileControllerTest {
private static AccountsManager accountsManager = mock(AccountsManager.class );
private static RateLimiters rateLimiters = mock(RateLimiters.class );
private static RateLimiter rateLimiter = mock(RateLimiter.class );
private static CdnConfiguration configuration = mock(CdnConfiguration.class);
private static AccountsManager accountsManager = mock(AccountsManager.class );
private static UsernamesManager usernamesManager = mock(UsernamesManager.class);
private static RateLimiters rateLimiters = mock(RateLimiters.class );
private static RateLimiter rateLimiter = mock(RateLimiter.class );
private static RateLimiter usernameRateLimiter = mock(RateLimiter.class );
private static CdnConfiguration configuration = mock(CdnConfiguration.class);
static {
when(configuration.getAccessKey()).thenReturn("accessKey");
@@ -49,12 +52,14 @@ public class ProfileControllerTest {
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new ProfileController(rateLimiters,
accountsManager,
usernamesManager,
configuration))
.build();
@Before
public void setup() throws Exception {
when(rateLimiters.getProfileLimiter()).thenReturn(rateLimiter);
when(rateLimiters.getUsernameLookupLimiter()).thenReturn(usernameRateLimiter);
Account profileAccount = mock(Account.class);
@@ -62,6 +67,7 @@ public class ProfileControllerTest {
when(profileAccount.getProfileName()).thenReturn("baz");
when(profileAccount.getAvatar()).thenReturn("profiles/bang");
when(profileAccount.getAvatarDigest()).thenReturn("buh");
when(profileAccount.getUuid()).thenReturn(AuthHelper.VALID_UUID_TWO);
when(profileAccount.isEnabled()).thenReturn(true);
when(profileAccount.isUuidAddressingSupported()).thenReturn(false);
@@ -75,15 +81,37 @@ public class ProfileControllerTest {
when(capabilitiesAccount.isUuidAddressingSupported()).thenReturn(true);
when(accountsManager.get(AuthHelper.VALID_NUMBER_TWO)).thenReturn(Optional.of(profileAccount));
when(accountsManager.get(AuthHelper.VALID_UUID_TWO)).thenReturn(Optional.of(profileAccount));
when(usernamesManager.get(AuthHelper.VALID_UUID_TWO)).thenReturn(Optional.of("n00bkiller"));
when(usernamesManager.get("n00bkiller")).thenReturn(Optional.of(AuthHelper.VALID_UUID_TWO));
when(accountsManager.get(argThat((ArgumentMatcher<AmbiguousIdentifier>) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(AuthHelper.VALID_NUMBER_TWO)))).thenReturn(Optional.of(profileAccount));
when(accountsManager.get(argThat((ArgumentMatcher<AmbiguousIdentifier>) identifier -> identifier != null && identifier.hasUuid() && identifier.getUuid().equals(AuthHelper.VALID_UUID_TWO)))).thenReturn(Optional.of(profileAccount));
when(accountsManager.get(AuthHelper.VALID_NUMBER)).thenReturn(Optional.of(capabilitiesAccount));
when(accountsManager.get(argThat((ArgumentMatcher<AmbiguousIdentifier>) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(AuthHelper.VALID_NUMBER)))).thenReturn(Optional.of(capabilitiesAccount));
}
@Test
public void testProfileGetByUuid() throws RateLimitExceededException {
Profile profile= resources.getJerseyTest()
.target("/v1/profile/" + AuthHelper.VALID_UUID_TWO)
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
.get(Profile.class);
assertThat(profile.getIdentityKey()).isEqualTo("bar");
assertThat(profile.getName()).isEqualTo("baz");
assertThat(profile.getAvatar()).isEqualTo("profiles/bang");
assertThat(profile.getUsername()).isEqualTo("n00bkiller");
verify(accountsManager, times(1)).get(argThat((ArgumentMatcher<AmbiguousIdentifier>) identifier -> identifier != null && identifier.hasUuid() && identifier.getUuid().equals(AuthHelper.VALID_UUID_TWO)));
verify(usernamesManager, times(1)).get(eq(AuthHelper.VALID_UUID_TWO));
verify(rateLimiter, times(2)).validate(eq(AuthHelper.VALID_NUMBER));
reset(rateLimiter);
}
@Test
public void testProfileGet() throws RateLimitExceededException {
public void testProfileGetByNumber() throws RateLimitExceededException {
Profile profile= resources.getJerseyTest()
.target("/v1/profile/" + AuthHelper.VALID_NUMBER_TWO)
.request()
@@ -94,10 +122,32 @@ public class ProfileControllerTest {
assertThat(profile.getName()).isEqualTo("baz");
assertThat(profile.getAvatar()).isEqualTo("profiles/bang");
assertThat(profile.getCapabilities().isUuid()).isFalse();
assertThat(profile.getUsername()).isNull();
assertThat(profile.getUuid()).isNull();;
verify(accountsManager, times(1)).get(argThat((ArgumentMatcher<AmbiguousIdentifier>) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(AuthHelper.VALID_NUMBER_TWO)));
verify(rateLimiters, times(1)).getProfileLimiter();
verifyNoMoreInteractions(usernamesManager);
verify(rateLimiter, times(1)).validate(eq(AuthHelper.VALID_NUMBER));
reset(rateLimiter);
}
@Test
public void testProfileGetByUsername() throws RateLimitExceededException {
Profile profile= resources.getJerseyTest()
.target("/v1/profile/username/n00bkiller")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
.get(Profile.class);
assertThat(profile.getIdentityKey()).isEqualTo("bar");
assertThat(profile.getName()).isEqualTo("baz");
assertThat(profile.getAvatar()).isEqualTo("profiles/bang");
assertThat(profile.getUsername()).isEqualTo("n00bkiller");
assertThat(profile.getUuid()).isEqualTo(AuthHelper.VALID_UUID_TWO);
verify(accountsManager, times(1)).get(eq(AuthHelper.VALID_UUID_TWO));
verify(usernamesManager, times(1)).get(eq("n00bkiller"));
verify(usernameRateLimiter, times(1)).validate(eq(AuthHelper.VALID_UUID.toString()));
}
@Test
@@ -110,6 +160,33 @@ public class ProfileControllerTest {
assertThat(response.getStatus()).isEqualTo(401);
}
@Test
public void testProfileGetByUsernameUnauthorized() throws Exception {
Response response = resources.getJerseyTest()
.target("/v1/profile/username/n00bkiller")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(401);
}
@Test
public void testProfileGetByUsernameNotFound() throws RateLimitExceededException {
Response response = resources.getJerseyTest()
.target("/v1/profile/username/n00bkillerzzzzz")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
.get();
assertThat(response.getStatus()).isEqualTo(404);
verify(usernamesManager, times(1)).get(eq("n00bkillerzzzzz"));
verify(usernameRateLimiter, times(1)).validate(eq(AuthHelper.VALID_UUID.toString()));
reset(usernameRateLimiter);
}
@Test
public void testProfileGetDisabled() throws Exception {
Response response = resources.getJerseyTest()

View File

@@ -0,0 +1,191 @@
package org.whispersystems.textsecuregcm.tests.storage;
import org.junit.Test;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Accounts;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.DirectoryManager;
import org.whispersystems.textsecuregcm.storage.Usernames;
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
import java.util.HashSet;
import java.util.Optional;
import java.util.UUID;
import static junit.framework.TestCase.assertSame;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisException;
public class UsernamesManagerTest {
@Test
public void testGetByUsernameInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
Jedis jedis = mock(Jedis.class );
Usernames usernames = mock(Usernames.class );
UUID uuid = UUID.randomUUID();
when(cacheClient.getReadResource()).thenReturn(jedis);
when(jedis.get(eq("UsernameByUsername::n00bkiller"))).thenReturn(uuid.toString());
UsernamesManager usernamesManager = new UsernamesManager(usernames, cacheClient);
Optional<UUID> retrieved = usernamesManager.get("n00bkiller");
assertTrue(retrieved.isPresent());
assertEquals(retrieved.get(), uuid);
verify(jedis, times(1)).get(eq("UsernameByUsername::n00bkiller"));
verify(jedis, times(1)).close();
verifyNoMoreInteractions(jedis);
verifyNoMoreInteractions(usernames);
}
@Test
public void testGetByUuidInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
Jedis jedis = mock(Jedis.class );
Usernames usernames = mock(Usernames.class );
UUID uuid = UUID.randomUUID();
when(cacheClient.getReadResource()).thenReturn(jedis);
when(jedis.get(eq("UsernameByUuid::" + uuid.toString()))).thenReturn("n00bkiller");
UsernamesManager usernamesManager = new UsernamesManager(usernames, cacheClient);
Optional<String> retrieved = usernamesManager.get(uuid);
assertTrue(retrieved.isPresent());
assertEquals(retrieved.get(), "n00bkiller");
verify(jedis, times(1)).get(eq("UsernameByUuid::" + uuid.toString()));
verify(jedis, times(1)).close();
verifyNoMoreInteractions(jedis);
verifyNoMoreInteractions(usernames);
}
@Test
public void testGetByUsernameNotInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
Jedis jedis = mock(Jedis.class );
Usernames usernames = mock(Usernames.class );
UUID uuid = UUID.randomUUID();
when(cacheClient.getReadResource()).thenReturn(jedis);
when(cacheClient.getWriteResource()).thenReturn(jedis);
when(jedis.get(eq("UsernameByUsername::n00bkiller"))).thenReturn(null);
when(usernames.get(eq("n00bkiller"))).thenReturn(Optional.of(uuid));
UsernamesManager usernamesManager = new UsernamesManager(usernames, cacheClient);
Optional<UUID> retrieved = usernamesManager.get("n00bkiller");
assertTrue(retrieved.isPresent());
assertSame(retrieved.get(), uuid);
verify(jedis, times(1)).get(eq("UsernameByUsername::n00bkiller"));
verify(jedis, times(1)).set(eq("UsernameByUsername::n00bkiller"), eq(uuid.toString()));
verify(jedis, times(1)).set(eq("UsernameByUuid::" + uuid.toString()), eq("n00bkiller"));
verify(jedis, times(2)).close();
verifyNoMoreInteractions(jedis);
verify(usernames, times(1)).get(eq("n00bkiller"));
verifyNoMoreInteractions(usernames);
}
@Test
public void testGetByUuidNotInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
Jedis jedis = mock(Jedis.class );
Usernames usernames = mock(Usernames.class );
UUID uuid = UUID.randomUUID();
when(cacheClient.getReadResource()).thenReturn(jedis);
when(cacheClient.getWriteResource()).thenReturn(jedis);
when(jedis.get(eq("UsernameByUuid::" + uuid.toString()))).thenReturn(null);
when(usernames.get(eq(uuid))).thenReturn(Optional.of("n00bkiller"));
UsernamesManager usernamesManager = new UsernamesManager(usernames, cacheClient);
Optional<String> retrieved = usernamesManager.get(uuid);
assertTrue(retrieved.isPresent());
assertEquals(retrieved.get(), "n00bkiller");
verify(jedis, times(1)).get(eq("UsernameByUuid::" + uuid));
verify(jedis, times(1)).set(eq("UsernameByUuid::" + uuid), eq("n00bkiller"));
verify(jedis, times(1)).set(eq("UsernameByUsername::n00bkiller"), eq(uuid.toString()));
verify(jedis, times(2)).close();
verifyNoMoreInteractions(jedis);
verify(usernames, times(1)).get(eq(uuid));
verifyNoMoreInteractions(usernames);
}
@Test
public void testGetByUsernameBrokenCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
Jedis jedis = mock(Jedis.class );
Usernames usernames = mock(Usernames.class );
UUID uuid = UUID.randomUUID();
when(cacheClient.getReadResource()).thenReturn(jedis);
when(cacheClient.getWriteResource()).thenReturn(jedis);
when(jedis.get(eq("UsernameByUsername::n00bkiller"))).thenThrow(new JedisException("Connection lost!"));
when(usernames.get(eq("n00bkiller"))).thenReturn(Optional.of(uuid));
UsernamesManager usernamesManager = new UsernamesManager(usernames, cacheClient);
Optional<UUID> retrieved = usernamesManager.get("n00bkiller");
assertTrue(retrieved.isPresent());
assertEquals(retrieved.get(), uuid);
verify(jedis, times(1)).get(eq("UsernameByUsername::n00bkiller"));
verify(jedis, times(1)).set(eq("UsernameByUsername::n00bkiller"), eq(uuid.toString()));
verify(jedis, times(1)).set(eq("UsernameByUuid::" + uuid.toString()), eq("n00bkiller"));
verify(jedis, times(2)).close();
verifyNoMoreInteractions(jedis);
verify(usernames, times(1)).get(eq("n00bkiller"));
verifyNoMoreInteractions(usernames);
}
@Test
public void testGetAccountByUuidBrokenCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
Jedis jedis = mock(Jedis.class );
Usernames usernames = mock(Usernames.class );
UUID uuid = UUID.randomUUID();
when(cacheClient.getReadResource()).thenReturn(jedis);
when(cacheClient.getWriteResource()).thenReturn(jedis);
when(jedis.get(eq("UsernameByUuid::" + uuid))).thenThrow(new JedisException("Connection lost!"));
when(usernames.get(eq(uuid))).thenReturn(Optional.of("n00bkiller"));
UsernamesManager usernamesManager = new UsernamesManager(usernames, cacheClient);
Optional<String> retrieved = usernamesManager.get(uuid);
assertTrue(retrieved.isPresent());
assertEquals(retrieved.get(), "n00bkiller");
verify(jedis, times(1)).get(eq("UsernameByUuid::" + uuid));
verify(jedis, times(1)).set(eq("UsernameByUsername::n00bkiller"), eq(uuid.toString()));
verify(jedis, times(1)).set(eq("UsernameByUuid::" + uuid.toString()), eq("n00bkiller"));
verify(jedis, times(2)).close();
verifyNoMoreInteractions(jedis);
verify(usernames, times(1)).get(eq(uuid));
verifyNoMoreInteractions(usernames);
}
}

View File

@@ -0,0 +1,164 @@
package org.whispersystems.textsecuregcm.tests.storage;
import com.opentable.db.postgres.embedded.LiquibasePreparer;
import com.opentable.db.postgres.junit.EmbeddedPostgresRules;
import com.opentable.db.postgres.junit.PreparedDbRule;
import org.jdbi.v3.core.Jdbi;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
import org.whispersystems.textsecuregcm.storage.FaultTolerantDatabase;
import org.whispersystems.textsecuregcm.storage.Usernames;
import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Optional;
import java.util.UUID;
import static junit.framework.TestCase.assertTrue;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.Assert.assertFalse;
public class UsernamesTest {
@Rule
public PreparedDbRule db = EmbeddedPostgresRules.preparedDatabase(LiquibasePreparer.forClasspathLocation("accountsdb.xml"));
private Usernames usernames;
@Before
public void setupAccountsDao() {
FaultTolerantDatabase faultTolerantDatabase = new FaultTolerantDatabase("usernamesTest",
Jdbi.create(db.getTestDatabase()),
new CircuitBreakerConfiguration());
this.usernames = new Usernames(faultTolerantDatabase);
}
@Test
public void testPut() throws SQLException, IOException {
UUID uuid = UUID.randomUUID();
String username = "myusername";
assertTrue(usernames.put(uuid, username));
PreparedStatement statement = db.getTestDatabase().getConnection().prepareStatement("SELECT * FROM usernames WHERE uuid = ?");
verifyStoredState(statement, uuid, username);
}
@Test
public void testPutChange() throws SQLException, IOException {
UUID uuid = UUID.randomUUID();
String firstUsername = "myfirstusername";
String secondUsername = "mysecondusername";
assertTrue(usernames.put(uuid, firstUsername));
PreparedStatement statement = db.getTestDatabase().getConnection().prepareStatement("SELECT * FROM usernames WHERE uuid = ?");
verifyStoredState(statement, uuid, firstUsername);
assertTrue(usernames.put(uuid, secondUsername));
verifyStoredState(statement, uuid, secondUsername);
}
@Test
public void testPutConflict() throws SQLException {
UUID firstUuid = UUID.randomUUID();
UUID secondUuid = UUID.randomUUID();
String username = "myfirstusername";
assertTrue(usernames.put(firstUuid, username));
assertFalse(usernames.put(secondUuid, username));
PreparedStatement statement = db.getTestDatabase().getConnection().prepareStatement("SELECT * FROM usernames WHERE username = ?");
statement.setString(1, username);
ResultSet resultSet = statement.executeQuery();
assertTrue(resultSet.next());
assertThat(resultSet.getString("uuid")).isEqualTo(firstUuid.toString());
assertThat(resultSet.next()).isFalse();
}
@Test
public void testGetByUuid() {
UUID uuid = UUID.randomUUID();
String username = "myusername";
assertTrue(usernames.put(uuid, username));
Optional<String> retrieved = usernames.get(uuid);
assertTrue(retrieved.isPresent());
assertThat(retrieved.get()).isEqualTo(username);
}
@Test
public void testGetByUuidMissing() {
Optional<String> retrieved = usernames.get(UUID.randomUUID());
assertFalse(retrieved.isPresent());
}
@Test
public void testGetByUsername() {
UUID uuid = UUID.randomUUID();
String username = "myusername";
assertTrue(usernames.put(uuid, username));
Optional<UUID> retrieved = usernames.get(username);
assertTrue(retrieved.isPresent());
assertThat(retrieved.get()).isEqualTo(uuid);
}
@Test
public void testGetByUsernameMissing() {
Optional<UUID> retrieved = usernames.get("myusername");
assertFalse(retrieved.isPresent());
}
@Test
public void testDelete() {
UUID uuid = UUID.randomUUID();
String username = "myusername";
assertTrue(usernames.put(uuid, username));
Optional<UUID> retrieved = usernames.get(username);
assertTrue(retrieved.isPresent());
assertThat(retrieved.get()).isEqualTo(uuid);
usernames.delete(uuid);
assertThat(usernames.get(uuid).isPresent()).isFalse();
}
private void verifyStoredState(PreparedStatement statement, UUID uuid, String expectedUsername)
throws SQLException, IOException
{
statement.setObject(1, uuid);
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
String data = resultSet.getString("username");
assertThat(data).isNotEmpty();
assertThat(data).isEqualTo(expectedUsername);
} else {
throw new AssertionError("No data");
}
assertThat(resultSet.next()).isFalse();
}
}