refactor: implement dependency injection for encryption and enhance model analysis

This commit is contained in:
Thomas
2026-04-25 15:06:56 +02:00
parent 7b9743e490
commit fd1fcaa221
25 changed files with 638 additions and 304 deletions
@@ -0,0 +1,67 @@
package de.tki.comfymodels;
import de.tki.comfymodels.domain.ModelInfo;
import de.tki.comfymodels.service.impl.ComfyModelAnalyzer;
import de.tki.comfymodels.service.impl.WorkflowService;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
/**
* Ported from BatchVerification.java main method.
* This test is disabled by default as it requires local files.
*/
public class BatchVerificationTest {
@Test
@Disabled("Requires local ComfyUI workflow files")
public void testBatchVerification() throws Exception {
ComfyModelAnalyzer analyzer = new ComfyModelAnalyzer();
WorkflowService workflowService = new WorkflowService();
// Path can be adjusted as needed
Path root = Paths.get("C:/pinokio/api/comfy.git/workflows/ComfyUI_examples");
if (!Files.exists(root)) {
System.out.println("Root path does not exist, skipping batch verification: " + root);
return;
}
System.out.println("=== BATCH VERIFICATION START ===");
Files.walk(root)
.filter(p -> p.toString().endsWith(".json") || p.toString().endsWith(".png"))
.limit(30)
.forEach(path -> {
try {
System.out.println("\nFILE: " + path.getFileName());
String content = workflowService.extractWorkflow(path.toFile());
if (content == null) {
System.out.println(" [!] No workflow found");
return;
}
List<ModelInfo> results = analyzer.analyze(content, path.getFileName().toString());
if (results.isEmpty()) {
System.out.println(" [?] No models detected");
} else {
for (ModelInfo info : results) {
System.out.printf(" [%s] %s | URL: %s | Source: %s\n",
info.getType(), info.getName(),
info.getUrl().equals("MISSING") ? "Missing" : "FOUND",
info.getPopularity());
}
}
} catch (Exception e) {
System.out.println(" [ERROR] " + e.getMessage());
}
});
System.out.println("\n=== BATCH VERIFICATION END ===");
}
}
@@ -4,6 +4,7 @@ import com.sun.net.httpserver.HttpServer;
import de.tki.comfymodels.domain.ModelInfo;
import de.tki.comfymodels.service.impl.ConfigService;
import de.tki.comfymodels.service.impl.DefaultDownloadManager;
import de.tki.comfymodels.service.impl.EncryptionUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -15,7 +16,6 @@ import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -36,8 +36,9 @@ public class DownloadStatusTest {
tempDir = Files.createTempDirectory("downloader_test");
downloadManager = new DefaultDownloadManager();
// Use real ConfigService
ConfigService configService = new ConfigService();
// Use real ConfigService and EncryptionUtils
EncryptionUtils encryptionUtils = new EncryptionUtils();
ConfigService configService = new ConfigService(encryptionUtils);
Field field = DefaultDownloadManager.class.getDeclaredField("configService");
field.setAccessible(true);
@@ -50,7 +51,7 @@ public class DownloadStatusTest {
exchange.getResponseHeaders().add("Content-Length", String.valueOf(data.length));
exchange.sendResponseHeaders(200, data.length);
try (OutputStream os = exchange.getResponseBody()) {
// Sende Daten langsam, damit wir Pause/Stop testen können
// Send data slowly so we can test pause/stop
for (int i = 0; i < 10; i++) {
os.write(data, i * (data.length / 10), data.length / 10);
os.flush();
@@ -86,7 +87,7 @@ public class DownloadStatusTest {
finishedLatch::countDown
);
// 1. Prüfe auf Start/Download
// 1. Check for Start/Download
Thread.sleep(300);
assertTrue(capturedStatus.stream().anyMatch(s -> s.contains("Downloading")), "Should show Downloading status");
@@ -27,8 +27,8 @@ public class HiDreamReproductionTest {
ComfyModelAnalyzer analyzer = new ComfyModelAnalyzer();
List<ModelInfo> models = analyzer.analyze(json, "workflow.json");
// Wir erwarten 7 Modelle: 1 UNET, 4 CLIPs, 1 VAE (aus Loadern) UND 1 UNET (aus der MarkdownNote)
assertEquals(7, models.size(), "Should detect 7 models (including one from MarkdownNote)");
// We expect 6 models: 1 UNET, 4 CLIPs, 1 VAE (from loaders). The UNET in MarkdownNote is now ignored.
assertEquals(6, models.size(), "Should detect 6 models (ignoring one from MarkdownNote)");
boolean foundUnet = false;
int clipsFound = 0;
@@ -51,7 +51,7 @@ public class HiDreamReproductionTest {
assertTrue(foundVae, "VAE not found");
assertEquals(4, clipsFound, "Should find 4 CLIP/TextEncoder models");
// Überprüfe ob der Kontext "hidream" erkannt wurde
// Check if context "hidream" was detected
boolean contextDetected = false;
for (ModelInfo m : models) {
System.out.println("Model: " + m.getName() + " | Popularity: " + m.getPopularity());
@@ -13,7 +13,7 @@ public class QualityAssuranceTest {
public void testFalsePositiveFiltering() {
ComfyModelAnalyzer analyzer = new ComfyModelAnalyzer();
// Workflow mit "None" und "default" Werten in Widgets
// Workflow with "None" and "default" values in widgets
String json = "{" +
"\"nodes\": [" +
" {\"id\": 1, \"type\": \"CheckpointLoaderSimple\", \"widgets_values\": [\"None\", \"default\"]}," +
@@ -23,7 +23,7 @@ public class QualityAssuranceTest {
List<ModelInfo> models = analyzer.analyze(json, "test.json");
// Es sollte nur "test.safetensors" gefunden werden
// Only "test.safetensors" should be found
assertEquals(1, models.size(), "Should only find one real model");
assertEquals("test.safetensors", models.get(0).getName());
}
@@ -32,12 +32,12 @@ public class QualityAssuranceTest {
public void testMalformedJsonHandling() {
ComfyModelAnalyzer analyzer = new ComfyModelAnalyzer();
// Kaputtes JSON
String json = "{ \"nodes\": [ { \"id\": 1, \"type\": \"VAELoader\", \"widgets_values\": [\"ae.safetensors\""; // Fehlende Klammern
// Broken JSON
String json = "{ \"nodes\": [ { \"id\": 1, \"type\": \"VAELoader\", \"widgets_values\": [\"ae.safetensors\""; // Missing brackets
List<ModelInfo> models = analyzer.analyze(json, "broken.json");
// Sollte nicht abstürzen, sondern einfach 0 oder gefundene Modelle (je nach Parser-Robustheit) zurückgeben
// Should not crash, but simply return 0 or found models (depending on parser robustness)
assertNotNull(models);
}
}
@@ -37,8 +37,8 @@ public class ReproductionTest {
List<ModelInfo> models = analyzer.analyze(json, "test.json");
// Erwartung: Nur EIN Eintrag für qwen_3_4b.safetensors
// Der Name darf NICHT die URL sein.
// Expectation: Only ONE entry for qwen_3_4b.safetensors
// The name must NOT be the URL.
long count = models.stream()
.filter(m -> m.getName().toLowerCase().contains("qwen"))
.count();
@@ -0,0 +1,80 @@
package de.tki.comfymodels.service;
import de.tki.comfymodels.domain.ModelInfo;
import de.tki.comfymodels.service.impl.ComfyModelAnalyzer;
import de.tki.comfymodels.service.impl.LocalAIService;
import de.tki.comfymodels.service.impl.ModelListService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class CosmosRegressionTest {
private ComfyModelAnalyzer analyzer;
@BeforeEach
public void setup() {
analyzer = new ComfyModelAnalyzer();
LocalAIService aiService = new LocalAIService();
aiService.init();
ReflectionTestUtils.setField(analyzer, "aiService", aiService);
ReflectionTestUtils.setField(analyzer, "modelListService", new ModelListService());
}
@Test
public void testFullCosmosWorkflow() {
String json = "{\n" +
" \"last_node_id\": 84,\n" +
" \"last_link_id\": 198,\n" +
" \"nodes\": [\n" +
" {\n" +
" \"id\": 38,\n" +
" \"type\": \"CLIPLoader\",\n" +
" \"widgets_values\": [\n" +
" \"oldt5_xxl_fp8_e4m3fn_scaled.safetensors\",\n" +
" \"cosmos\",\n" +
" \"default\"\n" +
" ]\n" +
" },\n" +
" {\n" +
" \"id\": 39,\n" +
" \"type\": \"VAELoader\",\n" +
" \"widgets_values\": [\n" +
" \"cosmos_cv8x8x8_1.0.safetensors\"\n" +
" ]\n" +
" },\n" +
" {\n" +
" \"id\": 37,\n" +
" \"type\": \"UNETLoader\",\n" +
" \"widgets_values\": [\n" +
" \"Cosmos-1_0-Diffusion-7B-Video2World.safetensors\",\n" +
" \"default\"\n" +
" ]\n" +
" },\n" +
" {\n" +
" \"id\": 83,\n" +
" \"type\": \"CosmosImageToVideoLatent\",\n" +
" \"widgets_values\": [ 1024, 1024, 121, 1 ]\n" +
" }\n" +
" ]\n" +
"}";
List<ModelInfo> results = analyzer.analyze(json, "full_cosmos.json");
// Expectations
boolean foundT5 = results.stream().anyMatch(m -> m.getName().equals("oldt5_xxl_fp8_e4m3fn_scaled.safetensors"));
boolean foundDiffusion = results.stream().anyMatch(m -> m.getName().equals("Cosmos-1_0-Diffusion-7B-Video2World.safetensors"));
boolean foundVae = results.stream().anyMatch(m -> m.getName().equals("cosmos_cv8x8x8_1.0.safetensors"));
assertTrue(foundT5, "T5 XXL not found");
assertTrue(foundDiffusion, "Cosmos Diffusion not found");
assertTrue(foundVae, "Cosmos VAE not found");
assertEquals(3, results.size());
}
}
@@ -3,29 +3,29 @@ package de.tki.comfymodels.service;
import de.tki.comfymodels.domain.ModelInfo;
import de.tki.comfymodels.service.impl.ConfigService;
import de.tki.comfymodels.service.impl.DefaultDownloadManager;
import de.tki.comfymodels.service.impl.EncryptionUtils;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class DownloadSelectionTest {
private final EncryptionUtils encryptionUtils = new EncryptionUtils();
@Test
public void testOnlySelectedItemsAreProcessed() throws InterruptedException {
DefaultDownloadManager downloadManager = new DefaultDownloadManager();
// Mocking ConfigService to avoid NPE
ConfigService configService = new ConfigService();
ConfigService configService = new ConfigService(encryptionUtils);
ReflectionTestUtils.setField(downloadManager, "configService", configService);
List<ModelInfo> models = new ArrayList<>();
@@ -61,7 +61,7 @@ public class DownloadSelectionTest {
@Test
public void testDynamicUncheckWhilePaused() throws InterruptedException {
DefaultDownloadManager downloadManager = new DefaultDownloadManager();
ConfigService configService = new ConfigService();
ConfigService configService = new ConfigService(encryptionUtils);
ReflectionTestUtils.setField(downloadManager, "configService", configService);
List<ModelInfo> models = new ArrayList<>();
@@ -0,0 +1,33 @@
package de.tki.comfymodels.service;
import de.tki.comfymodels.domain.ModelInfo;
import de.tki.comfymodels.service.impl.ModelSearchService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ExistingModelSizeTest {
@TempDir
Path tempDir;
@Test
public void testLocalSizeFormatting() throws IOException {
ModelSearchService searchService = new ModelSearchService();
File dummyFile = tempDir.resolve("test.safetensors").toFile();
byte[] content = new byte[1024 * 1024 * 5]; // 5MB
Files.write(dummyFile.toPath(), content);
long size = dummyFile.length();
String formatted = searchService.formatSize(size);
assertEquals("5.00 MB", formatted);
}
}
@@ -0,0 +1,78 @@
package de.tki.comfymodels.service;
import de.tki.comfymodels.domain.ModelInfo;
import de.tki.comfymodels.service.impl.ComfyModelAnalyzer;
import de.tki.comfymodels.service.impl.LocalAIService;
import de.tki.comfymodels.service.impl.ModelListService;
import de.tki.comfymodels.service.impl.ModelSearchService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class FluxSchnellTest {
private ComfyModelAnalyzer analyzer;
private ModelSearchService searchService;
@BeforeEach
public void setup() {
analyzer = new ComfyModelAnalyzer();
LocalAIService aiService = new LocalAIService();
aiService.init();
ReflectionTestUtils.setField(analyzer, "aiService", aiService);
ReflectionTestUtils.setField(analyzer, "modelListService", new ModelListService());
searchService = new ModelSearchService();
}
@Test
public void testFluxSchnellExtractionAndSizeValidation() {
String json = "{\n" +
" \"nodes\": [\n" +
" {\n" +
" \"id\": 30,\n" +
" \"type\": \"CheckpointLoaderSimple\",\n" +
" \"properties\": {\n" +
" \"models\": [\n" +
" {\n" +
" \"name\": \"flux1-schnell-fp8.safetensors\",\n" +
" \"url\": \"https://huggingface.co/Comfy-Org/flux1-schnell/resolve/main/flux1-schnell-fp8.safetensors?download=true\",\n" +
" \"directory\": \"checkpoints\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"widgets_values\": [ \"flux1-schnell-fp8.safetensors\" ]\n" +
" }\n" +
" ]\n" +
"}";
List<ModelInfo> results = analyzer.analyze(json, "flux_schnell.json");
assertFalse(results.isEmpty(), "Should extract at least one model");
ModelInfo flux = results.get(0);
assertEquals("flux1-schnell-fp8.safetensors", flux.getName());
assertEquals("checkpoints", flux.getType());
assertNotEquals("MISSING", flux.getUrl());
// Size validation logic test
// 1. Valid size
assertEquals("1.00 GB", searchService.formatSize(1024L * 1024L * 1024L));
// 2. Zero size check (Crucial: Should NOT return "0.00 MB")
String zeroResult = searchService.formatSize(0);
assertNotEquals("0.00 MB", zeroResult, "Size 0 should be handled as Unknown or Error, not 0.00 MB");
assertEquals("Unknown", zeroResult);
// 3. Negative size (Error)
assertEquals("Unknown", searchService.formatSize(-1));
// 4. Auth required
assertEquals("🔒 Auth Required", searchService.formatSize(-401));
}
}
@@ -0,0 +1,83 @@
package de.tki.comfymodels.service;
import de.tki.comfymodels.domain.ModelInfo;
import de.tki.comfymodels.service.impl.ComfyModelAnalyzer;
import de.tki.comfymodels.service.impl.LocalAIService;
import de.tki.comfymodels.service.impl.ModelListService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
public class HiDreamComprehensiveTest {
private ComfyModelAnalyzer analyzer;
@BeforeEach
public void setup() {
analyzer = new ComfyModelAnalyzer();
LocalAIService aiService = new LocalAIService();
aiService.init(); // Initialize the AI service
ReflectionTestUtils.setField(analyzer, "aiService", aiService);
ReflectionTestUtils.setField(analyzer, "modelListService", new ModelListService());
}
@Test
public void testHiDreamWorkflowExtraction() {
String json = "{\n" +
" \"id\": \"01d66ae9-78be-4a8d-b737-24eee5e1d447\",\n" +
" \"nodes\": [\n" +
" {\n" +
" \"id\": 55,\n" +
" \"type\": \"VAELoader\",\n" +
" \"properties\": {\n" +
" \"models\": [\n" +
" {\n" +
" \"name\": \"ae.safetensors\",\n" +
" \"url\": \"https://huggingface.co/Comfy-Org/HiDream-I1_ComfyUI/resolve/main/split_files/vae/ae.safetensors\",\n" +
" \"directory\": \"vae\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"widgets_values\": [ \"ae.safetensors\" ]\n" +
" },\n" +
" {\n" +
" \"id\": 54,\n" +
" \"type\": \"QuadrupleCLIPLoader\",\n" +
" \"properties\": {\n" +
" \"models\": [\n" +
" { \"name\": \"clip_l_hidream.safetensors\", \"url\": \"https://huggingface.co/Comfy-Org/HiDream-I1_ComfyUI/resolve/main/split_files/text_encoders/clip_l_hidream.safetensors\", \"directory\": \"text_encoders\" }\n" +
" ]\n" +
" },\n" +
" \"widgets_values\": [ \"clip_l_hidream.safetensors\" ]\n" +
" },\n" +
" {\n" +
" \"id\": 72,\n" +
" \"type\": \"MarkdownNote\",\n" +
" \"widgets_values\": [\n" +
" \"* hidream_i1_full_fp16.safetensors\"\n" +
" ]\n" +
" }\n" +
" ]\n" +
"}";
List<ModelInfo> results = analyzer.analyze(json, "hidream_test.json");
assertFalse(results.isEmpty());
// Check for extraction from properties.models
assertTrue(results.stream().anyMatch(m -> m.getName().equals("ae.safetensors")));
// Check for extraction from text in MarkdownNote - SHOULD BE IGNORED NOW
assertTrue(results.stream().noneMatch(m -> m.getName().equals("hidream_i1_full_fp16.safetensors")), "Model from MarkdownNote should be ignored");
for (ModelInfo info : results) {
System.out.println("Extracted: " + info.getName() + " | Type: " + info.getType() + " | URL: " + info.getUrl());
}
}
}
@@ -0,0 +1,84 @@
package de.tki.comfymodels.service;
import de.tki.comfymodels.service.impl.WorkflowService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class PngExtractionTest {
@TempDir
Path tempDir;
@Test
public void testWorkflowExtractionFromSyntheticPng() throws IOException {
File pngFile = tempDir.resolve("test.png").toFile();
String expectedWorkflow = "{\"nodes\": [{\"type\": \"CheckpointLoaderSimple\"}]}";
createTestPng(pngFile, expectedWorkflow);
WorkflowService workflowService = new WorkflowService();
String extracted = workflowService.extractWorkflow(pngFile);
assertNotNull(extracted, "Workflow should be extracted from PNG");
assertEquals(expectedWorkflow, extracted, "Extracted workflow should match the original");
}
private void createTestPng(File file, String workflow) throws IOException {
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) {
// PNG Signature
dos.writeLong(0x89504E470D0A1A0AL);
// IHDR chunk (minimal)
dos.writeInt(13);
byte[] ihdrType = "IHDR".getBytes(StandardCharsets.US_ASCII);
dos.write(ihdrType);
dos.writeInt(1); // Width
dos.writeInt(1); // Height
dos.writeByte(8); // Bit depth
dos.writeByte(2); // Color type
dos.writeByte(0); // Compression
dos.writeByte(0); // Filter
dos.writeByte(0); // Interlace
dos.writeInt(calculateCrc(ihdrType, new byte[]{0,0,0,1, 0,0,0,1, 8, 2, 0, 0, 0}));
// tEXt chunk with workflow
byte[] key = "workflow".getBytes(StandardCharsets.UTF_8);
byte[] value = workflow.getBytes(StandardCharsets.UTF_8);
byte[] textData = new byte[key.length + 1 + value.length];
System.arraycopy(key, 0, textData, 0, key.length);
textData[key.length] = 0;
System.arraycopy(value, 0, textData, key.length + 1, value.length);
dos.writeInt(textData.length);
byte[] textType = "tEXt".getBytes(StandardCharsets.US_ASCII);
dos.write(textType);
dos.write(textData);
dos.writeInt(calculateCrc(textType, textData));
// IEND chunk
dos.writeInt(0);
byte[] iendType = "IEND".getBytes(StandardCharsets.US_ASCII);
dos.write(iendType);
dos.writeInt(calculateCrc(iendType, new byte[0]));
}
}
private int calculateCrc(byte[] type, byte[] data) {
CRC32 crc = new CRC32();
crc.update(type);
crc.update(data);
return (int) crc.getValue();
}
}
@@ -11,7 +11,7 @@ public class QwenReproductionTest {
@Test
public void testQwenMarkdownModelDetection() {
ComfyModelAnalyzer analyzer = new ComfyModelAnalyzer();
// Minimaler Workflow mit der MarkdownNote des Nutzers
// Minimal workflow with the user's MarkdownNote
String workflow = "{\n" +
" \"nodes\": [\n" +
" {\n" +
@@ -26,12 +26,8 @@ public class QwenReproductionTest {
List<ModelInfo> models = analyzer.analyze(workflow, "test.json");
// Wir prüfen, ob beide Namen gefunden werden
boolean foundLightning = models.stream().anyMatch(m -> m.getName().equals("Qwen-Image-Lightning-4steps-V1.0.safetensors"));
boolean foundEdit = models.stream().anyMatch(m -> m.getName().equals("Qwen-Image-Edit-2509-Lightning-4steps-V1.0-bf16.safetensors"));
assertTrue(foundEdit, "Modell aus dem Link sollte gefunden werden");
assertTrue(foundLightning, "Modell aus dem Text-Diagramm sollte gefunden werden");
// We expect NO models because MarkdownNote is ignored
assertTrue(models.isEmpty(), "No models should be found from MarkdownNote");
}
@Test
@@ -50,12 +46,6 @@ public class QwenReproductionTest {
List<ModelInfo> models = analyzer.analyze(workflow, "test.json");
ModelInfo editModel = models.stream()
.filter(m -> m.getName().equals("Qwen-Image-Edit-2509-Lightning-4steps-V1.0-bf16.safetensors"))
.findFirst().orElse(null);
assertNotNull(editModel, "Modell sollte gefunden werden");
assertEquals("https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Edit-2509/Qwen-Image-Edit-2509-Lightning-4steps-V1.0-bf16.safetensors",
editModel.getUrl(), "URL sollte aus dem Markdown-Link extrahiert werden");
assertTrue(models.isEmpty(), "No models should be found from MarkdownNote, even with links");
}
}
@@ -30,12 +30,13 @@ public class StatusTransitionTest {
private String serverUrl;
private Path tempDir;
private ConfigService configService;
private final EncryptionUtils encryptionUtils = new EncryptionUtils();
@BeforeEach
public void setup() throws Exception {
tempDir = Files.createTempDirectory("transition_test");
downloadManager = new DefaultDownloadManager();
configService = new ConfigService();
configService = new ConfigService(encryptionUtils);
ReflectionTestUtils.setField(downloadManager, "configService", configService);
// Setup Local Test Server
@@ -93,7 +94,7 @@ public class StatusTransitionTest {
@Test
public void testTransition_Idle_To_Searching_To_NotFound() throws InterruptedException {
ModelSearchService searchService = new ModelSearchService();
ConfigService cfg = new ConfigService();
ConfigService cfg = new ConfigService(encryptionUtils);
GeminiAIService gemini = new GeminiAIService();
ModelListService list = new ModelListService();
@@ -1,6 +1,7 @@
package de.tki.comfymodels.service;
import de.tki.comfymodels.service.impl.ConfigService;
import de.tki.comfymodels.service.impl.EncryptionUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.File;
@@ -13,10 +14,12 @@ public class VaultIntegrationTest {
@TempDir
Path tempDir;
private final EncryptionUtils encryptionUtils = new EncryptionUtils();
@Test
public void testVaultSaveAndLoad() throws Exception {
// Setup ConfigService with temp directory
ConfigService configService = new ConfigService() {
ConfigService configService = new ConfigService(encryptionUtils) {
@Override
public String getAppDataPath() {
return tempDir.toString();
@@ -34,7 +37,7 @@ public class VaultIntegrationTest {
configService.save();
// 3. Create NEW instance to simulate restart
ConfigService newConfigService = new ConfigService() {
ConfigService newConfigService = new ConfigService(encryptionUtils) {
@Override
public String getAppDataPath() {
return tempDir.toString();
@@ -44,11 +47,24 @@ public class VaultIntegrationTest {
// 4. Unlock with correct password
newConfigService.unlock(password);
// 5. Verify key
// 5. Verify key and dark mode
assertEquals(testKey, newConfigService.getGeminiApiKey(), "Key should be 4711 after loading");
assertFalse(newConfigService.isDarkMode(), "Default should be false");
newConfigService.setDarkMode(true);
// 5a. Reload again to verify dark mode persistence
ConfigService restartService = new ConfigService(encryptionUtils) {
@Override
public String getAppDataPath() {
return tempDir.toString();
}
};
restartService.unlock(password);
assertTrue(restartService.isDarkMode(), "Dark mode should be true after reload");
// 6. Verify wrong password fails
ConfigService wrongPassService = new ConfigService() {
ConfigService wrongPassService = new ConfigService(encryptionUtils) {
@Override
public String getAppDataPath() {
return tempDir.toString();
@@ -59,4 +75,4 @@ public class VaultIntegrationTest {
wrongPassService.unlock("wrong_password");
}, "Unlock should fail with wrong password");
}
}
}