Fix mock tests

This commit is contained in:
Tour
2025-12-05 03:42:36 +01:00
parent ff8f5f2c1a
commit 8ecd9fcbda
11 changed files with 105 additions and 22 deletions

9
.mvn/extensions.xml Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<extensions>
<!-- Override Maven's internal Guice with a Java 25 compatible version -->
<extension>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>7.0.0</version>
</extension>
</extensions>

8
mvnw.cmd vendored Normal file
View File

@@ -0,0 +1,8 @@
@echo off
@REM Wrapper script to suppress Maven Guice warnings
@REM Redirects stderr warnings to nul while keeping actual errors
set MAVEN_OPTS=%MAVEN_OPTS% --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/jdk.internal.misc=ALL-UNNAMED
@REM Run maven and filter out Guice warnings
mvn %* 2>&1 | findstr /V /C:"sun.misc.Unsafe" /C:"com.google.inject" /C:"WARNING: package sun.misc"

13
pom.xml
View File

@@ -223,6 +223,19 @@
</exclusions>
</dependency>
<!-- Explicitly add cron-utils with slf4j excluded to avoid path warning -->
<dependency>
<groupId>com.cronutils</groupId>
<artifactId>cron-utils</artifactId>
<version>9.2.1</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Force Netty 4.1.124.Final to avoid sun.misc.Unsafe warnings -->
<dependency>
<groupId>io.netty</groupId>

View File

@@ -1,10 +1,13 @@
package auctiora;
import io.quarkus.runtime.Startup;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Singleton;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.logging.Logger;
import org.opencv.core.Core;
import java.io.IOException;
import java.sql.SQLException;
@@ -13,11 +16,23 @@ import java.sql.SQLException;
* CDI Producer for auction monitor services.
* Creates and configures singleton instances of core services.
*/
@Startup
@ApplicationScoped
public class AuctionMonitorProducer {
private static final Logger LOG = Logger.getLogger(AuctionMonitorProducer.class);
@PostConstruct
void init() {
// Load OpenCV native library at startup
try {
nu.pattern.OpenCV.loadLocally();
LOG.info("✓ OpenCV loaded successfully");
} catch (Exception e) {
LOG.warn("⚠️ OpenCV not available - image detection will be disabled: " + e.getMessage());
}
}
@Produces
@Singleton
public DatabaseService produceDatabaseService(

View File

@@ -111,6 +111,36 @@ public class DatabaseService {
// Table might not exist yet, which is fine
log.debug("Could not check auctions table schema: " + e.getMessage());
}
// Check if sale_id column exists in lots table (old schema used auction_id)
try (var rs = stmt.executeQuery("PRAGMA table_info(lots)")) {
boolean hasSaleId = false;
boolean hasAuctionId = false;
while (rs.next()) {
String colName = rs.getString("name");
if ("sale_id".equals(colName)) {
hasSaleId = true;
}
if ("auction_id".equals(colName)) {
hasAuctionId = true;
}
}
// If we have auction_id but not sale_id, we need to rename the column
// SQLite doesn't support RENAME COLUMN before 3.25.0, so we add sale_id and copy data
if (hasAuctionId && !hasSaleId) {
log.info("Migrating schema: Adding 'sale_id' column to lots table and copying data from auction_id");
stmt.execute("ALTER TABLE lots ADD COLUMN sale_id INTEGER");
stmt.execute("UPDATE lots SET sale_id = auction_id");
} else if (!hasSaleId && !hasAuctionId) {
// New table, add sale_id
log.info("Migrating schema: Adding 'sale_id' column to new lots table");
stmt.execute("ALTER TABLE lots ADD COLUMN sale_id INTEGER");
}
} catch (SQLException e) {
// Table might not exist yet, which is fine
log.debug("Could not check lots table schema: " + e.getMessage());
}
}
/**

View File

@@ -43,7 +43,7 @@ class ImageProcessingService {
try {
var response = httpClient.sendGetBytes(imageUrl);
if (response.statusCode() == 200) {
if (response != null && response.statusCode() == 200) {
// Use Windows path: C:\mnt\okcomputer\output\images
var baseDir = Paths.get("C:", "mnt", "okcomputer", "output", "images");
var dir = baseDir.resolve(String.valueOf(saleId)).resolve(String.valueOf(lotId));

View File

@@ -91,8 +91,8 @@ public class NotificationService {
if ("desktop".equalsIgnoreCase(cfg)) {
return new Config(true, false, null, null, null);
} else if (cfg.startsWith("smtp:")) {
var parts = cfg.split(":", 4);
if (parts.length != 4)
var parts = cfg.split(":", -1); // Use -1 to include trailing empty strings
if (parts.length < 4)
throw new IllegalArgumentException("Email config must be 'smtp:username:password:toEmail'");
return new Config(true, true, parts[1], parts[2], parts[3]);
}

View File

@@ -66,6 +66,9 @@ public class ObjectDetectionService {
this.classNames = Files.readAllLines(classNamesFile);
this.enabled = true;
log.info("✓ Object detection enabled with YOLO");
} catch (UnsatisfiedLinkError e) {
System.err.println("⚠️ Object detection disabled: OpenCV native libraries not loaded");
throw new IOException("Failed to initialize object detection: OpenCV native libraries not loaded", e);
} catch (Exception e) {
System.err.println("⚠️ Object detection disabled: " + e.getMessage());
throw new IOException("Failed to initialize object detection", e);

View File

@@ -73,7 +73,11 @@ public class ScraperDataAdapter {
public static int extractNumericId(String id) {
if (id == null || id.isBlank()) return 0;
var digits = id.replaceAll("\\D+", "");
// Remove the type prefix (e.g., "A7-") first, then extract all digits
// "A7-39813" → "39813" → 39813
// "A1-28505-5" → "28505-5" → "285055"
var afterPrefix = id.indexOf('-') >= 0 ? id.substring(id.indexOf('-') + 1) : id;
var digits = afterPrefix.replaceAll("\\D+", "");
return digits.isEmpty() ? 0 : Integer.parseInt(digits);
}

View File

@@ -30,14 +30,14 @@ class NotificationServiceTest {
@Test
@DisplayName("Should reject invalid SMTP configuration format")
void testInvalidSMTPConfiguration() {
// Missing parts
// Missing parts (only 2 parts total)
assertThrows(IllegalArgumentException.class, () ->
new NotificationService("smtp:incomplete")
);
// Wrong format
// Wrong format (only 3 parts total, needs 4)
assertThrows(IllegalArgumentException.class, () ->
new NotificationService("smtp:only:two:parts")
new NotificationService("smtp:only:two")
);
}

View File

@@ -82,7 +82,7 @@ class ObjectDetectionServiceTest {
}
@Test
@DisplayName("Should initialize successfully with valid model files")
@DisplayName("Should throw IOException when model files exist but OpenCV fails to load")
void testInitializeWithValidModels() throws IOException {
// Create dummy model files for testing initialization
var cfgPath = Paths.get(TEST_CFG);
@@ -94,15 +94,10 @@ class ObjectDetectionServiceTest {
Files.write(weightsPath, new byte[]{0, 1, 2, 3});
Files.writeString(classesPath, "person\ncar\ntruck\n");
// Note: This will still fail to load actual YOLO model without OpenCV
// But it tests file existence check
assertDoesNotThrow(() -> {
try {
new ObjectDetectionService(TEST_CFG, TEST_WEIGHTS, TEST_CLASSES);
} catch (IOException e) {
// Expected if OpenCV not loaded
assertTrue(e.getMessage().contains("Failed to initialize"));
}
// When files exist but OpenCV native library isn't loaded,
// constructor should throw IOException wrapping the UnsatisfiedLinkError
assertThrows(IOException.class, () -> {
new ObjectDetectionService(TEST_CFG, TEST_WEIGHTS, TEST_CLASSES);
});
} finally {
Files.deleteIfExists(cfgPath);
@@ -113,10 +108,16 @@ class ObjectDetectionServiceTest {
@Test
@DisplayName("Should handle missing class names file")
void testMissingClassNamesFile() {
assertThrows(IOException.class, () -> {
new ObjectDetectionService("non_existent.cfg", "non_existent.weights", "non_existent.txt");
});
void testMissingClassNamesFile() throws IOException {
// When model files don't exist, service initializes in disabled mode (no exception)
ObjectDetectionService service = new ObjectDetectionService(
"non_existent.cfg",
"non_existent.weights",
"non_existent.txt"
);
assertNotNull(service);
// Verify it returns empty results when disabled
assertTrue(service.detectObjects("test.jpg").isEmpty());
}
@Test