package org.implab.gradle.common.core.lang;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.ArrayList;
import java.util.List;

import org.junit.jupiter.api.Test;

class ReplayableQueueTest {
    @Test
    void replaysExistingValuesAndReceivesFutureValues() {
        var queue = new ReplayableQueue<String>();
        var seen = new ArrayList<String>();

        queue.add("one");
        queue.add("two");
        queue.forEach(seen::add);
        queue.add("three");

        assertEquals(List.of("one", "two", "three"), seen);
        assertEquals(List.of("one", "two", "three"), queue.values());
    }

    @Test
    void commitsValueBeforeDispatchingConsumers() {
        var queue = new ReplayableQueue<String>();

        queue.forEach(value -> {
            throw new IllegalStateException("boom");
        });

        assertThrows(IllegalStateException.class, () -> queue.add("one"));
        assertEquals(List.of("one"), queue.values());
    }

    @Test
    void rejectsReentrantAdd() {
        var queue = new ReplayableQueue<String>();

        queue.forEach(value -> queue.add("nested"));

        var ex = assertThrows(IllegalStateException.class, () -> queue.add("one"));
        assertTrue(ex.getMessage().contains("Reentrant replay is not supported"));
    }

    @Test
    void rejectsReentrantForEach() {
        var queue = new ReplayableQueue<String>();
        queue.add("one");

        var ex = assertThrows(IllegalStateException.class, () -> queue.forEach(value -> queue.forEach(nested -> {
        })));

        assertTrue(ex.getMessage().contains("Reentrant replay is not supported"));
    }
}
