from grantflow.core.job_runner import RedisJobRunner
from grantflow.tests.job_runner_helpers import (
    AlwaysFullRedisClient,
    FakeRedisClient,
    clear_redis_observed,
    redis_client_factory,
    redis_observed_values,
    redis_test_task,
    wait_until,
)


def test_redis_job_runner_executes_tasks_with_fake_client():
    fake_client = FakeRedisClient()
    clear_redis_observed()
    runner = RedisJobRunner(
        worker_count=1,
        queue_maxsize=8,
        redis_url="redis://local-test/0",
        queue_name="grantflow:test:jobs",
        pop_timeout_seconds=0.1,
        redis_client_factory=redis_client_factory(fake_client),
    )

    assert runner.submit(redis_test_task, 7) is True
    assert wait_until(lambda: redis_observed_values() == [7])
    diag = runner.diagnostics()
    assert diag["backend"] == "redis"
    assert diag["redis_available"] is True
    assert diag["completed_count"] >= 1
    assert diag["failed_count"] == 0
    assert diag["retry_count"] == 0
    assert diag["dead_lettered_count"] == 0
    assert isinstance(diag.get("worker_heartbeat"), dict)
    runner.stop()


def test_redis_job_runner_rejects_non_json_serializable_args():
    fake_client = FakeRedisClient()
    runner = RedisJobRunner(
        worker_count=1,
        queue_maxsize=8,
        redis_url="redis://local-test/0",
        queue_name="grantflow:test:jobs:opaque",
        pop_timeout_seconds=0.1,
        redis_client_factory=redis_client_factory(fake_client),
    )
    try:
        try:
            runner.submit(redis_test_task, object())
            assert False, "Expected submit to reject non-JSON arguments"
        except TypeError as exc:
            assert "JSON-serializable" in str(exc)
    finally:
        runner.stop()


def test_redis_job_runner_rejects_submit_when_queue_is_full():
    fake_client = AlwaysFullRedisClient()
    runner = RedisJobRunner(
        worker_count=1,
        queue_maxsize=1,
        redis_url="redis://local-test/0",
        queue_name="grantflow:test:jobs:full",
        pop_timeout_seconds=1.0,
        redis_client_factory=redis_client_factory(fake_client),
    )
    assert runner.submit(redis_test_task, 1) is False
    runner.stop()
