refactor: implement dependency injection for encryption and enhance model analysis
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user