package com.easyagents.flow.core.test;

import com.easyagents.flow.core.chain.runtime.ExecutionBudget;
import com.easyagents.flow.core.chain.runtime.ExecutionBudgetExceededException;
import org.junit.Assert;
import org.junit.Test;

/**
 * 验证工作流执行预算的宽松默认值和边界检查。
 */
public class ExecutionBudgetTest {

    /**
     * 验证缺省预算与 XL12 约定一致。
     */
    @Test
    public void shouldUseGenerousDefaultBudgets() {
        ExecutionBudget budget = ExecutionBudget.defaults();

        Assert.assertEquals(100_000L, budget.getMaxIterations());
        // 人工确认和长期挂起不应被墙钟时间误伤，时长预算默认关闭。
        Assert.assertEquals(0L, budget.getMaxDurationMillis());
        Assert.assertEquals(1_000_000L, budget.getMaxChildExecutions());
        Assert.assertEquals(512L * 1024L * 1024L, budget.getMaxAccumulatedBytes());
        Assert.assertEquals(32, budget.getMaxNestedDepth());
        // 热状态估算可能误伤既有业务，默认关闭并允许部署方显式配置。
        Assert.assertEquals(0L, budget.getMaxHotStateBytes());
    }

    /**
     * 验证达到阈值时仍允许执行，超过阈值后才阻止。
     */
    @Test
    public void shouldRejectOnlyAfterBudgetIsExceeded() {
        ExecutionBudget budget = new ExecutionBudget(3, 1000, 5, 10, 2, 20);

        budget.checkIterations("loop", 3);
        budget.checkDuration(1000, 2000);
        budget.checkChildExecutions(5);
        budget.checkAccumulatedBytes("loop", 10);
        budget.checkNestedDepth("loop", 2);

        assertExceeded(() -> budget.checkIterations("loop", 4));
        assertExceeded(() -> budget.checkDuration(1000, 2001));
        assertExceeded(() -> budget.checkChildExecutions(6));
        assertExceeded(() -> budget.checkAccumulatedBytes("loop", 11));
        assertExceeded(() -> budget.checkNestedDepth("loop", 3));
    }

    /**
     * 断言动作触发预算超限异常。
     *
     * @param action 待执行动作
     */
    private void assertExceeded(Runnable action) {
        try {
            action.run();
            Assert.fail("Expected ExecutionBudgetExceededException");
        } catch (ExecutionBudgetExceededException expected) {
            Assert.assertNotNull(expected.getMessage());
        }
    }
}
