feat(library): Steam name and avatar for the header

This commit is contained in:
lobo committed 2026-09-24 15:27:33 +02:00
1 parent 00a7991380
commit ba75c00301
8 files changed
+258 -2

No files matched your search

@@ -0,0 +1,52 @@
package core_service.modules.library.api;
import core_service.modules.steam_adapter.api.dto.response.SteamPlayerSummaryResponse;
import core_service.modules.steam_adapter.service.SteamApiService;
import core_service.shared.test.AbstractIntegrationTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Optional;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* The dev profile authenticates every request as user 1 with Steam ID 76561198000000001.
*/
@AutoConfigureMockMvc
class LibraryControllerSteamProfileTest extends AbstractIntegrationTest {
@Autowired
private MockMvc mockMvc;
@MockitoBean
private SteamApiService steamApiService;
@Test
void steamProfile_SendsNameAndAvatar() throws Exception {
when(steamApiService.getPlayerSummary("76561198000000001")).thenReturn(Optional.of(new SteamPlayerSummaryResponse.Player(
"76561198000000001", "Lobo", null, "https://avatars.steamstatic.com/abc_full.jpg")));
mockMvc.perform(get("/api/library/steam-profile"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.personaName").value("Lobo"))
.andExpect(jsonPath("$.avatarUrl").value("https://avatars.steamstatic.com/abc_full.jpg"));
}
@Test
void steamProfile_SteamDoesNotAnswer_SendsEmptyFields() throws Exception {
when(steamApiService.getPlayerSummary("76561198000000001")).thenReturn(Optional.empty());
mockMvc.perform(get("/api/library/steam-profile"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.personaName").value(nullValue()))
.andExpect(jsonPath("$.avatarUrl").value(nullValue()));
}
}
@@ -0,0 +1,58 @@
package core_service.modules.library.service;
import core_service.modules.library.api.dto.SteamProfileDto;
import core_service.modules.steam_adapter.api.dto.response.SteamPlayerSummaryResponse;
import core_service.modules.steam_adapter.service.SteamApiService;
import core_service.shared.test.AbstractUnitTest;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class SteamProfileServiceTest extends AbstractUnitTest {
private static final String AVATAR = "https://avatars.steamstatic.com/abc_full.jpg";
@Mock
private SteamApiService steamApiService;
@InjectMocks
private SteamProfileService service;
@Test
void getProfile_TakesNameAndAvatarFromSteam() {
when(steamApiService.getPlayerSummary("76561198000000001")).thenReturn(Optional.of(
new SteamPlayerSummaryResponse.Player("76561198000000001", "Lobo", "https://steamcommunity.com/id/lobo", AVATAR)));
assertEquals(new SteamProfileDto("Lobo", AVATAR), service.getProfile(76561198000000001L));
}
@Test
void getProfile_AvatarThatIsNotHttps_IsLeftOut() {
when(steamApiService.getPlayerSummary(any())).thenReturn(Optional.of(
new SteamPlayerSummaryResponse.Player("1", "Lobo", null, "javascript:alert(1)")));
assertEquals(new SteamProfileDto("Lobo", null), service.getProfile(1L));
}
@Test
void getProfile_SteamDoesNotAnswer_IsEmpty() {
when(steamApiService.getPlayerSummary(any())).thenReturn(Optional.empty());
assertEquals(new SteamProfileDto(null, null), service.getProfile(1L));
}
@Test
void getProfile_NoSteamId_AsksSteamNothing() {
assertEquals(new SteamProfileDto(null, null), service.getProfile(null));
verify(steamApiService, never()).getPlayerSummary(any());
}
}
@@ -0,0 +1,101 @@
package core_service.modules.steam_adapter.service;
import core_service.modules.steam_adapter.api.dto.response.SteamPlayerSummaryResponse;
import core_service.shared.test.AbstractIntegrationTest;
import io.github.resilience4j.ratelimiter.RateLimiter;
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.mockito.Answers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.web.client.RestClient;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
/**
* The caching and resilience4j proxies around the real SteamApiService, with only the HTTP client mocked.
*/
class SteamApiServiceCacheTest extends AbstractIntegrationTest {
private static final String STEAM_ID = "76561198000000001";
@MockitoBean(name = "steamRestClient", answers = Answers.RETURNS_DEEP_STUBS)
private RestClient steamRestClient;
@Autowired
private SteamApiService steamApiService;
@Autowired
private RateLimiterRegistry rateLimiterRegistry;
@Autowired
private CacheManager cacheManager;
private final AtomicInteger steamCalls = new AtomicInteger();
@AfterEach
void restore() {
RateLimiter limiter = rateLimiterRegistry.rateLimiter("steamApi");
limiter.changeTimeoutDuration(Duration.ofSeconds(5));
cacheManager.getCache("steamPlayerSummary").clear();
}
private void steamAnswers(SteamPlayerSummaryResponse response) {
when(steamRestClient.get().uri(any(Function.class)).retrieve().body(SteamPlayerSummaryResponse.class))
.thenAnswer(invocation -> {
steamCalls.incrementAndGet();
return response;
});
}
private static SteamPlayerSummaryResponse player() {
return new SteamPlayerSummaryResponse(new SteamPlayerSummaryResponse.SteamResponse(List.of(
new SteamPlayerSummaryResponse.Player(STEAM_ID, "Lobo", null, "https://avatars.steamstatic.com/a_full.jpg"))));
}
@Test
void getPlayerSummary_AsksSteamOncePerSteamId() {
steamAnswers(player());
assertThat(steamApiService.getPlayerSummary(STEAM_ID)).isPresent();
assertThat(steamApiService.getPlayerSummary(STEAM_ID)).isPresent();
assertThat(steamCalls).hasValue(1);
}
@Test
void getPlayerSummary_NoPlayer_IsNotCached() {
steamAnswers(new SteamPlayerSummaryResponse(new SteamPlayerSummaryResponse.SteamResponse(List.of())));
assertThat(steamApiService.getPlayerSummary(STEAM_ID)).isEmpty();
assertThat(steamApiService.getPlayerSummary(STEAM_ID)).isEmpty();
assertThat(steamCalls).hasValue(2);
}
// The header and the Dashboard ask on every page load, also while a library sync uses all Steam permits.
@Test
void getPlayerSummary_CacheHit_TakesNoRateLimiterPermit() {
steamAnswers(player());
assertThat(steamApiService.getPlayerSummary(STEAM_ID)).isPresent();
assertThat(steamCalls).hasValue(1);
RateLimiter limiter = rateLimiterRegistry.rateLimiter("steamApi");
limiter.changeTimeoutDuration(Duration.ZERO);
while (limiter.acquirePermission()) {
// drain the current period
}
assertThat(steamApiService.getPlayerSummary(STEAM_ID)).isPresent();
assertThat(steamCalls).hasValue(1);
}
}