mirror of
https://github.com/imgfloat/server.git
synced 2026-05-08 10:19:35 +00:00
refactor+test: add MarketplaceService and SettingsService unit tests; use StringNormalizer in AccountService
This commit is contained in:
@@ -7,8 +7,8 @@ import dev.kruhlmann.imgfloat.repository.ChannelRepository;
|
|||||||
import dev.kruhlmann.imgfloat.repository.MarketplaceScriptHeartRepository;
|
import dev.kruhlmann.imgfloat.repository.MarketplaceScriptHeartRepository;
|
||||||
import dev.kruhlmann.imgfloat.repository.ScriptAssetFileRepository;
|
import dev.kruhlmann.imgfloat.repository.ScriptAssetFileRepository;
|
||||||
import dev.kruhlmann.imgfloat.repository.SystemAdministratorRepository;
|
import dev.kruhlmann.imgfloat.repository.SystemAdministratorRepository;
|
||||||
|
import dev.kruhlmann.imgfloat.util.StringNormalizer;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
@@ -96,6 +96,6 @@ public class AccountService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String normalize(String value) {
|
private String normalize(String value) {
|
||||||
return value == null ? null : value.toLowerCase(Locale.ROOT);
|
return StringNormalizer.toLowerCaseRoot(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package dev.kruhlmann.imgfloat.service;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyList;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import dev.kruhlmann.imgfloat.model.api.response.ScriptMarketplaceEntry;
|
||||||
|
import dev.kruhlmann.imgfloat.model.db.imgfloat.MarketplaceScriptHeart;
|
||||||
|
import dev.kruhlmann.imgfloat.model.db.imgfloat.ScriptAsset;
|
||||||
|
import dev.kruhlmann.imgfloat.repository.AssetRepository;
|
||||||
|
import dev.kruhlmann.imgfloat.repository.MarketplaceScriptHeartRepository;
|
||||||
|
import dev.kruhlmann.imgfloat.repository.ScriptAssetFileRepository;
|
||||||
|
import dev.kruhlmann.imgfloat.repository.ScriptAssetRepository;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class MarketplaceServiceTest {
|
||||||
|
|
||||||
|
private MarketplaceService service;
|
||||||
|
private ScriptAssetRepository scriptAssetRepository;
|
||||||
|
private MarketplaceScriptHeartRepository heartRepository;
|
||||||
|
private MarketplaceScriptSeedLoader seedLoader;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() throws Exception {
|
||||||
|
scriptAssetRepository = mock(ScriptAssetRepository.class);
|
||||||
|
AssetRepository assetRepository = mock(AssetRepository.class);
|
||||||
|
ScriptAssetFileRepository scriptAssetFileRepository = mock(ScriptAssetFileRepository.class);
|
||||||
|
AssetStorageService assetStorageService = mock(AssetStorageService.class);
|
||||||
|
heartRepository = mock(MarketplaceScriptHeartRepository.class);
|
||||||
|
|
||||||
|
when(scriptAssetRepository.findByIsPublicTrue()).thenReturn(List.of());
|
||||||
|
when(heartRepository.countByScriptIds(anyList())).thenReturn(List.of());
|
||||||
|
when(heartRepository.findByUsernameAndScriptIdIn(anyString(), anyList())).thenReturn(List.of());
|
||||||
|
|
||||||
|
Path marketplaceRoot = Files.createTempDirectory("imgfloat-marketplace-test");
|
||||||
|
Path scriptDir = marketplaceRoot.resolve("test-script");
|
||||||
|
Files.createDirectories(scriptDir);
|
||||||
|
Files.writeString(scriptDir.resolve("metadata.json"),
|
||||||
|
"{\"name\":\"Test Script\",\"description\":\"A test script\"}");
|
||||||
|
Files.writeString(scriptDir.resolve("source.js"), "exports.init = function() {};");
|
||||||
|
|
||||||
|
seedLoader = new MarketplaceScriptSeedLoader(marketplaceRoot.toString());
|
||||||
|
service = new MarketplaceService(
|
||||||
|
seedLoader,
|
||||||
|
scriptAssetRepository,
|
||||||
|
assetRepository,
|
||||||
|
scriptAssetFileRepository,
|
||||||
|
assetStorageService,
|
||||||
|
heartRepository
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void listScriptsReturnsSeedEntries() {
|
||||||
|
List<ScriptMarketplaceEntry> entries = service.listScripts(null, null);
|
||||||
|
assertThat(entries).anyMatch(e -> "test-script".equals(e.id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void listScriptsFiltersEntriesByQuery() {
|
||||||
|
List<ScriptMarketplaceEntry> entries = service.listScripts("test", null);
|
||||||
|
assertThat(entries).anyMatch(e -> "test-script".equals(e.id()));
|
||||||
|
|
||||||
|
List<ScriptMarketplaceEntry> noMatch = service.listScripts("zzznomatch", null);
|
||||||
|
assertThat(noMatch).noneMatch(e -> "test-script".equals(e.id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void listScriptsIncludesPublicDatabaseScripts() {
|
||||||
|
ScriptAsset publicScript = new ScriptAsset();
|
||||||
|
publicScript.setId("db-script");
|
||||||
|
publicScript.setName("DB Script");
|
||||||
|
publicScript.setPublic(true);
|
||||||
|
when(scriptAssetRepository.findByIsPublicTrue()).thenReturn(List.of(publicScript));
|
||||||
|
when(heartRepository.countByScriptIds(anyList())).thenReturn(List.of());
|
||||||
|
|
||||||
|
List<ScriptMarketplaceEntry> entries = service.listScripts(null, null);
|
||||||
|
assertThat(entries).anyMatch(e -> "db-script".equals(e.id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toggleHeartAddsHeartWhenNotAlreadyHearted() {
|
||||||
|
String scriptId = "test-script";
|
||||||
|
String username = "user1";
|
||||||
|
when(heartRepository.existsByScriptIdAndUsername(scriptId, username)).thenReturn(false);
|
||||||
|
when(heartRepository.countByScriptId(scriptId)).thenReturn(1L);
|
||||||
|
|
||||||
|
Optional<ScriptMarketplaceEntry> result = service.toggleHeart(scriptId, username);
|
||||||
|
assertThat(result).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toggleHeartReturnsEmptyForBlankInputs() {
|
||||||
|
assertThat(service.toggleHeart(null, "user")).isEmpty();
|
||||||
|
assertThat(service.toggleHeart("script", null)).isEmpty();
|
||||||
|
assertThat(service.toggleHeart("", "user")).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void listScriptsWithBlankQueryTreatsAsNoFilter() {
|
||||||
|
List<ScriptMarketplaceEntry> withNull = service.listScripts(null, null);
|
||||||
|
List<ScriptMarketplaceEntry> withBlank = service.listScripts(" ", null);
|
||||||
|
assertThat(withBlank).hasSize(withNull.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package dev.kruhlmann.imgfloat.service;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import dev.kruhlmann.imgfloat.model.db.imgfloat.AudioAsset;
|
||||||
|
import dev.kruhlmann.imgfloat.model.db.imgfloat.Settings;
|
||||||
|
import dev.kruhlmann.imgfloat.model.db.imgfloat.VisualAsset;
|
||||||
|
import dev.kruhlmann.imgfloat.repository.AudioAssetRepository;
|
||||||
|
import dev.kruhlmann.imgfloat.repository.SettingsRepository;
|
||||||
|
import dev.kruhlmann.imgfloat.repository.VisualAssetRepository;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class SettingsServiceTest {
|
||||||
|
|
||||||
|
private SettingsRepository repo;
|
||||||
|
private VisualAssetRepository visualAssetRepository;
|
||||||
|
private AudioAssetRepository audioAssetRepository;
|
||||||
|
private SettingsService service;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() {
|
||||||
|
repo = mock(SettingsRepository.class);
|
||||||
|
visualAssetRepository = mock(VisualAssetRepository.class);
|
||||||
|
audioAssetRepository = mock(AudioAssetRepository.class);
|
||||||
|
when(visualAssetRepository.findAll()).thenReturn(List.of());
|
||||||
|
when(audioAssetRepository.findAll()).thenReturn(List.of());
|
||||||
|
service = new SettingsService(repo, visualAssetRepository, audioAssetRepository, new ObjectMapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void initDefaultsCreatesSettingsWhenAbsent() {
|
||||||
|
when(repo.existsById(1)).thenReturn(false);
|
||||||
|
service.initDefaults();
|
||||||
|
verify(repo).save(any(Settings.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void initDefaultsSkipsWhenAlreadyPresent() {
|
||||||
|
when(repo.existsById(1)).thenReturn(true);
|
||||||
|
service.initDefaults();
|
||||||
|
verify(repo, never()).save(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getReturnsStoredSettings() {
|
||||||
|
Settings stored = Settings.defaults();
|
||||||
|
stored.setId(1);
|
||||||
|
when(repo.findById(1)).thenReturn(Optional.of(stored));
|
||||||
|
assertThat(service.get()).isSameAs(stored);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void saveClampsVisualAssetSpeedToNewRange() {
|
||||||
|
VisualAsset visual = new VisualAsset();
|
||||||
|
visual.setSpeed(5.0); // exceeds max
|
||||||
|
visual.setAudioVolume(0.5);
|
||||||
|
when(visualAssetRepository.findAll()).thenReturn(List.of(visual));
|
||||||
|
|
||||||
|
Settings settings = Settings.defaults();
|
||||||
|
settings.setMaxAssetPlaybackSpeedFraction(2.0);
|
||||||
|
settings.setMinAssetPlaybackSpeedFraction(0.1);
|
||||||
|
settings.setId(1);
|
||||||
|
when(repo.save(any())).thenReturn(settings);
|
||||||
|
|
||||||
|
service.save(settings);
|
||||||
|
|
||||||
|
verify(visualAssetRepository).saveAll(any());
|
||||||
|
assertThat(visual.getSpeed()).isEqualTo(2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void saveClampsAudioAssetPitchToNewRange() {
|
||||||
|
AudioAsset audio = new AudioAsset();
|
||||||
|
audio.setAudioSpeed(1.0);
|
||||||
|
audio.setAudioPitch(3.0); // exceeds max
|
||||||
|
audio.setAudioVolume(0.5);
|
||||||
|
when(audioAssetRepository.findAll()).thenReturn(List.of(audio));
|
||||||
|
|
||||||
|
Settings settings = Settings.defaults();
|
||||||
|
settings.setMaxAssetAudioPitchFraction(2.0);
|
||||||
|
settings.setMinAssetAudioPitchFraction(0.5);
|
||||||
|
settings.setId(1);
|
||||||
|
when(repo.save(any())).thenReturn(settings);
|
||||||
|
|
||||||
|
service.save(settings);
|
||||||
|
|
||||||
|
verify(audioAssetRepository).saveAll(any());
|
||||||
|
assertThat(audio.getAudioPitch()).isEqualTo(2.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user