SingleCronTest.java

package com.cronutils.model;

import com.cronutils.model.definition.CronDefinition;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.parser.CronParser;
import com.cronutils.model.CronType;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Disabled; // Will use if a test needs specific time mocking not available

import java.time.ZonedDateTime;
import java.time.ZoneId; // For ZonedDateTime construction

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;

public class SingleCronTest {

    private CronParser cronParser;

    @BeforeEach
    public void setUp() {
        // Using QUARTZ definition for 7 fields (includes year)
        CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ);
        cronParser = new CronParser(cronDefinition);
    }

    @Test
    public void testOverlapFromIssueDescription() {
        // cron1 = "0 0 10 1/1 ? *" (every day at 10am) - This is not a QUARTZ expression (6 fields)
        // cron2 = "0 0 10 ? * MON *" (every Monday at 10am) - QUARTZ
        // Let's make cron1 QUARTZ compatible: "0 0 10 1/1 * ? *"
        Cron cron1 = cronParser.parse("0 0 10 1/1 * ? *"); // Every day at 10 AM
        Cron cron2 = cronParser.parse("0 0 10 ? * MON *"); // Every Monday at 10 AM
        assertTrue(cron1.overlap(cron2), "Cron1 (every day at 10am) should overlap with Cron2 (every Monday at 10am)");
        assertTrue(cron2.overlap(cron1), "Overlap should be symmetric");
    }

    @Test
    public void testNonOverlappingExpressions() {
        Cron cron1 = cronParser.parse("0 0 10 ? * MON *"); // Every Monday at 10 AM
        Cron cron2 = cronParser.parse("0 0 12 ? * TUE *"); // Every Tuesday at 12 PM
        assertFalse(cron1.overlap(cron2), "Cron1 (Monday 10am) should not overlap with Cron2 (Tuesday 12pm)");
        assertFalse(cron2.overlap(cron1), "Overlap should be symmetric");
    }

    @Test
    public void testAlwaysOverlappingExpressions() {
        Cron cron1 = cronParser.parse("* * * * * ? *");    // Every second
        Cron cron2 = cronParser.parse("0 * * * * ? *");    // Every minute at second 0
        assertTrue(cron1.overlap(cron2), "Cron1 (every second) should overlap with Cron2 (every minute at second 0)");
        assertTrue(cron2.overlap(cron1), "Overlap should be symmetric");
    }

    @Test
    public void testOverlapOnSpecificDate() {
        // Ensure test year has Dec 25th as a Friday. 2026 is such a year.
        // cron1: Dec 25th, 2026 at 12 PM
        Cron cron1 = cronParser.parse("0 0 12 25 12 ? 2026");
        // cron2: Every Friday in December 2026 at 12 PM
        Cron cron2 = cronParser.parse("0 0 12 ? 12 FRI 2026");
        assertTrue(cron1.overlap(cron2), "Cron1 (Dec 25th 2026 12pm) should overlap with Cron2 (Fridays in Dec 2026 at 12pm) as Dec 25, 2026 is a Friday.");
        assertTrue(cron2.overlap(cron1), "Overlap should be symmetric");

        // Test with a year where Dec 25 is NOT a Friday. 2025 is such a year (Thursday).
        Cron cron3 = cronParser.parse("0 0 12 25 12 ? 2025"); // Dec 25th, 2025 at 12 PM
        Cron cron4 = cronParser.parse("0 0 12 ? 12 FRI 2025"); // Every Friday in December 2025 at 12 PM
        assertFalse(cron3.overlap(cron4), "Cron3 (Dec 25th 2025 12pm) should NOT overlap with Cron4 (Fridays in Dec 2025 at 12pm) as Dec 25, 2025 is a Thursday.");
        assertFalse(cron4.overlap(cron3), "Overlap should be symmetric");
    }
    
    @Test
    public void testSlightlyDifferentExpressionsThatShouldOverlap() {
        // Every 5 minutes vs Every 10 minutes
        Cron cron1 = cronParser.parse("0 0/5 * * * ? *");
        Cron cron2 = cronParser.parse("0 0/10 * * * ? *");
        assertTrue(cron1.overlap(cron2), "Every 5 minutes should overlap with every 10 minutes.");
        assertTrue(cron2.overlap(cron1), "Overlap should be symmetric");
    }

    @Test
    public void testNonOverlappingSameDayDifferentHours() {
        Cron cron1 = cronParser.parse("0 0 10 ? * MON *"); // Monday 10:00 AM
        Cron cron2 = cronParser.parse("0 0 11 ? * MON *"); // Monday 11:00 AM
        assertFalse(cron1.overlap(cron2), "Monday 10am should not overlap with Monday 11am.");
        assertFalse(cron2.overlap(cron1), "Overlap should be symmetric");
    }
    
    @Test
    public void testOverlappingHourlyAndDaily() {
        // Every hour vs Every day at a specific hour
        Cron cron1 = cronParser.parse("0 0 * * * ? *"); // Every hour at minute 0
        Cron cron2 = cronParser.parse("0 0 12 * * ? *"); // Every day at 12:00 PM
        assertTrue(cron1.overlap(cron2), "Every hour should overlap with every day at 12 PM.");
        assertTrue(cron2.overlap(cron1), "Overlap should be symmetric");
    }

    // It might be good to have a test case where the year is not specified in one
    // and specified in another, if the cron definition allows optional years.
    // For QUARTZ, year is a field. If not specified, it might default or be an error.
    // The CronParser with QUARTZ definition expects 7 fields usually.
    // Let's try a case where one has a wildcard year and another a specific year.
    @Test
    public void testOverlapWithWildcardYearAndSpecificYear() {
        // cron1: Every Jan 1st 10 AM, any year
        Cron cron1 = cronParser.parse("0 0 10 1 1 ? *");
        // cron2: Jan 1st, 2027 at 10 AM
        Cron cron2 = cronParser.parse("0 0 10 1 1 ? 2027");
        assertTrue(cron1.overlap(cron2), "Jan 1st any year should overlap with Jan 1st 2027.");
        assertTrue(cron2.overlap(cron1), "Overlap should be symmetric");

        // cron3: Every Jan 1st 10 AM, any year
        Cron cron3 = cronParser.parse("0 0 10 1 1 ? *");
        // cron4: Feb 1st, 2027 at 10 AM
        Cron cron4 = cronParser.parse("0 0 10 1 2 ? 2027");
        assertFalse(cron3.overlap(cron4), "Jan 1st any year should NOT overlap with Feb 1st 2027.");
        assertFalse(cron4.overlap(cron3), "Overlap should be symmetric");
    }

    // Test for expressions that will never overlap because of year constraints
    @Test
    public void testNonOverlapDueToYear() {
        Cron cron1 = cronParser.parse("0 0 12 1 1 ? 2025"); // Jan 1st, 2025 12:00 PM
        Cron cron2 = cronParser.parse("0 0 12 1 1 ? 2026"); // Jan 1st, 2026 12:00 PM
        assertFalse(cron1.overlap(cron2), "Expressions for different years should not overlap.");
        assertFalse(cron2.overlap(cron1), "Overlap should be symmetric");
    }
}