fix-tests-cleanup

This commit is contained in:
Tour
2025-12-08 07:52:54 +01:00
parent be65f4a5e6
commit 3cc0d40fa3
14 changed files with 70 additions and 3410 deletions

View File

@@ -1 +1 @@
jvmArguments=--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED
jvmArguments=-Djava.util.logging.manager=org.jboss.logmanager.LogManager --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED

View File

@@ -14,8 +14,8 @@ services:
- AUCTION_IMAGES_PATH=/mnt/okcomputer/output/images
# Notification configuration
- AUCTION_NOTIFICATION_CONFIG=desktop
# - AUCTION_NOTIFICATION_CONFIG=desktop
- AUCTION_NOTIFICATION_CONFIG=smtp:michael.bakker1986@gmail.com:agrepolhlnvhipkv:michael.bakker1986@gmail.com
# Quarkus configuration
- QUARKUS_HTTP_PORT=8081
- QUARKUS_HTTP_HOST=0.0.0.0

View File

@@ -440,6 +440,8 @@
<!-- Enable ByteBuddy experimental mode for Java 25 support -->
<!-- Mockito requires this for Java 24+ -->
<argLine>
--enable-native-access=ALL-UNNAMED
--add-opens java.base/sun.misc=ALL-UNNAMED
-Dnet.bytebuddy.experimental=true
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,13 @@
package auctiora;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import lombok.extern.slf4j.Slf4j;
import nu.pattern.OpenCV;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
@@ -23,7 +26,7 @@ import static org.opencv.dnn.Dnn.DNN_TARGET_CPU;
/**
* Service for performing object detection on images using OpenCV's DNN
* module. The DNN module can load pretrained models from several
* frameworks (Darknet, TensorFlow, ONNX, etc.)【784097309529506†L209-L233】. Here
* frameworks (Darknet, TensorFlow, ONNX, etc.). Here
* we load a YOLO model (Darknet) by specifying the configuration and
* weights files. For each image we run a forward pass and return a
* list of detected class labels.
@@ -33,14 +36,45 @@ import static org.opencv.dnn.Dnn.DNN_TARGET_CPU;
*/
@Slf4j
public class ObjectDetectionService {
private final Net net;
private final List<String> classNames;
private final boolean enabled;
private Net net;
private List<String> classNames;
private boolean enabled;
private int warnCount = 0;
private static final int MAX_WARNINGS = 5;
private static boolean openCvLoaded = false;
private final String cfgPath;
private final String weightsPath;
private final String classNamesPath;
ObjectDetectionService(String cfgPath, String weightsPath, String classNamesPath) throws IOException {
this.cfgPath = cfgPath;
this.weightsPath = weightsPath;
this.classNamesPath = classNamesPath;
}
@PostConstruct
void init() {
// Load OpenCV native libraries first
if (!openCvLoaded) {
try {
OpenCV.loadLocally();
openCvLoaded = true;
log.info("✓ OpenCV {} loaded successfully", Core.VERSION);
} catch (Exception e) {
log.warn("⚠️ Object detection disabled: OpenCV native libraries not loaded");
enabled = false;
net = null;
classNames = new ArrayList<>();
return;
}
}
initializeModel();
}
private void initializeModel() {
// Check if model files exist
var cfgFile = Paths.get(cfgPath);
var weightsFile = Paths.get(weightsPath);
@@ -86,11 +120,15 @@ public class ObjectDetectionService {
classNames = Files.readAllLines(classNamesFile);
enabled = true;
} 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);
log.error("⚠️ Object detection disabled: OpenCV native libraries not loaded", e);
enabled = false;
net = null;
classNames = new ArrayList<>();
} catch (Exception e) {
System.err.println("⚠️ Object detection disabled: " + e.getMessage());
throw new IOException("Failed to initialize object detection", e);
log.error("⚠️ Object detection disabled: " + e.getMessage(), e);
enabled = false;
net = null;
classNames = new ArrayList<>();
}
}
/**

View File

@@ -65,10 +65,10 @@ public class StatusResource {
private String getOpenCvVersion() {
try {
OpenCV.loadLocally();
// OpenCV is already loaded by AuctionMonitorProducer
return Core.VERSION;
} catch (Exception e) {
return "4.9.0 (default)";
return "Not loaded";
}
}
}

View File

@@ -119,7 +119,7 @@ public class TroostwijkMonitor {
allLots.size(), imageCount);
if (!allLots.isEmpty()) {
var sum = allLots.stream().mapToDouble(Lot::currentBid).sum();
log.info("Total current bids: €{:.2f}", sum);
log.info("Total current bids: €{}", String.format("%.2f", sum));
}
} catch (Exception e) {
log.warn("Could not retrieve database stats", e);

View File

@@ -46,7 +46,10 @@ quarkus.http.root-path=/
# Auction Monitor Configuration
auction.database.path=/mnt/okcomputer/output/cache.db
auction.images.path=/mnt/okcomputer/output/images
auction.notification.config=desktop
# auction.notification.config=desktop
# Format: smtp:username:password:recipient_email
auction.notification.config=smtp:michael.bakker1986@gmail.com:agrepolhlnvhipkv:michael.bakker1986@gmail.com
auction.yolo.config=/mnt/okcomputer/output/models/yolov4.cfg
auction.yolo.weights=/mnt/okcomputer/output/models/yolov4.weights
auction.yolo.classes=/mnt/okcomputer/output/models/coco.names
@@ -69,3 +72,4 @@ auction.http.timeout-seconds=30
# Health Check Configuration
quarkus.smallrye-health.root-path=/health

View File

@@ -1,115 +0,0 @@
package auctiora;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* Test auction parsing logic using saved HTML from test.html
* Tests the markup data extraction for each auction found
*/
@Slf4j
public class AuctionParsingTest {
private static String testHtml;
@BeforeAll
public static void loadTestHtml() throws IOException {
// Load the test HTML file
testHtml = Files.readString(Paths.get("src/test/resources/test_auctions.html"));
log.info("Loaded test HTML ({} characters)", testHtml.length());
}
@Test
public void testLocationPatternMatching() {
log.info("\n=== Location Pattern Tests ===");
// Test different location formats
var testCases = new String[]{
"<p>Amsterdam, NL</p>",
"<p class=\"flex truncate\"><span class=\"w-full truncate\">Sofia,<!-- --> </span>BG</p>",
"<p>Berlin, DE</p>",
"<span>Brussels,</span>BE"
};
for (var testHtml : testCases) {
var doc = Jsoup.parse(testHtml);
var elem = doc.select("p, span").first();
if (elem != null) {
var text = elem.text();
log.info("\nTest: {}", testHtml);
log.info("Text: {}", text);
// Test regex pattern
if (text.matches(".*[A-Z]{2}$")) {
var countryCode = text.substring(text.length() - 2);
var cityPart = text.substring(0, text.length() - 2).trim().replaceAll("[,\\s]+$", "");
log.info("→ Extracted: {}, {}", cityPart, countryCode);
} else {
log.info("→ No match");
}
}
}
}
@Test
public void testFullTextPatternMatching() {
log.info("\n=== Full Text Pattern Tests ===");
// Test the complete auction text format
var testCases = new String[]{
"woensdag om 18:00 1 Vrachtwagens voor bedrijfsvoertuigen Loßburg, DE",
"maandag om 14:30 5 Industriële machines Amsterdam, NL",
"vrijdag om 10:00 12 Landbouwmachines Antwerpen, BE"
};
for (var testText : testCases) {
log.info("\nParsing: \"{}\"", testText);
// Simulated extraction
var remaining = testText;
// Extract time
var timePattern = java.util.regex.Pattern.compile("(\\w+)\\s+om\\s+(\\d{1,2}:\\d{2})");
var timeMatcher = timePattern.matcher(remaining);
if (timeMatcher.find()) {
log.info(" Time: {} om {}", timeMatcher.group(1), timeMatcher.group(2));
remaining = remaining.substring(timeMatcher.end()).trim();
}
// Extract location
var locPattern = java.util.regex.Pattern.compile(
"([A-ZÀ-ÿa-z][A-ZÀ-ÿa-z\\s\\-'öäüßàèéêëïôùûç]+?),\\s*([A-Z]{2})\\s*$"
);
var locMatcher = locPattern.matcher(remaining);
if (locMatcher.find()) {
log.info(" Location: {}, {}", locMatcher.group(1), locMatcher.group(2));
remaining = remaining.substring(0, locMatcher.start()).trim();
}
// Extract lot count
var lotPattern = java.util.regex.Pattern.compile("^(\\d+)\\s+");
var lotMatcher = lotPattern.matcher(remaining);
if (lotMatcher.find()) {
log.info(" Lot count: {}", lotMatcher.group(1));
remaining = remaining.substring(lotMatcher.end()).trim();
}
// What remains is title
log.info(" Title: {}", remaining);
}
}
}

View File

@@ -80,13 +80,13 @@ class ClosingTimeCalculationTest {
@DisplayName("Should identify lots closing within 30 minutes (dashboard threshold)")
void testDashboardClosingThreshold() {
var closing20Min = createLot(LocalDateTime.now().plusMinutes(20));
var closing30Min = createLot(LocalDateTime.now().plusMinutes(30));
var closing31Min = createLot(LocalDateTime.now().plusMinutes(31)); // Use 31 to avoid boundary timing issue
var closing40Min = createLot(LocalDateTime.now().plusMinutes(40));
assertTrue(closing20Min.minutesUntilClose() < 30,
"Lot closing in 20 min should be < 30 minutes");
assertTrue(closing30Min.minutesUntilClose() >= 30,
"Lot closing in 30 min should be >= 30 minutes");
assertTrue(closing31Min.minutesUntilClose() >= 30,
"Lot closing in 31 min should be >= 30 minutes");
assertTrue(closing40Min.minutesUntilClose() > 30,
"Lot closing in 40 min should be > 30 minutes");
}

View File

@@ -81,7 +81,7 @@ class ObjectDetectionServiceTest {
}
@Test
@DisplayName("Should throw IOException when model files exist but OpenCV fails to load")
@DisplayName("Should gracefully handle when model files exist but OpenCV fails to load")
void testInitializeWithValidModels() throws IOException {
var cfgPath = Paths.get(TEST_CFG);
var weightsPath = Paths.get(TEST_WEIGHTS);
@@ -93,10 +93,11 @@ class ObjectDetectionServiceTest {
Files.writeString(classesPath, "person\ncar\ntruck\n");
// 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);
});
// service should construct successfully but be disabled (handled in @PostConstruct)
var service = new ObjectDetectionService(TEST_CFG, TEST_WEIGHTS, TEST_CLASSES);
// Service is created, but init() handles failures gracefully
// detectObjects should return empty list when disabled
assertNotNull(service);
} finally {
Files.deleteIfExists(cfgPath);
Files.deleteIfExists(weightsPath);

File diff suppressed because one or more lines are too long

View File

@@ -1,26 +0,0 @@
Configure your devices to use the Pi-hole as their DNS server │
│ using: │
│ │
│ IPv4: 192.168.1.159 │
│ IPv6: fdc5:59a6:9ac1:f11f:2c86:25d3:6282:37ef │
│ If you have not done so already, the above IP should be set to │
│ static. │
│ View the web interface at http://pi.hole:80/admin or │
│ http://192.168.1.159:80/admin │
│ │
│ Your Admin Webpage login password is gYj7Enh- │
│ │
│ │
│ To allow your user to use all CLI functions without │
│ authentication, │
│ refer to https://docs.pi-hole.net/main/post-install/ │
├─────────────────────────────────────────────────────────────
127.0.0.1
192.168.1.159
::1
fdc5:59a6:9ac1:f11f:2c86:25d3:6282:37ef
fdc5:59a6:9ac1:f11f:bd8c:6e87:65f0:243c
fe80::a05b:bbc6:d47f:3002%enp9s0
2IXD-XJN9-C337-1K4Y-BBEO-HV1F-3BVI

File diff suppressed because one or more lines are too long