fix revokation bugs and add unit tests
All checks were successful
CI / Maven unit tests (push) Successful in 18s
CI / Build and push images (push) Has been skipped
CI / Deploy to server (push) Has been skipped

This commit is contained in:
2026-10-15 20:55:40 +01:00
parent 954a3b7f6e
commit cf951afbae
4 changed files with 149 additions and 38 deletions

View File

@@ -17,6 +17,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.security.MessageDigest;
import java.time.*;
import java.util.HexFormat;
import java.util.Optional;
@@ -518,20 +519,110 @@ class TokenServiceTest {
verify(refreshTokenRepository, never()).save(any());
verifyNoInteractions(tokenFamilyRepository);
}
}
private TokenFamily activeFamily() {
TokenFamily tokenFamily = new TokenFamily();
tokenFamily.setId(1L);
tokenFamily.setUser(createUser(1L, "user", "user"));
tokenFamily.setExpiresAt(clock.instant().plusSeconds(10));
return tokenFamily;
@Nested
@DisplayName("revoke()")
class revoke {
@Test
void knownToken_revokesTokenAndFamily() {
TokenFamily tokenFamily = activeFamily();
RefreshToken refreshToken = tokenIn(tokenFamily);
when(refreshTokenRepository.findByHash(any())).thenReturn(Optional.of(refreshToken));
tokenService.revoke(rawToken);
assertEquals(clock.instant(), tokenFamily.getRevokedAt());
assertEquals(clock.instant(), refreshToken.getRevokedAt());
}
private RefreshToken tokenIn(TokenFamily tokenFamily) {
RefreshToken refreshToken = new RefreshToken();
refreshToken.setTokenFamily(tokenFamily);
refreshToken.setExpiresAt(clock.instant().plusSeconds(10));
return refreshToken;
@Test
void knownToken_savesTokenAndFamily() {
TokenFamily tokenFamily = activeFamily();
RefreshToken refreshToken = tokenIn(tokenFamily);
when(refreshTokenRepository.findByHash(any())).thenReturn(Optional.of(refreshToken));
tokenService.revoke(rawToken);
verify(tokenFamilyRepository).save(tokenFamily);
verify(refreshTokenRepository).save(refreshToken);
}
@Test
void knownToken_looksUpBySingleSha256Hash() throws Exception {
when(refreshTokenRepository.findByHash(any())).thenReturn(Optional.empty());
tokenService.revoke(rawToken);
byte[] expectedHash = MessageDigest.getInstance("SHA-256").digest(HEX.parseHex(rawToken));
verify(refreshTokenRepository).findByHash(expectedHash);
}
@Test
void alreadyRevoked_keepsOriginalTimestampsAndDoesNotSave() {
Instant revokedAt = clock.instant();
TokenFamily tokenFamily = activeFamily();
tokenFamily.setRevokedAt(revokedAt);
RefreshToken refreshToken = tokenIn(tokenFamily);
refreshToken.setRevokedAt(revokedAt);
when(refreshTokenRepository.findByHash(any())).thenReturn(Optional.of(refreshToken));
clock.advance(Duration.ofMinutes(1));
tokenService.revoke(rawToken);
assertEquals(revokedAt, tokenFamily.getRevokedAt());
assertEquals(revokedAt, refreshToken.getRevokedAt());
verify(tokenFamilyRepository, never()).save(any());
verify(refreshTokenRepository, never()).save(any());
}
@Test
void unknownToken_doesNothing() {
when(refreshTokenRepository.findByHash(any())).thenReturn(Optional.empty());
tokenService.revoke(rawToken);
verify(refreshTokenRepository, never()).save(any());
verifyNoInteractions(tokenFamilyRepository);
}
@Test
void nullToken_doesNothing() {
tokenService.revoke(null);
verifyNoInteractions(refreshTokenRepository, tokenFamilyRepository);
}
@Test
void blankToken_doesNothing() {
tokenService.revoke("");
verifyNoInteractions(refreshTokenRepository, tokenFamilyRepository);
}
@Test
void malformedToken_throwsNotAuthenticated() {
assertThrows(
NotAuthenticated.class,
() -> tokenService.revoke("not-hex")
);
verifyNoInteractions(refreshTokenRepository, tokenFamilyRepository);
}
}
}
private TokenFamily activeFamily() {
TokenFamily tokenFamily = new TokenFamily();
tokenFamily.setId(1L);
tokenFamily.setUser(createUser(1L, "user", "user"));
tokenFamily.setExpiresAt(clock.instant().plusSeconds(10));
return tokenFamily;
}
private RefreshToken tokenIn(TokenFamily tokenFamily) {
RefreshToken refreshToken = new RefreshToken();
refreshToken.setTokenFamily(tokenFamily);
refreshToken.setExpiresAt(clock.instant().plusSeconds(10));
return refreshToken;
}
}