diff --git a/README.md b/README.md index c6178a6..34af565 100644 --- a/README.md +++ b/README.md @@ -137,3 +137,9 @@ orcli uses [bashly](https://github.com/DannyBen/bashly/) for generating the one- ```sh bashly generate --upgrade ``` + +4. Run tests + + ```sh + ./orcli test + ``` \ No newline at end of file diff --git a/orcli b/orcli index 52d5779..08e93bc 100755 --- a/orcli +++ b/orcli @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# This script was generated by bashly 0.8.10 (https://bashly.dannyb.co) +# This script was generated by bashly 0.9.0 (https://bashly.dannyb.co) # Modifying it manually is not recommended # :wrapper.bash3_bouncer @@ -38,6 +38,7 @@ orcli_usage() { echo " import commands to create OpenRefine projects from files or URLs" echo " list list projects on OpenRefine server" echo " info show OpenRefine project's metadata" + echo " test run functional tests on tmp OpenRefine workspace" echo " transform apply undo/redo JSON file(s) to an OpenRefine project" echo " export commands to export data from OpenRefine projects to files" echo " run run tmp OpenRefine workspace and execute shell script(s)" @@ -363,6 +364,35 @@ orcli_info_usage() { fi } +# :command.usage +orcli_test_usage() { + if [[ -n $long_usage ]]; then + printf "orcli test - run functional tests on tmp OpenRefine workspace\n" + echo + + else + printf "orcli test - run functional tests on tmp OpenRefine workspace\n" + echo + + fi + + printf "Usage:\n" + printf " orcli test\n" + printf " orcli test --help | -h\n" + echo + + # :command.long_usage + if [[ -n $long_usage ]]; then + printf "Options:\n" + + # :command.usage_fixed_flags + echo " --help, -h" + printf " Show this help\n" + echo + + fi +} + # :command.usage orcli_transform_usage() { if [[ -n $long_usage ]]; then @@ -596,7 +626,7 @@ normalize_input() { input+=("${BASH_REMATCH[2]}") elif [[ $arg =~ ^-([a-zA-Z0-9][a-zA-Z0-9]+)$ ]]; then flags="${BASH_REMATCH[1]}" - for (( i=0 ; i < ${#flags} ; i++ )); do + for ((i = 0; i < ${#flags}; i++)); do input+=("-${flags:i:1}") done else @@ -609,14 +639,14 @@ normalize_input() { # :command.inspect_args inspect_args() { readarray -t sorted_keys < <(printf '%s\n' "${!args[@]}" | sort) - if (( ${#args[@]} )); then + if ((${#args[@]})); then echo args: for k in "${sorted_keys[@]}"; do echo "- \${args[$k]} = ${args[$k]}"; done else echo args: none fi - if (( ${#other_args[@]} )); then + if ((${#other_args[@]})); then echo echo other_args: echo "- \${other_args[*]} = ${other_args[*]}" @@ -880,12 +910,16 @@ send_completions() { echo $' while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "$(_orcli_completions_filter "--help -h")" -- "$cur" )' echo $' ;;' echo $'' + echo $' \'test\'*)' + echo $' while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "$(_orcli_completions_filter "--help -h")" -- "$cur" )' + echo $' ;;' + echo $'' echo $' \'run\'*)' echo $' while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "$(_orcli_completions_filter "--help --interactive --memory --port --quiet -h -q")" -- "$cur" )' echo $' ;;' echo $'' echo $' *)' - echo $' while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "$(_orcli_completions_filter "--help --version -h -v completions export import info list run transform")" -- "$cur" )' + echo $' while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "$(_orcli_completions_filter "--help --version -h -v completions export import info list run test transform")" -- "$cur" )' echo $' ;;' echo $'' echo $' esac' @@ -1004,6 +1038,85 @@ orcli_info_command() { echo "$projectid" } +# :command.function +orcli_test_command() { + # src/test_command.sh + # shellcheck shell=bash disable=SC2154 + + # locate orcli and OpenRefine + scriptpath=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")") + if [[ -x "${scriptpath}/refine" ]]; then + openrefine="${scriptpath}/refine" + else + error "OpenRefine's startup script (refine) not found!" "Did you put orcli in your OpenRefine app dir?" + fi + + # create tmp directory + OPENREFINE_TMPDIR="$(mktemp -d)" + trap '{ rm -rf "$OPENREFINE_TMPDIR"; }' 0 2 3 15 + + # download the test files if needed + if ! [[ -f "tests/help.sh" ]]; then + cd "$OPENREFINE_TMPDIR" + if ! curl -fs -L -o orcli.zip https://github.com/opencultureconsulting/orcli/archive/refs/heads/main.zip; then + error "downloading test files failed!" "Please download the tests dir manually from GitHub." + fi + unzip -q -j orcli.zip "*/tests/*.sh" -d "tests/" + unzip -q -j orcli.zip "*/tests/data/*" -d "tests/data/" + fi + + # check if OpenRefine is already running + if curl -fs "${OPENREFINE_URL}" &>/dev/null; then + error "OpenRefine is already running on port 3333." "Please stop the other process." + fi + + # start OpenRefine with tmp workspace + $openrefine -d "$OPENREFINE_TMPDIR" -x refine.headless=true -v warn &>"$OPENREFINE_TMPDIR/openrefine.log" & + OPENREFINE_PID="$!" + + # update trap to kill OpenRefine on error or exit + trap '{ rm -rf "$OPENREFINE_TMPDIR"; kill -9 "$OPENREFINE_PID"; }' 0 2 3 15 + + # wait until OpenRefine is running (timeout 20s) + if ! curl -fs --retry 20 --retry-connrefused --retry-delay 1 "${OPENREFINE_URL}/command/core/get-version" &>/dev/null; then + error "starting OpenRefine server failed!" + else + log "started OpenRefine with tmp workspace ${OPENREFINE_TMPDIR}" + fi + + # execute tests in subshell + export OPENREFINE_TMPDIR OPENREFINE_URL OPENREFINE_PID + cd "tests" + files=(*.sh) + results=() + for i in "${!files[@]}"; do + set +e # do not exit on failed tests + bash -e <( + if ! command -v orcli &>/dev/null; then + echo "shopt -s expand_aliases" + echo "alias orcli=${scriptpath}/orcli" + fi + awk 1 "${files[$i]}" + ) &>"$OPENREFINE_TMPDIR/test.log" + results+=(${?}) + set -e + if [[ "${results[$i]}" =~ [1-9] ]]; then + cat "$OPENREFINE_TMPDIR/test.log" + log "FAILED ${files[$i]} with exit code ${results[$i]}!" + else + log "PASSED ${files[$i]}" + fi + done + + # print overall result + if [[ "${results[*]}" =~ [1-9] ]]; then + error "failed tests!" + else + log "all tests passed!" + fi + +} + # :command.function orcli_transform_command() { # src/transform_command.sh @@ -1253,30 +1366,37 @@ orcli_run_command() { # :command.parse_requirements parse_requirements() { # :command.fixed_flags_filter - case "${1:-}" in - --version | -v ) - version_command - exit - ;; + while [[ $# -gt 0 ]]; do + case "${1:-}" in + --version | -v) + version_command + exit + ;; - --help | -h ) - long_usage=yes - orcli_usage - exit - ;; + --help | -h) + long_usage=yes + orcli_usage + exit + ;; - esac + *) + break + ;; + + esac + done # :command.environment_variables_filter # :command.environment_variables_default export OPENREFINE_URL="${OPENREFINE_URL:-http://localhost:3333}" # :command.dependencies_filter - if ! [[ -x "$(command -v curl)" ]]; then + if ! command -v curl >/dev/null 2>&1; then printf "missing dependency: curl\n" >&2 exit 1 fi - if ! [[ -x "$(command -v jq)" ]]; then + + if ! command -v jq >/dev/null 2>&1; then printf "missing dependency: jq\n" >&2 exit 1 fi @@ -1285,68 +1405,75 @@ parse_requirements() { action=${1:-} case $action in - -* ) - ;; + -*) + ;; - completions ) - action="completions" - shift - orcli_completions_parse_requirements "$@" - shift $# - ;; + completions) + action="completions" + shift + orcli_completions_parse_requirements "$@" + shift $# + ;; - import ) - action="import" - shift - orcli_import_parse_requirements "$@" - shift $# - ;; + import) + action="import" + shift + orcli_import_parse_requirements "$@" + shift $# + ;; - list ) - action="list" - shift - orcli_list_parse_requirements "$@" - shift $# - ;; + list) + action="list" + shift + orcli_list_parse_requirements "$@" + shift $# + ;; - info ) - action="info" - shift - orcli_info_parse_requirements "$@" - shift $# - ;; + info) + action="info" + shift + orcli_info_parse_requirements "$@" + shift $# + ;; - transform ) - action="transform" - shift - orcli_transform_parse_requirements "$@" - shift $# - ;; + test) + action="test" + shift + orcli_test_parse_requirements "$@" + shift $# + ;; - export ) - action="export" - shift - orcli_export_parse_requirements "$@" - shift $# - ;; + transform) + action="transform" + shift + orcli_transform_parse_requirements "$@" + shift $# + ;; - run ) - action="run" - shift - orcli_run_parse_requirements "$@" - shift $# - ;; + export) + action="export" + shift + orcli_export_parse_requirements "$@" + shift $# + ;; - # :command.command_fallback - "" ) - orcli_usage >&2 - exit 1 - ;; + run) + action="run" + shift + orcli_run_parse_requirements "$@" + shift $# + ;; - * ) - printf "invalid command: %s\n" "$action" >&2 - exit 1 - ;; + # :command.command_fallback + "") + orcli_usage >&2 + exit 1 + ;; + + *) + printf "invalid command: %s\n" "$action" >&2 + exit 1 + ;; esac @@ -1355,18 +1482,18 @@ parse_requirements() { key="$1" case "$key" in - -?* ) - printf "invalid option: %s\n" "$key" >&2 - exit 1 - ;; + -?*) + printf "invalid option: %s\n" "$key" >&2 + exit 1 + ;; - * ) - # :command.parse_requirements_case - # :command.parse_requirements_case_simple - printf "invalid argument: %s\n" "$key" >&2 - exit 1 + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_simple + printf "invalid argument: %s\n" "$key" >&2 + exit 1 - ;; + ;; esac done @@ -1376,14 +1503,20 @@ parse_requirements() { # :command.parse_requirements orcli_completions_parse_requirements() { # :command.fixed_flags_filter - case "${1:-}" in - --help | -h ) - long_usage=yes - orcli_completions_usage - exit - ;; + while [[ $# -gt 0 ]]; do + case "${1:-}" in + --help | -h) + long_usage=yes + orcli_completions_usage + exit + ;; - esac + *) + break + ;; + + esac + done # :command.command_filter action="completions" @@ -1393,18 +1526,18 @@ orcli_completions_parse_requirements() { key="$1" case "$key" in - -?* ) - printf "invalid option: %s\n" "$key" >&2 - exit 1 - ;; + -?*) + printf "invalid option: %s\n" "$key" >&2 + exit 1 + ;; - * ) - # :command.parse_requirements_case - # :command.parse_requirements_case_simple - printf "invalid argument: %s\n" "$key" >&2 - exit 1 + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_simple + printf "invalid argument: %s\n" "$key" >&2 + exit 1 - ;; + ;; esac done @@ -1414,46 +1547,52 @@ orcli_completions_parse_requirements() { # :command.parse_requirements orcli_import_parse_requirements() { # :command.fixed_flags_filter - case "${1:-}" in - --help | -h ) - long_usage=yes - orcli_import_usage - exit - ;; + while [[ $# -gt 0 ]]; do + case "${1:-}" in + --help | -h) + long_usage=yes + orcli_import_usage + exit + ;; - esac + *) + break + ;; + + esac + done # :command.command_filter action=${1:-} case $action in - -* ) - ;; + -*) + ;; - csv ) - action="csv" - shift - orcli_import_csv_parse_requirements "$@" - shift $# - ;; + csv) + action="csv" + shift + orcli_import_csv_parse_requirements "$@" + shift $# + ;; - tsv ) - action="tsv" - shift - orcli_import_tsv_parse_requirements "$@" - shift $# - ;; + tsv) + action="tsv" + shift + orcli_import_tsv_parse_requirements "$@" + shift $# + ;; - # :command.command_fallback - "" ) - orcli_import_usage >&2 - exit 1 - ;; + # :command.command_fallback + "") + orcli_import_usage >&2 + exit 1 + ;; - * ) - printf "invalid command: %s\n" "$action" >&2 - exit 1 - ;; + *) + printf "invalid command: %s\n" "$action" >&2 + exit 1 + ;; esac @@ -1462,18 +1601,18 @@ orcli_import_parse_requirements() { key="$1" case "$key" in - -?* ) - printf "invalid option: %s\n" "$key" >&2 - exit 1 - ;; + -?*) + printf "invalid option: %s\n" "$key" >&2 + exit 1 + ;; - * ) - # :command.parse_requirements_case - # :command.parse_requirements_case_simple - printf "invalid argument: %s\n" "$key" >&2 - exit 1 + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_simple + printf "invalid argument: %s\n" "$key" >&2 + exit 1 - ;; + ;; esac done @@ -1483,14 +1622,20 @@ orcli_import_parse_requirements() { # :command.parse_requirements orcli_import_csv_parse_requirements() { # :command.fixed_flags_filter - case "${1:-}" in - --help | -h ) - long_usage=yes - orcli_import_csv_usage - exit - ;; + while [[ $# -gt 0 ]]; do + case "${1:-}" in + --help | -h) + long_usage=yes + orcli_import_csv_usage + exit + ;; - esac + *) + break + ;; + + esac + done # :command.command_filter action="import csv" @@ -1499,85 +1644,85 @@ orcli_import_csv_parse_requirements() { while [[ $# -gt 0 ]]; do key="$1" case "$key" in - # :flag.case - --separator ) + # :flag.case + --separator) - # :flag.case_arg - if [[ -n ${2+x} ]]; then + # :flag.case_arg + if [[ -n ${2+x} ]]; then - args[--separator]="$2" + args[--separator]="$2" + shift + shift + else + printf "%s\n" "--separator requires an argument: --separator SEPARATOR" >&2 + exit 1 + fi + ;; + + # :flag.case + --encoding) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + + args[--encoding]="$2" + shift + shift + else + printf "%s\n" "--encoding requires an argument: --encoding ENCODING" >&2 + exit 1 + fi + ;; + + # :flag.case + --trimStrings) + + # :flag.case_no_arg + args[--trimStrings]=1 shift + ;; + + # :flag.case + --projectName) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + + args[--projectName]="$2" + shift + shift + else + printf "%s\n" "--projectName requires an argument: --projectName PROJECTNAME" >&2 + exit 1 + fi + ;; + + # :flag.case + --quiet | -q) + + # :flag.case_no_arg + args[--quiet]=1 shift - else - printf "%s\n" "--separator requires an argument: --separator SEPARATOR" >&2 + ;; + + -?*) + printf "invalid option: %s\n" "$key" >&2 exit 1 - fi - ;; + ;; - # :flag.case - --encoding ) + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_repeatable + if [[ -z ${args[file]+x} ]]; then - # :flag.case_arg - if [[ -n ${2+x} ]]; then + args[file]="\"$1\"" + shift + else + args[file]="${args[file]} \"$1\"" + shift + fi - args[--encoding]="$2" - shift - shift - else - printf "%s\n" "--encoding requires an argument: --encoding ENCODING" >&2 - exit 1 - fi - ;; - - # :flag.case - --trimStrings ) - - # :flag.case_no_arg - args[--trimStrings]=1 - shift - ;; - - # :flag.case - --projectName ) - - # :flag.case_arg - if [[ -n ${2+x} ]]; then - - args[--projectName]="$2" - shift - shift - else - printf "%s\n" "--projectName requires an argument: --projectName PROJECTNAME" >&2 - exit 1 - fi - ;; - - # :flag.case - --quiet | -q ) - - # :flag.case_no_arg - args[--quiet]=1 - shift - ;; - - -?* ) - printf "invalid option: %s\n" "$key" >&2 - exit 1 - ;; - - * ) - # :command.parse_requirements_case - # :command.parse_requirements_case_repeatable - if [[ -z ${args[file]+x} ]]; then - - args[file]="\"$1\"" - shift - else - args[file]="${args[file]} \"$1\"" - shift - fi - - ;; + ;; esac done @@ -1591,14 +1736,20 @@ orcli_import_csv_parse_requirements() { # :command.parse_requirements orcli_import_tsv_parse_requirements() { # :command.fixed_flags_filter - case "${1:-}" in - --help | -h ) - long_usage=yes - orcli_import_tsv_usage - exit - ;; + while [[ $# -gt 0 ]]; do + case "${1:-}" in + --help | -h) + long_usage=yes + orcli_import_tsv_usage + exit + ;; - esac + *) + break + ;; + + esac + done # :command.command_filter action="import tsv" @@ -1607,70 +1758,70 @@ orcli_import_tsv_parse_requirements() { while [[ $# -gt 0 ]]; do key="$1" case "$key" in - # :flag.case - --encoding ) + # :flag.case + --encoding) - # :flag.case_arg - if [[ -n ${2+x} ]]; then + # :flag.case_arg + if [[ -n ${2+x} ]]; then - args[--encoding]="$2" + args[--encoding]="$2" + shift + shift + else + printf "%s\n" "--encoding requires an argument: --encoding ENCODING" >&2 + exit 1 + fi + ;; + + # :flag.case + --trimStrings) + + # :flag.case_no_arg + args[--trimStrings]=1 shift + ;; + + # :flag.case + --projectName) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + + args[--projectName]="$2" + shift + shift + else + printf "%s\n" "--projectName requires an argument: --projectName PROJECTNAME" >&2 + exit 1 + fi + ;; + + # :flag.case + --quiet | -q) + + # :flag.case_no_arg + args[--quiet]=1 shift - else - printf "%s\n" "--encoding requires an argument: --encoding ENCODING" >&2 + ;; + + -?*) + printf "invalid option: %s\n" "$key" >&2 exit 1 - fi - ;; + ;; - # :flag.case - --trimStrings ) + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_repeatable + if [[ -z ${args[file]+x} ]]; then - # :flag.case_no_arg - args[--trimStrings]=1 - shift - ;; + args[file]="\"$1\"" + shift + else + args[file]="${args[file]} \"$1\"" + shift + fi - # :flag.case - --projectName ) - - # :flag.case_arg - if [[ -n ${2+x} ]]; then - - args[--projectName]="$2" - shift - shift - else - printf "%s\n" "--projectName requires an argument: --projectName PROJECTNAME" >&2 - exit 1 - fi - ;; - - # :flag.case - --quiet | -q ) - - # :flag.case_no_arg - args[--quiet]=1 - shift - ;; - - -?* ) - printf "invalid option: %s\n" "$key" >&2 - exit 1 - ;; - - * ) - # :command.parse_requirements_case - # :command.parse_requirements_case_repeatable - if [[ -z ${args[file]+x} ]]; then - - args[file]="\"$1\"" - shift - else - args[file]="${args[file]} \"$1\"" - shift - fi - - ;; + ;; esac done @@ -1683,14 +1834,20 @@ orcli_import_tsv_parse_requirements() { # :command.parse_requirements orcli_list_parse_requirements() { # :command.fixed_flags_filter - case "${1:-}" in - --help | -h ) - long_usage=yes - orcli_list_usage - exit - ;; + while [[ $# -gt 0 ]]; do + case "${1:-}" in + --help | -h) + long_usage=yes + orcli_list_usage + exit + ;; - esac + *) + break + ;; + + esac + done # :command.command_filter action="list" @@ -1700,18 +1857,18 @@ orcli_list_parse_requirements() { key="$1" case "$key" in - -?* ) - printf "invalid option: %s\n" "$key" >&2 - exit 1 - ;; + -?*) + printf "invalid option: %s\n" "$key" >&2 + exit 1 + ;; - * ) - # :command.parse_requirements_case - # :command.parse_requirements_case_simple - printf "invalid argument: %s\n" "$key" >&2 - exit 1 + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_simple + printf "invalid argument: %s\n" "$key" >&2 + exit 1 - ;; + ;; esac done @@ -1721,14 +1878,20 @@ orcli_list_parse_requirements() { # :command.parse_requirements orcli_info_parse_requirements() { # :command.fixed_flags_filter - case "${1:-}" in - --help | -h ) - long_usage=yes - orcli_info_usage - exit - ;; + while [[ $# -gt 0 ]]; do + case "${1:-}" in + --help | -h) + long_usage=yes + orcli_info_usage + exit + ;; - esac + *) + break + ;; + + esac + done # :command.command_filter action="info" @@ -1738,24 +1901,24 @@ orcli_info_parse_requirements() { key="$1" case "$key" in - -?* ) - printf "invalid option: %s\n" "$key" >&2 - exit 1 - ;; - - * ) - # :command.parse_requirements_case - # :command.parse_requirements_case_simple - if [[ -z ${args[project]+x} ]]; then - - args[project]=$1 - shift - else - printf "invalid argument: %s\n" "$key" >&2 + -?*) + printf "invalid option: %s\n" "$key" >&2 exit 1 - fi + ;; - ;; + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_simple + if [[ -z ${args[project]+x} ]]; then + + args[project]=$1 + shift + else + printf "invalid argument: %s\n" "$key" >&2 + exit 1 + fi + + ;; esac done @@ -1768,17 +1931,67 @@ orcli_info_parse_requirements() { } +# :command.parse_requirements +orcli_test_parse_requirements() { + # :command.fixed_flags_filter + while [[ $# -gt 0 ]]; do + case "${1:-}" in + --help | -h) + long_usage=yes + orcli_test_usage + exit + ;; + + *) + break + ;; + + esac + done + + # :command.command_filter + action="test" + + # :command.parse_requirements_while + while [[ $# -gt 0 ]]; do + key="$1" + case "$key" in + + -?*) + printf "invalid option: %s\n" "$key" >&2 + exit 1 + ;; + + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_simple + printf "invalid argument: %s\n" "$key" >&2 + exit 1 + + ;; + + esac + done + +} + # :command.parse_requirements orcli_transform_parse_requirements() { # :command.fixed_flags_filter - case "${1:-}" in - --help | -h ) - long_usage=yes - orcli_transform_usage - exit - ;; + while [[ $# -gt 0 ]]; do + case "${1:-}" in + --help | -h) + long_usage=yes + orcli_transform_usage + exit + ;; - esac + *) + break + ;; + + esac + done # :command.command_filter action="transform" @@ -1787,36 +2000,36 @@ orcli_transform_parse_requirements() { while [[ $# -gt 0 ]]; do key="$1" case "$key" in - # :flag.case - --quiet | -q ) + # :flag.case + --quiet | -q) - # :flag.case_no_arg - args[--quiet]=1 - shift - ;; - - -?* ) - printf "invalid option: %s\n" "$key" >&2 - exit 1 - ;; - - * ) - # :command.parse_requirements_case - # :command.parse_requirements_case_repeatable - if [[ -z ${args[project]+x} ]]; then - - args[project]=$1 + # :flag.case_no_arg + args[--quiet]=1 shift - elif [[ -z ${args[file]+x} ]]; then + ;; - args[file]="\"$1\"" - shift - else - args[file]="${args[file]} \"$1\"" - shift - fi + -?*) + printf "invalid option: %s\n" "$key" >&2 + exit 1 + ;; - ;; + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_repeatable + if [[ -z ${args[project]+x} ]]; then + + args[project]=$1 + shift + elif [[ -z ${args[file]+x} ]]; then + + args[file]="\"$1\"" + shift + else + args[file]="${args[file]} \"$1\"" + shift + fi + + ;; esac done @@ -1835,39 +2048,45 @@ orcli_transform_parse_requirements() { # :command.parse_requirements orcli_export_parse_requirements() { # :command.fixed_flags_filter - case "${1:-}" in - --help | -h ) - long_usage=yes - orcli_export_usage - exit - ;; + while [[ $# -gt 0 ]]; do + case "${1:-}" in + --help | -h) + long_usage=yes + orcli_export_usage + exit + ;; - esac + *) + break + ;; + + esac + done # :command.command_filter action=${1:-} case $action in - -* ) - ;; + -*) + ;; - tsv ) - action="tsv" - shift - orcli_export_tsv_parse_requirements "$@" - shift $# - ;; + tsv) + action="tsv" + shift + orcli_export_tsv_parse_requirements "$@" + shift $# + ;; - # :command.command_fallback - "" ) - orcli_export_usage >&2 - exit 1 - ;; + # :command.command_fallback + "") + orcli_export_usage >&2 + exit 1 + ;; - * ) - printf "invalid command: %s\n" "$action" >&2 - exit 1 - ;; + *) + printf "invalid command: %s\n" "$action" >&2 + exit 1 + ;; esac @@ -1876,18 +2095,18 @@ orcli_export_parse_requirements() { key="$1" case "$key" in - -?* ) - printf "invalid option: %s\n" "$key" >&2 - exit 1 - ;; + -?*) + printf "invalid option: %s\n" "$key" >&2 + exit 1 + ;; - * ) - # :command.parse_requirements_case - # :command.parse_requirements_case_simple - printf "invalid argument: %s\n" "$key" >&2 - exit 1 + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_simple + printf "invalid argument: %s\n" "$key" >&2 + exit 1 - ;; + ;; esac done @@ -1897,14 +2116,20 @@ orcli_export_parse_requirements() { # :command.parse_requirements orcli_export_tsv_parse_requirements() { # :command.fixed_flags_filter - case "${1:-}" in - --help | -h ) - long_usage=yes - orcli_export_tsv_usage - exit - ;; + while [[ $# -gt 0 ]]; do + case "${1:-}" in + --help | -h) + long_usage=yes + orcli_export_tsv_usage + exit + ;; - esac + *) + break + ;; + + esac + done # :command.command_filter action="export tsv" @@ -1913,62 +2138,62 @@ orcli_export_tsv_parse_requirements() { while [[ $# -gt 0 ]]; do key="$1" case "$key" in - # :flag.case - --output ) + # :flag.case + --output) - # :flag.case_arg - if [[ -n ${2+x} ]]; then + # :flag.case_arg + if [[ -n ${2+x} ]]; then - args[--output]="$2" + args[--output]="$2" + shift + shift + else + printf "%s\n" "--output requires an argument: --output FILE" >&2 + exit 1 + fi + ;; + + # :flag.case + --encoding) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + + args[--encoding]="$2" + shift + shift + else + printf "%s\n" "--encoding requires an argument: --encoding ENCODING" >&2 + exit 1 + fi + ;; + + # :flag.case + --quiet | -q) + + # :flag.case_no_arg + args[--quiet]=1 shift - shift - else - printf "%s\n" "--output requires an argument: --output FILE" >&2 + ;; + + -?*) + printf "invalid option: %s\n" "$key" >&2 exit 1 - fi - ;; + ;; - # :flag.case - --encoding ) + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_simple + if [[ -z ${args[project]+x} ]]; then - # :flag.case_arg - if [[ -n ${2+x} ]]; then + args[project]=$1 + shift + else + printf "invalid argument: %s\n" "$key" >&2 + exit 1 + fi - args[--encoding]="$2" - shift - shift - else - printf "%s\n" "--encoding requires an argument: --encoding ENCODING" >&2 - exit 1 - fi - ;; - - # :flag.case - --quiet | -q ) - - # :flag.case_no_arg - args[--quiet]=1 - shift - ;; - - -?* ) - printf "invalid option: %s\n" "$key" >&2 - exit 1 - ;; - - * ) - # :command.parse_requirements_case - # :command.parse_requirements_case_simple - if [[ -z ${args[project]+x} ]]; then - - args[project]=$1 - shift - else - printf "invalid argument: %s\n" "$key" >&2 - exit 1 - fi - - ;; + ;; esac done @@ -1987,14 +2212,20 @@ orcli_export_tsv_parse_requirements() { # :command.parse_requirements orcli_run_parse_requirements() { # :command.fixed_flags_filter - case "${1:-}" in - --help | -h ) - long_usage=yes - orcli_run_usage - exit - ;; + while [[ $# -gt 0 ]]; do + case "${1:-}" in + --help | -h) + long_usage=yes + orcli_run_usage + exit + ;; - esac + *) + break + ;; + + esac + done # :command.command_filter action="run" @@ -2003,70 +2234,70 @@ orcli_run_parse_requirements() { while [[ $# -gt 0 ]]; do key="$1" case "$key" in - # :flag.case - --memory ) + # :flag.case + --memory) - # :flag.case_arg - if [[ -n ${2+x} ]]; then + # :flag.case_arg + if [[ -n ${2+x} ]]; then - args[--memory]="$2" + args[--memory]="$2" + shift + shift + else + printf "%s\n" "--memory requires an argument: --memory RAM" >&2 + exit 1 + fi + ;; + + # :flag.case + --port) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + + args[--port]="$2" + shift + shift + else + printf "%s\n" "--port requires an argument: --port PORT" >&2 + exit 1 + fi + ;; + + # :flag.case + --interactive) + + # :flag.case_no_arg + args[--interactive]=1 shift + ;; + + # :flag.case + --quiet | -q) + + # :flag.case_no_arg + args[--quiet]=1 shift - else - printf "%s\n" "--memory requires an argument: --memory RAM" >&2 + ;; + + -?*) + printf "invalid option: %s\n" "$key" >&2 exit 1 - fi - ;; + ;; - # :flag.case - --port ) + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_repeatable + if [[ -z ${args[file]+x} ]]; then - # :flag.case_arg - if [[ -n ${2+x} ]]; then + args[file]="\"$1\"" + shift + else + args[file]="${args[file]} \"$1\"" + shift + fi - args[--port]="$2" - shift - shift - else - printf "%s\n" "--port requires an argument: --port PORT" >&2 - exit 1 - fi - ;; - - # :flag.case - --interactive ) - - # :flag.case_no_arg - args[--interactive]=1 - shift - ;; - - # :flag.case - --quiet | -q ) - - # :flag.case_no_arg - args[--quiet]=1 - shift - ;; - - -?* ) - printf "invalid option: %s\n" "$key" >&2 - exit 1 - ;; - - * ) - # :command.parse_requirements_case - # :command.parse_requirements_case_repeatable - if [[ -z ${args[file]+x} ]]; then - - args[file]="\"$1\"" - shift - else - args[file]="${args[file]} \"$1\"" - shift - fi - - ;; + ;; esac done @@ -2099,89 +2330,111 @@ run() { normalize_input "$@" parse_requirements "${input[@]}" - if [[ $action == "completions" ]]; then - if [[ ${args[--help]:-} ]]; then - long_usage=yes - orcli_completions_usage - else - orcli_completions_command - fi + case "$action" in + "completions") + if [[ ${args[--help]:-} ]]; then + long_usage=yes + orcli_completions_usage + else + orcli_completions_command + fi + ;; - elif [[ $action == "import" ]]; then - if [[ ${args[--help]:-} ]]; then - long_usage=yes - orcli_import_usage - else - orcli_import_command - fi + "import") + if [[ ${args[--help]:-} ]]; then + long_usage=yes + orcli_import_usage + else + orcli_import_command + fi + ;; - elif [[ $action == "import csv" ]]; then - if [[ ${args[--help]:-} ]]; then - long_usage=yes - orcli_import_csv_usage - else - orcli_import_csv_command - fi + "import csv") + if [[ ${args[--help]:-} ]]; then + long_usage=yes + orcli_import_csv_usage + else + orcli_import_csv_command + fi + ;; - elif [[ $action == "import tsv" ]]; then - if [[ ${args[--help]:-} ]]; then - long_usage=yes - orcli_import_tsv_usage - else - orcli_import_tsv_command - fi + "import tsv") + if [[ ${args[--help]:-} ]]; then + long_usage=yes + orcli_import_tsv_usage + else + orcli_import_tsv_command + fi + ;; - elif [[ $action == "list" ]]; then - if [[ ${args[--help]:-} ]]; then - long_usage=yes - orcli_list_usage - else - orcli_list_command - fi + "list") + if [[ ${args[--help]:-} ]]; then + long_usage=yes + orcli_list_usage + else + orcli_list_command + fi + ;; - elif [[ $action == "info" ]]; then - if [[ ${args[--help]:-} ]]; then - long_usage=yes - orcli_info_usage - else - orcli_info_command - fi + "info") + if [[ ${args[--help]:-} ]]; then + long_usage=yes + orcli_info_usage + else + orcli_info_command + fi + ;; - elif [[ $action == "transform" ]]; then - if [[ ${args[--help]:-} ]]; then - long_usage=yes - orcli_transform_usage - else - orcli_transform_command - fi + "test") + if [[ ${args[--help]:-} ]]; then + long_usage=yes + orcli_test_usage + else + orcli_test_command + fi + ;; - elif [[ $action == "export" ]]; then - if [[ ${args[--help]:-} ]]; then - long_usage=yes - orcli_export_usage - else - orcli_export_command - fi + "transform") + if [[ ${args[--help]:-} ]]; then + long_usage=yes + orcli_transform_usage + else + orcli_transform_command + fi + ;; - elif [[ $action == "export tsv" ]]; then - if [[ ${args[--help]:-} ]]; then - long_usage=yes - orcli_export_tsv_usage - else - orcli_export_tsv_command - fi + "export") + if [[ ${args[--help]:-} ]]; then + long_usage=yes + orcli_export_usage + else + orcli_export_command + fi + ;; - elif [[ $action == "run" ]]; then - if [[ ${args[--help]:-} ]]; then - long_usage=yes - orcli_run_usage - else - orcli_run_command - fi + "export tsv") + if [[ ${args[--help]:-} ]]; then + long_usage=yes + orcli_export_tsv_usage + else + orcli_export_tsv_command + fi + ;; - elif [[ $action == "root" ]]; then - root_command - fi + "run") + if [[ ${args[--help]:-} ]]; then + long_usage=yes + orcli_run_usage + else + orcli_run_command + fi + ;; + + "root") + root_command + ;; + + esac } initialize diff --git a/src/bashly.yml b/src/bashly.yml index dbe56ae..aaa23f9 100644 --- a/src/bashly.yml +++ b/src/bashly.yml @@ -116,6 +116,9 @@ commands: - orcli info "duplicates" - orcli info 1234567890123 + - name: test + help: run functional tests on tmp OpenRefine workspace + - name: transform help: apply undo/redo JSON file(s) to an OpenRefine project args: diff --git a/src/lib/send_completions.sh b/src/lib/send_completions.sh index dbee111..5cadb15 100644 --- a/src/lib/send_completions.sh +++ b/src/lib/send_completions.sh @@ -66,12 +66,16 @@ send_completions() { echo $' while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "$(_orcli_completions_filter "--help -h")" -- "$cur" )' echo $' ;;' echo $'' + echo $' \'test\'*)' + echo $' while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "$(_orcli_completions_filter "--help -h")" -- "$cur" )' + echo $' ;;' + echo $'' echo $' \'run\'*)' echo $' while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "$(_orcli_completions_filter "--help --interactive --memory --port --quiet -h -q")" -- "$cur" )' echo $' ;;' echo $'' echo $' *)' - echo $' while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "$(_orcli_completions_filter "--help --version -h -v completions export import info list run transform")" -- "$cur" )' + echo $' while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "$(_orcli_completions_filter "--help --version -h -v completions export import info list run test transform")" -- "$cur" )' echo $' ;;' echo $'' echo $' esac' diff --git a/src/test_command.sh b/src/test_command.sh new file mode 100644 index 0000000..82f7e48 --- /dev/null +++ b/src/test_command.sh @@ -0,0 +1,73 @@ +# shellcheck shell=bash disable=SC2154 + +# locate orcli and OpenRefine +scriptpath=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")") +if [[ -x "${scriptpath}/refine" ]]; then + openrefine="${scriptpath}/refine" +else + error "OpenRefine's startup script (refine) not found!" "Did you put orcli in your OpenRefine app dir?" +fi + +# create tmp directory +OPENREFINE_TMPDIR="$(mktemp -d)" +trap '{ rm -rf "$OPENREFINE_TMPDIR"; }' 0 2 3 15 + +# download the test files if needed +if ! [[ -f "tests/help.sh" ]]; then + cd "$OPENREFINE_TMPDIR" + if ! curl -fs -L -o orcli.zip https://github.com/opencultureconsulting/orcli/archive/refs/heads/main.zip; then + error "downloading test files failed!" "Please download the tests dir manually from GitHub." + fi + unzip -q -j orcli.zip "*/tests/*.sh" -d "tests/" + unzip -q -j orcli.zip "*/tests/data/*" -d "tests/data/" +fi + +# check if OpenRefine is already running +if curl -fs "${OPENREFINE_URL}" &>/dev/null; then + error "OpenRefine is already running on port 3333." "Please stop the other process." +fi + +# start OpenRefine with tmp workspace +$openrefine -d "$OPENREFINE_TMPDIR" -x refine.headless=true -v warn &>"$OPENREFINE_TMPDIR/openrefine.log" & +OPENREFINE_PID="$!" + +# update trap to kill OpenRefine on error or exit +trap '{ rm -rf "$OPENREFINE_TMPDIR"; kill -9 "$OPENREFINE_PID"; }' 0 2 3 15 + +# wait until OpenRefine is running (timeout 20s) +if ! curl -fs --retry 20 --retry-connrefused --retry-delay 1 "${OPENREFINE_URL}/command/core/get-version" &>/dev/null; then + error "starting OpenRefine server failed!" +else + log "started OpenRefine with tmp workspace ${OPENREFINE_TMPDIR}" +fi + +# execute tests in subshell +export OPENREFINE_TMPDIR OPENREFINE_URL OPENREFINE_PID +cd "tests" +files=(*.sh) +results=() +for i in "${!files[@]}"; do + set +e # do not exit on failed tests + bash -e <( + if ! command -v orcli &>/dev/null; then + echo "shopt -s expand_aliases" + echo "alias orcli=${scriptpath}/orcli" + fi + awk 1 "${files[$i]}" + ) &>"$OPENREFINE_TMPDIR/test.log" + results+=(${?}) + set -e + if [[ "${results[$i]}" =~ [1-9] ]]; then + cat "$OPENREFINE_TMPDIR/test.log" + log "FAILED ${files[$i]} with exit code ${results[$i]}!" + else + log "PASSED ${files[$i]}" + fi +done + +# print overall result +if [[ "${results[*]}" =~ [1-9] ]]; then + error "failed tests!" +else + log "all tests passed!" +fi diff --git a/tests/completions.sh b/tests/completions.sh new file mode 100644 index 0000000..95010fd --- /dev/null +++ b/tests/completions.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +t="completions" + +# create tmp directory +tmpdir="$(mktemp -d)" +trap '{ rm -rf "${tmpdir}"; }' 0 2 3 15 + +# assertion +cat << "DATA" > "${tmpdir}/${t}.assert" +# orcli completion -*- shell-script -*- +DATA + +# action +cd "${tmpdir}" || exit 1 +orcli completions | head -n1 > "${t}.output" + +# test +diff -u "${t}.assert" "${t}.output" diff --git a/tests/data/duplicates-transformed.tsv b/tests/data/duplicates-transformed.tsv new file mode 100644 index 0000000..88ae21b --- /dev/null +++ b/tests/data/duplicates-transformed.tsv @@ -0,0 +1,7 @@ +email count name state gender purchase +arthur.duff@example4.com 2 Arthur Duff OR M Dining table +ben.morisson@example6.org 1 Ben Morisson FL M Amplifier +ben.tyler@example3.org 1 Ben Tyler NV M Flashlight +danny.baron@example1.com 3 Danny Baron CA M TV +jean.griffith@example5.org 1 Jean Griffith WA F Power drill +melanie.white@example2.edu 2 Melanie White NC F iPhone diff --git a/tests/data/duplicates.csv b/tests/data/duplicates.csv new file mode 100644 index 0000000..8fcb581 --- /dev/null +++ b/tests/data/duplicates.csv @@ -0,0 +1,11 @@ +email,name,state,gender,purchase +danny.baron@example1.com,Danny Baron,CA,M,TV +melanie.white@example2.edu,Melanie White,NC,F,iPhone +danny.baron@example1.com,D. Baron,CA,M,Winter jacket +ben.tyler@example3.org,Ben Tyler,NV,M,Flashlight +arthur.duff@example4.com,Arthur Duff,OR,M,Dining table +danny.baron@example1.com,Daniel Baron,CA,M,Bike +jean.griffith@example5.org,Jean Griffith,WA,F,Power drill +melanie.white@example2.edu,Melanie White,NC,F,iPad +ben.morisson@example6.org,Ben Morisson,FL,M,Amplifier +arthur.duff@example4.com,Arthur Duff,OR,M,Night table \ No newline at end of file diff --git a/tests/data/duplicates.tsv b/tests/data/duplicates.tsv new file mode 100644 index 0000000..13ff40f --- /dev/null +++ b/tests/data/duplicates.tsv @@ -0,0 +1,11 @@ +email name state gender purchase +danny.baron@example1.com Danny Baron CA M TV +melanie.white@example2.edu Melanie White NC F iPhone +danny.baron@example1.com D. Baron CA M Winter jacket +ben.tyler@example3.org Ben Tyler NV M Flashlight +arthur.duff@example4.com Arthur Duff OR M Dining table +danny.baron@example1.com Daniel Baron CA M Bike +jean.griffith@example5.org Jean Griffith WA F Power drill +melanie.white@example2.edu Melanie White NC F iPad +ben.morisson@example6.org Ben Morisson FL M Amplifier +arthur.duff@example4.com Arthur Duff OR M Night table diff --git a/tests/data/example.csv b/tests/data/example.csv new file mode 100644 index 0000000..8be467b --- /dev/null +++ b/tests/data/example.csv @@ -0,0 +1,4 @@ +a,b,c +1,2,3 +0,0,0 +$,\,' diff --git a/tests/data/example.tsv b/tests/data/example.tsv new file mode 100644 index 0000000..8c6d4cb --- /dev/null +++ b/tests/data/example.tsv @@ -0,0 +1,4 @@ +a b c +1 2 3 +0 0 0 +$ \ ' diff --git a/tests/export-tsv.sh b/tests/export-tsv.sh new file mode 100644 index 0000000..f831c4f --- /dev/null +++ b/tests/export-tsv.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +t="export-tsv" + +# create tmp directory +tmpdir="$(mktemp -d)" +trap '{ rm -rf "${tmpdir}"; }' 0 2 3 15 + +# input +cp data/example.csv "${tmpdir}/${t}.csv" + +# assertion +cp data/example.tsv "${tmpdir}/${t}.assert" + +# action +cd "${tmpdir}" || exit 1 +orcli import csv "${t}.csv" +orcli export tsv "${t} csv" --output "${t}.output" + +# test +diff -u "${t}.assert" "${t}.output" diff --git a/tests/help.sh b/tests/help.sh new file mode 100644 index 0000000..aa8ab87 --- /dev/null +++ b/tests/help.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +t="help" + +# create tmp directory +tmpdir="$(mktemp -d)" +trap '{ rm -rf "${tmpdir}"; }' 0 2 3 15 + +# assertion +cat << "DATA" > "${tmpdir}/${t}.assert" +orcli - OpenRefine command-line interface written in Bash +DATA + +# action +cd "${tmpdir}" || exit 1 +orcli --help | head -n1 > "${t}.output" + +# test +diff -u "${t}.assert" "${t}.output" diff --git a/tests/import-csv.sh b/tests/import-csv.sh new file mode 100644 index 0000000..4918d59 --- /dev/null +++ b/tests/import-csv.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +t="import-csv" + +# create tmp directory +tmpdir="$(mktemp -d)" +trap '{ rm -rf "${tmpdir}"; }' 0 2 3 15 + +# input +cp data/example.csv "${tmpdir}/${t}.csv" + +# assertion +cp data/example.tsv "${tmpdir}/${t}.assert" + +# action +cd "${tmpdir}" || exit 1 +orcli import csv "${t}.csv" +orcli export tsv "${t} csv" > "${t}.output" + +# test +diff -u "${t}.assert" "${t}.output" diff --git a/tests/info.sh b/tests/info.sh new file mode 100644 index 0000000..b6c89ae --- /dev/null +++ b/tests/info.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +t="info" + +# create tmp directory +tmpdir="$(mktemp -d)" +trap '{ rm -rf "${tmpdir}"; }' 0 2 3 15 + +# input +cp data/example.csv "${tmpdir}/${t}.csv" + +# action +cd "${tmpdir}" || exit 1 +orcli import csv "${t}.csv" +orcli info "${t} csv" + +# test +# grep "${t}" "${t}.output" diff --git a/tests/list.sh b/tests/list.sh new file mode 100644 index 0000000..cb28e0d --- /dev/null +++ b/tests/list.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +t="list" + +# create tmp directory +tmpdir="$(mktemp -d)" +trap '{ rm -rf "${tmpdir}"; }' 0 2 3 15 + +# input +cp data/example.csv "${tmpdir}/${t}.csv" + +# action +cd "${tmpdir}" || exit 1 +orcli import csv "${t}.csv" +orcli list > "${t}.output" + +# test +grep "${t}" "${t}.output" diff --git a/tests/run.sh b/tests/run.sh new file mode 100644 index 0000000..4420b8d --- /dev/null +++ b/tests/run.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +t="run" + +# create tmp directory +tmpdir="$(mktemp -d)" +trap '{ rm -rf "${tmpdir}"; }' 0 2 3 15 + +# assertion +cp data/duplicates-transformed.tsv "${tmpdir}/${t}.assert" + +# action +cd "${tmpdir}" || exit 1 +orcli run --memory "2000M" --port "3334" << EOF +orcli import csv "https://git.io/fj5hF" --projectName "duplicates" +orcli transform "duplicates" "https://git.io/fj5ju" +orcli export tsv "duplicates" --output "${t}.output" +EOF + +# test +diff -u "${t}.assert" "${t}.output" \ No newline at end of file diff --git a/tests/transform.sh b/tests/transform.sh new file mode 100644 index 0000000..a6ee492 --- /dev/null +++ b/tests/transform.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +t="transform" + +# create tmp directory +tmpdir="$(mktemp -d)" +trap '{ rm -rf "${tmpdir}"; }' 0 2 3 15 + +# assertion +cp data/duplicates-transformed.tsv "${tmpdir}/${t}.assert" + +# action +cd "${tmpdir}" || exit 1 +orcli import csv "https://git.io/fj5hF" --projectName "duplicates" +orcli transform "duplicates" "https://git.io/fj5ju" +orcli export tsv "duplicates" --output "${t}.output" + +# test +diff -u "${t}.assert" "${t}.output" \ No newline at end of file