beforeAll()
{
    TEST=$( mktemp -d )


    mkdir -p "$TEST/.core/hooks"

    cp "$PWD/hooks/pre-push" "$TEST/.core/hooks/pre-push"


    cd $TEST > /dev/null || exit 1
}

afterAll()
{
    cd - > /dev/null || exit 1

    [[ -n "$TEST" && -d "$TEST" ]] && rm -r $TEST
}


mock()
{
    STAGED=($1)
    HEAD=$2
    COMMANDS=$3

    git()
    {
        if [[ $1 == "diff" && $2 == "--staged" && $3 == "--name-only" ]]

        then
            printf "%s\n" "${STAGED[@]}"
        fi

        if [[ $1 == "restore" && $2 == "--staged" ]]

        then
            echo "Mock : git restore --staged ${@:3}"
        fi

        if [[ $1 == "rev-list" && $2 == "HEAD" && $3 == "--invert-grep" ]]

        then
            echo $HEAD
        fi

        if [[ $1 == "reset" && $2 == "--soft" && $4 == "--quiet" ]]

        then
            echo "Mock : git reset --soft $3 --quiet"
        fi


        if [[ -f "$COMMANDS" ]]

        then
            echo $@ >> $COMMANDS
        fi
    }
}

unmock()
{
    unset -f git
}




it_restores_staged_files_if_staged_files_exist()
{
    mock "file.001.foo file.002.foo"

    output=$( source "$TEST/.core/hooks/pre-push" )
    echo $output | grep -q "Mock : git restore --staged file.001.foo file.002.foo"
    assert "Should restore staged files"

    unmock
}


it_exports_staged_files_if_staged_files_exist()
{
    mock "file.001.foo file.002.foo"

    source "$TEST/.core/hooks/pre-push" > /dev/null
    [[ -n $FLINT_STAGED_FILES ]]
    assert "Should export staged files"
    echo $FLINT_STAGED_FILES | grep -q "file.001.foo file.002.foo"
    assert "Should list staged files"
}


it_handles_no_manual_commits()
{
    mock

    output=$( source "$TEST/.core/hooks/pre-push" )
    echo $output | grep -qv "git reset"
    assert "Should do nothing when no manual commits are found"

    unmock
}


it_resets_to_last_manual_commit_if_manual_commit_exists()
{
    mock "" "foo"

    output=$( source "$TEST/.core/hooks/pre-push" )
    echo $output | grep -q "Mock : git reset --soft foo"
    assert "Should reset to the last non-temporary commit"

    unmock
}


it_resets_to_last_manual_commit_silently()
{
    mock "" "bar"

    output=$( source "$TEST/.core/hooks/pre-push" )
    echo $output | grep -q "Mock : git reset --soft bar --quiet"
    assert "Should reset to the last non-temporary commit"

    unmock
}


it_uses_correct_git_commands()
{
    commands=$( mktemp )

    mock "file.001.foo" "baz" $commands

    source "$TEST/.core/hooks/pre-push" > /dev/null
    [[ "$( head -n 1 "$commands" | tail -n 1 )" == "diff --staged --name-only" ]]
    assert "Should use correct staged command"
    [[ "$( head -n 2 "$commands" | tail -n 1 )" == "restore --staged file.001.foo" ]]
    assert "Should use correct restore command"
    [[ "$( head -n 3 "$commands" | tail -n 1 )" == "rev-list HEAD --invert-grep --grep=FLINT-TEMPORARY-COMMIT --max-count=1" ]]
    assert "Should use correct rev-list command"
    [[ "$( head -n 4 "$commands" | tail -n 1 )" == "reset --soft baz --quiet" ]]
    assert "Should use correct reset command"

    unmock

    [[ -f "$commands" ]] && rm $commands
}
