"""
Test suite for vulture, our in-house mock of HTCondor
"""
import os
from enum import Enum

import pytest
from vulture.condorlite import DAGMan, Job, MockLogger


def test_job_execution(caplog):
    """
    Test execution of the null executable
    """
    log_entries = [
        "000 (4242.000.000)",
        "Job submitted from host: <0.0.0.0:7777>",
        "Job executing on host: <0.0.0.0:7777>",
        "Job terminated.",
        "    Job node: null" "    Normal termination (return value 0)",
    ]
    test_job = Job("test/null.condor")
    test_job.execute()
    output = "\n".join([record.message for record in caplog.records])
    assert [entry in output for entry in log_entries]


def test_job_exec_failed(caplog):
    """
    Test null -e, which will terminate with return value -1
    """
    log_entries = [
        "000 (4242.000.000)",
        "Job submitted from host: <0.0.0.0:7777>",
        "Job executing on host: <0.0.0.0:7777>",
        "Job terminated.",
        "    Job node: null" "    Error in execution (return value -1)",
    ]
    test_job = Job("test/null_fail.condor")
    test_job.execute()
    output = "\n".join([record.message for record in caplog.records])
    assert [entry in output for entry in log_entries]


def test_job_stdout():
    """
    Test that vulture writes correct output from stdout to a log file
    """
    test_job = Job("test/null.condor")
    test_job.execute()
    with open("null.out", "r") as f:
        contents = f.read()
    assert "Hello, world!" in contents
    # Clean up output files
    os.remove("null.out")
    os.remove("null.error")


def test_shell_script_in_working_directory(caplog):
    test_job = Job("test/localscript.condor")
    test_job.execute()
    with open("test.sh.out", "r") as f:
        contents = f.read()
    assert "hello" in contents
    os.remove("test.sh.out")
    os.remove("test.sh.error")


def test_job_stderr():
    """
    Test that vulture writes correct output from stderr to a log file
    """
    test_job = Job("test/null_fail.condor")
    test_job.execute()
    with open("null.error", "r") as f:
        contents = f.read()
    assert "ERROR: An error has been purposefully induced." in contents
    # Clean up output files
    os.remove("null.out")
    os.remove("null.error")


def test_write_out_error():
    """
    Tests that write_out correctly errors out
    """
    with pytest.raises(ValueError):
        MockLogger("test/null.condor", "null.out", "null.err").write_out(
            "wrong", "this should fail"
        )


class MockWorkflowEventType(Enum):
    TEST = -2


def test_create_header_error():
    """
    Tests that create_header correctly errors out
    """
    with pytest.raises(ValueError):
        MockLogger("test/null.condor", "null.out", "null.err").create_header(
            MockWorkflowEventType.TEST
        )


def test_job_parse_error():
    """
    Tests that Job.parse() correctly errors out
    """
    with pytest.raises(SystemExit):
        Job("does_not_exist.txt", False)
    with pytest.raises(ValueError) as e:
        Job("test/malformed.condor", False)


def test_job_execute_error():
    """
    Tests that Job.execute() correctly errors out
    """
    with pytest.raises(ValueError) as e:
        Job("test/no-exec.condor", False).execute()


def test_dag_parse():
    """
    Tests that a HTCondor-formatted DAG file can be correctly parsed
    """
    # Null DAG (linear)
    # A -> B -> C
    test_dag_null = DAGMan("test/null.dag")
    expected_dag_null = {
        "null_greeting": {"null_error"},
        "null_error": {"null_exit_fail"},
        "null_exit_fail": set(),
    }
    assert test_dag_null.dag.graph == expected_dag_null
    # Diamond DAG
    #   A
    #  / \
    # B   C
    #  \ /
    #   D
    test_dag_diamond = DAGMan("test/diamond.dag")
    expected_dag_diamond = {
        "A": {"B", "C"},
        "B": {"D"},
        "C": {"D"},
        "D": set(),
    }
    assert test_dag_diamond.dag.graph == expected_dag_diamond


def test_dag_parse_error():
    """
    Tests that DAG parsing correctly detects a malformed DAG file
    """
    with pytest.raises(SystemExit):
        DAGMan("test/does_not_exist.dag")
    with pytest.raises(ValueError):
        # Malformed DAG file
        DAGMan("test/malformed.dag")
        # DAG created will contain a cycle
        DAGMan("test/invalid.dag")


def test_dag_execute(caplog):
    """
    Tests that a HTCondor-formatted DAG file can be correctly executed
    """
    test_dag = DAGMan("test/null.dag")
    test_dag.execute(False)
    expected_messages = [
        "Hello, world!\n",
        "ERROR: This is an error.\n",
        "ERROR: An error has been purposefully induced.\n",
    ]
    messages = [record.message for record in caplog.records]
    assert [expected_message in messages for expected_message in expected_messages]
    # Clear logged messages
    caplog.clear()
    # DAG with reversed order from the last
    test_dag_2 = DAGMan("test/null2.dag")
    test_dag_2.execute(False)
    messages = [record.message for record in caplog.records]
    assert [expected_message in messages for expected_message in expected_messages]