package auctiora; import org.junit.jupiter.api.*; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.sql.SQLException; import java.time.LocalDateTime; import static org.junit.jupiter.api.Assertions.*; /** * Test cases for TroostwijkMonitor. * Tests monitoring orchestration, bid tracking, and notification triggers. */ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class TroostwijkMonitorTest { private String testDbPath; private TroostwijkMonitor monitor; @BeforeAll void setUp() throws SQLException, IOException { testDbPath = "test_monitor_" + System.currentTimeMillis() + ".db"; // Initialize with non-existent YOLO models (disabled mode) monitor = new TroostwijkMonitor( testDbPath, "desktop", "non_existent.cfg", "non_existent.weights", "non_existent.txt" ); } @AfterAll void tearDown() throws Exception { Files.deleteIfExists(Paths.get(testDbPath)); } @Test @DisplayName("Should initialize monitor successfully") void testMonitorInitialization() { assertNotNull(monitor); assertNotNull(monitor.getDb()); } @Test @DisplayName("Should print database stats without error") void testPrintDatabaseStats() { assertDoesNotThrow(() -> monitor.printDatabaseStats()); } @Test @DisplayName("Should process pending images without error") void testProcessPendingImages() { assertDoesNotThrow(() -> monitor.processPendingImages()); } @Test @DisplayName("Should handle empty database gracefully") void testEmptyDatabaseHandling() throws SQLException { var auctions = monitor.getDb().getAllAuctions(); var lots = monitor.getDb().getAllLots(); assertNotNull(auctions); assertNotNull(lots); assertTrue(auctions.isEmpty() || auctions.size() >= 0); } @Test @DisplayName("Should track lots in database") void testLotTracking() throws SQLException { // Insert test lot var lot = new Lot( 11111, 22222, "Test Forklift", "Electric forklift in good condition", "Toyota", "Electric", 2020, "Machinery", 1500.00, "EUR", "https://example.com/lot/22222", LocalDateTime.now().plusDays(1), false ); monitor.getDb().upsertLot(lot); var lots = monitor.getDb().getAllLots(); assertTrue(lots.stream().anyMatch(l -> l.lotId() == 22222)); } @Test @DisplayName("Should monitor lots closing soon") void testClosingSoonMonitoring() throws SQLException { // Insert lot closing in 4 minutes var closingSoon = new Lot( 33333, 44444, "Closing Soon Item", "Description", "", "", 0, "Category", 100.00, "EUR", "https://example.com/lot/44444", LocalDateTime.now().plusMinutes(4), false ); monitor.getDb().upsertLot(closingSoon); var lots = monitor.getDb().getActiveLots(); var found = lots.stream() .filter(l -> l.lotId() == 44444) .findFirst() .orElse(null); assertNotNull(found); assertTrue(found.minutesUntilClose() < 30); } @Test @DisplayName("Should identify lots with time remaining") void testTimeRemainingCalculation() throws SQLException { var futureLot = new Lot( 55555, 66666, "Future Lot", "Description", "", "", 0, "Category", 200.00, "EUR", "https://example.com/lot/66666", LocalDateTime.now().plusHours(2), false ); monitor.getDb().upsertLot(futureLot); var lots = monitor.getDb().getActiveLots(); var found = lots.stream() .filter(l -> l.lotId() == 66666) .findFirst() .orElse(null); assertNotNull(found); assertTrue(found.minutesUntilClose() > 60); } @Test @DisplayName("Should handle lots without closing time") void testLotsWithoutClosingTime() throws SQLException { var noClosing = new Lot( 77777, 88888, "No Closing Time", "Description", "", "", 0, "Category", 150.00, "EUR", "https://example.com/lot/88888", null, false ); monitor.getDb().upsertLot(noClosing); var lots = monitor.getDb().getActiveLots(); var found = lots.stream() .filter(l -> l.lotId() == 88888) .findFirst() .orElse(null); assertNotNull(found); assertNull(found.closingTime()); } @Test @DisplayName("Should track notification status") void testNotificationStatusTracking() throws SQLException { var lot = new Lot( 99999, 11110, "Test Notification", "Description", "", "", 0, "Category", 100.00, "EUR", "https://example.com/lot/11110", LocalDateTime.now().plusMinutes(3), false ); monitor.getDb().upsertLot(lot); // Update notification flag var notified = new Lot( 99999, 11110, "Test Notification", "Description", "", "", 0, "Category", 100.00, "EUR", "https://example.com/lot/11110", LocalDateTime.now().plusMinutes(3), true ); monitor.getDb().updateLotNotificationFlags(notified); var lots = monitor.getDb().getActiveLots(); var found = lots.stream() .filter(l -> l.lotId() == 11110) .findFirst() .orElse(null); assertNotNull(found); assertTrue(found.closingNotified()); } @Test @DisplayName("Should update bid amounts") void testBidAmountUpdates() throws SQLException { var lot = new Lot( 12121, 13131, "Bid Update Test", "Description", "", "", 0, "Category", 100.00, "EUR", "https://example.com/lot/13131", LocalDateTime.now().plusDays(1), false ); monitor.getDb().upsertLot(lot); // Simulate bid increase var higherBid = new Lot( 12121, 13131, "Bid Update Test", "Description", "", "", 0, "Category", 250.00, "EUR", "https://example.com/lot/13131", LocalDateTime.now().plusDays(1), false ); monitor.getDb().updateLotCurrentBid(higherBid); var lots = monitor.getDb().getActiveLots(); var found = lots.stream() .filter(l -> l.lotId() == 13131) .findFirst() .orElse(null); assertNotNull(found); assertEquals(250.00, found.currentBid(), 0.01); } @Test @DisplayName("Should handle multiple concurrent lot updates") void testConcurrentLotUpdates() throws InterruptedException, SQLException { Thread t1 = new Thread(() -> { try { for (int i = 0; i < 5; i++) { monitor.getDb().upsertLot(new Lot( 20000 + i, 30000 + i, "Concurrent " + i, "Desc", "", "", 0, "Cat", 100.0, "EUR", "https://example.com/" + i, null, false )); } } catch (SQLException e) { fail("Thread 1 failed: " + e.getMessage()); } }); Thread t2 = new Thread(() -> { try { for (int i = 5; i < 10; i++) { monitor.getDb().upsertLot(new Lot( 20000 + i, 30000 + i, "Concurrent " + i, "Desc", "", "", 0, "Cat", 200.0, "EUR", "https://example.com/" + i, null, false )); } } catch (SQLException e) { fail("Thread 2 failed: " + e.getMessage()); } }); t1.start(); t2.start(); t1.join(); t2.join(); var lots = monitor.getDb().getActiveLots(); long count = lots.stream() .filter(l -> l.lotId() >= 30000 && l.lotId() < 30010) .count(); assertTrue(count >= 10); } @Test @DisplayName("Should schedule monitoring without error") void testScheduleMonitoring() { // This just tests that scheduling doesn't throw // Actual monitoring would run in background assertDoesNotThrow(() -> { // Don't actually start monitoring in test // Just verify monitor is ready assertNotNull(monitor); }); } @Test @DisplayName("Should handle database with auctions and lots") void testDatabaseWithData() throws SQLException { // Insert auction var auction = new AuctionInfo( 40000, "Test Auction", "Amsterdam, NL", "Amsterdam", "NL", "https://example.com/auction/40000", "A7", 10, LocalDateTime.now().plusDays(2) ); monitor.getDb().upsertAuction(auction); // Insert related lot var lot = new Lot( 40000, 50000, "Test Lot", "Description", "", "", 0, "Category", 500.00, "EUR", "https://example.com/lot/50000", LocalDateTime.now().plusDays(2), false ); monitor.getDb().upsertLot(lot); // Verify var auctions = monitor.getDb().getAllAuctions(); var lots = monitor.getDb().getAllLots(); assertTrue(auctions.stream().anyMatch(a -> a.auctionId() == 40000)); assertTrue(lots.stream().anyMatch(l -> l.lotId() == 50000)); } }