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.ScriptAssetFileRepository;
|
||||
import dev.kruhlmann.imgfloat.repository.SystemAdministratorRepository;
|
||||
import dev.kruhlmann.imgfloat.util.StringNormalizer;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@@ -96,6 +96,6 @@ public class AccountService {
|
||||
}
|
||||
|
||||
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