lint/test/lib/linterCommandsTest.sh
2024-10-24 15:39:40 +02:00

407 lines
14 KiB
Bash
Executable file

#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
# shellcheck source=/dev/null
source "test/testUtils.sh"
# linterCommands.sh needs these
# shellcheck source=/dev/null
source "lib/globals/languages.sh"
# shellcheck source=/dev/null
source "lib/globals/linterRules.sh"
# shellcheck source=/dev/null
source "lib/functions/linterRules.sh"
# shellcheck source=/dev/null
source "lib/functions/validation.sh"
# Initialize the environment
# shellcheck disable=SC2034
BASH_EXEC_IGNORE_LIBRARIES="false"
# shellcheck disable=SC2034
GITHUB_WORKSPACE="$(pwd)"
# shellcheck disable=SC2034
IGNORE_GITIGNORED_FILES="false"
# shellcheck disable=SC2034
TYPESCRIPT_STANDARD_TSCONFIG_FILE=".github/linters/tsconfig.json"
# shellcheck disable=SC2034
YAML_ERROR_ON_WARNING="false"
for LANGUAGE in "${LANGUAGE_ARRAY_FOR_LINTER_RULES[@]}"; do
GetLinterRules "${LANGUAGE}" "TEMPLATES"
done
ValidateValidationVariables
# Now we can load linter command options because they have
# dependencies on linter rules
# shellcheck source=/dev/null
source /action/lib/globals/linterCommandsOptions.sh
# The slim image might not have this variable defined
if [[ ! -v ARM_TTK_PSD1 ]]; then
ARM_TTK_PSD1="/usr/lib/microsoft/arm-ttk/arm-ttk.psd1"
fi
# Source the file so we can load commands to compare them without redefining
# each command. We're not interested in the actual values of those commands, but
# only in how we eventually modify them.
# shellcheck source=/dev/null
source "lib/functions/linterCommands.sh"
# Initialize the variables we're going to use to verify tests before running tests
# because some tests modify LINTER_COMMANDS_xxx variables
BASE_LINTER_COMMANDS_ARRAY_ANSIBLE=("${LINTER_COMMANDS_ARRAY_ANSIBLE[@]}")
BASE_LINTER_COMMANDS_ARRAY_GITHUB_ACTIONS=("${LINTER_COMMANDS_ARRAY_GITHUB_ACTIONS[@]}")
BASE_LINTER_COMMANDS_ARRAY_GIT_COMMITLINT=("${LINTER_COMMANDS_ARRAY_GIT_COMMITLINT[@]}")
BASE_LINTER_COMMANDS_ARRAY_GITLEAKS=("${LINTER_COMMANDS_ARRAY_GITLEAKS[@]}")
BASE_LINTER_COMMANDS_ARRAY_GO_MODULES=("${LINTER_COMMANDS_ARRAY_GO_MODULES[@]}")
BASE_LINTER_COMMANDS_ARRAY_JSCPD=("${LINTER_COMMANDS_ARRAY_JSCPD[@]}")
BASE_LINTER_COMMANDS_ARRAY_KUBERNETES_KUBECONFORM=("${LINTER_COMMANDS_ARRAY_KUBERNETES_KUBECONFORM[@]}")
BASE_LINTER_COMMANDS_ARRAY_PERL=("${LINTER_COMMANDS_ARRAY_PERL[@]}")
BASE_LINTER_COMMANDS_ARRAY_RUST_CLIPPY=("${LINTER_COMMANDS_ARRAY_RUST_CLIPPY[@]}")
function LinterCommandPresenceTest() {
local FUNCTION_NAME
FUNCTION_NAME="${FUNCNAME[0]}"
info "${FUNCTION_NAME} start"
for LANGUAGE in "${LANGUAGE_ARRAY[@]}"; do
local LINTER_COMMAND_ARRAY_VARIABLE_NAME="LINTER_COMMANDS_ARRAY_${LANGUAGE}"
debug "Check if ${LINTER_COMMAND_ARRAY_VARIABLE_NAME} has at least one element"
local -n LINTER_COMMAND_ARRAY="${LINTER_COMMAND_ARRAY_VARIABLE_NAME}"
if [ ${#LINTER_COMMAND_ARRAY[@]} -eq 0 ]; then
fatal "LINTER_COMMAND_ARRAY for ${LANGUAGE} is empty."
else
debug "LINTER_COMMAND_ARRAY for ${LANGUAGE} has ${#LINTER_COMMAND_ARRAY[@]} elements: ${LINTER_COMMAND_ARRAY[*]}"
fi
unset -n LINTER_COMMAND_ARRAY
done
notice "${FUNCTION_NAME} PASS"
}
function IgnoreGitIgnoredFilesJscpdCommandTest() {
local FUNCTION_NAME
FUNCTION_NAME="${FUNCNAME[0]}"
info "${FUNCTION_NAME} start"
# shellcheck disable=SC2034
IGNORE_GITIGNORED_FILES="true"
# Source the file again so it accounts for modifications
# shellcheck source=/dev/null
source "lib/functions/linterCommands.sh"
EXPECTED_COMMAND=("${BASE_LINTER_COMMANDS_ARRAY_JSCPD[@]}" "${JSCPD_GITIGNORE_OPTION}")
if ! AssertArraysElementsContentMatch "LINTER_COMMANDS_ARRAY_JSCPD" "EXPECTED_COMMAND"; then
fatal "${FUNCTION_NAME} test failed"
fi
notice "${FUNCTION_NAME} PASS"
}
function JscpdCommandTest() {
local FUNCTION_NAME
FUNCTION_NAME="${FUNCNAME[0]}"
info "${FUNCTION_NAME} start"
# shellcheck disable=SC2034
IGNORE_GITIGNORED_FILES="false"
# Source the file again so it accounts for modifications
# shellcheck source=/dev/null
source "lib/functions/linterCommands.sh"
# shellcheck disable=SC2034
EXPECTED_COMMAND=("${BASE_LINTER_COMMANDS_ARRAY_JSCPD[@]}")
if ! AssertArraysElementsContentMatch "LINTER_COMMANDS_ARRAY_JSCPD" "EXPECTED_COMMAND"; then
fatal "${FUNCTION_NAME} test failed"
fi
notice "${FUNCTION_NAME} PASS"
}
EnableCommitlintStrictModeCommandTest() {
local FUNCTION_NAME
FUNCTION_NAME="${FUNCNAME[0]}"
info "${FUNCTION_NAME} start"
# shellcheck disable=SC2034
ENABLE_COMMITLINT_STRICT_MODE="true"
# Source the file again so it accounts for modifications
# shellcheck source=/dev/null
source "lib/functions/linterCommands.sh"
EXPECTED_COMMAND=("${BASE_LINTER_COMMANDS_ARRAY_GIT_COMMITLINT[@]}" "${COMMITLINT_STRICT_MODE_OPTIONS[@]}")
if ! AssertArraysElementsContentMatch "LINTER_COMMANDS_ARRAY_GIT_COMMITLINT" "EXPECTED_COMMAND"; then
fatal "${FUNCTION_NAME} test failed"
fi
notice "${FUNCTION_NAME} PASS"
}
function GitleaksCommandTest() {
local FUNCTION_NAME
FUNCTION_NAME="${FUNCNAME[0]}"
info "${FUNCTION_NAME} start"
# shellcheck disable=SC2034
EXPECTED_COMMAND=("${BASE_LINTER_COMMANDS_ARRAY_GITLEAKS[@]}")
if [[ "${EXPECTED_GITLEAKS_LOG_LEVEL:-}" ]]; then
# The gitleaks command ends with an option to specify the path
# to the file to check, so we need to append the log option before that.
local GITLEAKS_FILE_PATH_OPTION="${EXPECTED_COMMAND[-1]}"
# Remove the file path option so we can append the log option
unset 'EXPECTED_COMMAND[-1]'
# shellcheck disable=SC2034
GITLEAKS_LOG_LEVEL="${EXPECTED_GITLEAKS_LOG_LEVEL}"
EXPECTED_COMMAND+=("${GITLEAKS_LOG_LEVEL_OPTIONS[@]}" "${EXPECTED_GITLEAKS_LOG_LEVEL}")
# Add the file path option back
EXPECTED_COMMAND+=("${GITLEAKS_FILE_PATH_OPTION}")
fi
# Source the file again so it accounts for modifications
# shellcheck source=/dev/null
source "lib/functions/linterCommands.sh"
if [[ ! -v GITLEAKS_LOG_LEVEL_OPTIONS ]]; then
fatal "GITLEAKS_LOG_LEVEL_OPTIONS is not defined"
fi
if [[ "${#GITLEAKS_LOG_LEVEL_OPTIONS[@]}" -eq 0 ]]; then
fatal "GITLEAKS_LOG_LEVEL_OPTIONS is empty"
fi
if ! AssertArraysElementsContentMatch "LINTER_COMMANDS_ARRAY_GITLEAKS" "EXPECTED_COMMAND"; then
fatal "${FUNCTION_NAME} test failed"
fi
notice "${FUNCTION_NAME} PASS"
}
function GitleaksCommandCustomLogLevelTest() {
local FUNCTION_NAME
FUNCTION_NAME="${FUNCNAME[0]}"
info "${FUNCTION_NAME} start"
EXPECTED_GITLEAKS_LOG_LEVEL="debug"
GitleaksCommandTest
notice "${FUNCTION_NAME} PASS"
}
function InitInputConsumeCommandsTest() {
local FUNCTION_NAME
FUNCTION_NAME="${FUNCNAME[0]}"
info "${FUNCTION_NAME} start"
# shellcheck disable=SC2034
EXPECTED_LINTER_COMMANDS_ARRAY_ANSIBLE=("${BASE_LINTER_COMMANDS_ARRAY_ANSIBLE[@]}" "${INPUT_CONSUME_COMMAND[@]}")
# shellcheck disable=SC2034
EXPECTED_LINTER_COMMANDS_ARRAY_GO_MODULES=("${BASE_LINTER_COMMANDS_ARRAY_GO_MODULES[@]}" "${INPUT_CONSUME_COMMAND[@]}")
# Add some custom options to the Rust command to ensure that they are added before the "input consume" command
# shellcheck disable=SC2034
local RUST_CLIPPY_COMMAND_OPTIONS_ARRAY=("--verbose --help")
RUST_CLIPPY_COMMAND_OPTIONS="${RUST_CLIPPY_COMMAND_OPTIONS_ARRAY[*]}"
# Source the file again so it accounts for modifications
# shellcheck source=/dev/null
source "lib/functions/linterCommands.sh"
# shellcheck disable=SC2034
EXPECTED_LINTER_COMMANDS_ARRAY_RUST_CLIPPY=("${BASE_LINTER_COMMANDS_ARRAY_RUST_CLIPPY[@]}" "${RUST_CLIPPY_COMMAND_OPTIONS_ARRAY[@]}" "${INPUT_CONSUME_COMMAND[@]}")
if ! InitInputConsumeCommands; then
fatal "Error while initializing GNU parallel input consume commands"
fi
if ! AssertArraysElementsContentMatch "LINTER_COMMANDS_ARRAY_ANSIBLE" "EXPECTED_LINTER_COMMANDS_ARRAY_ANSIBLE"; then
fatal "${FUNCTION_NAME} test failed"
fi
if ! AssertArraysElementsContentMatch "LINTER_COMMANDS_ARRAY_GO_MODULES" "EXPECTED_LINTER_COMMANDS_ARRAY_GO_MODULES"; then
fatal "${FUNCTION_NAME} test failed"
fi
if ! AssertArraysElementsContentMatch "LINTER_COMMANDS_ARRAY_RUST_CLIPPY" "EXPECTED_LINTER_COMMANDS_ARRAY_RUST_CLIPPY"; then
fatal "${FUNCTION_NAME} test failed"
fi
notice "${FUNCTION_NAME} PASS"
}
function InitFixModeOptionsAndCommandsTest() {
local FUNCTION_NAME
FUNCTION_NAME="${FUNCNAME[0]}"
info "${FUNCTION_NAME} start"
LANGUAGE_ARRAY=("A" "B" "C")
# Test a command that has only fix mode options to add
# shellcheck disable=SC2034
A_FIX_MODE_OPTIONS=(--fixA)
# Test a command that has only check only mode options to add
# shellcheck disable=SC2034
B_CHECK_ONLY_MODE_TEST=(--checkB)
# Test a command that has both fix mode and check only mode options to add
# shellcheck disable=SC2034
C_CHECK_ONLY_MODE_TEST=(--checkC)
# shellcheck disable=SC2034
C_FIX_MODE_OPTIONS=(--fixC)
for LANGUAGE in "${LANGUAGE_ARRAY[@]}"; do
local -n FIX_LANGUAGE_VARIABLE_NAME="FIX_${LANGUAGE}"
# shellcheck disable=SC2034
FIX_LANGUAGE_VARIABLE_NAME="true"
local -n LINTER_COMMANDS_ARRAY="LINTER_COMMANDS_ARRAY_${LANGUAGE}"
# shellcheck disable=SC2034
LINTER_COMMANDS_ARRAY=("LINTER_COMMANDS_ARRAY_FOR_LINTER_${LANGUAGE}_FIX_MODE_TEST")
EXPECTED_LINTER_COMMANDS_ARRAY_FIX_MODE=("${LINTER_COMMANDS_ARRAY[@]}")
local FIX_MODE_OPTIONS_VARIABLE_NAME="${LANGUAGE}_FIX_MODE_OPTIONS"
if [[ -v "${FIX_MODE_OPTIONS_VARIABLE_NAME}" ]]; then
local -n FIX_MODE_OPTIONS="${FIX_MODE_OPTIONS_VARIABLE_NAME}"
# shellcheck disable=SC2034
EXPECTED_LINTER_COMMANDS_ARRAY_FIX_MODE+=("${FIX_MODE_OPTIONS[@]}")
unset -n FIX_MODE_OPTIONS
fi
if ! InitFixModeOptionsAndCommands "${LANGUAGE}"; then
fatal "InitFixModeOptionsAndCommands for ${LANGUAGE} should have passed validation"
fi
if ! AssertArraysElementsContentMatch "LINTER_COMMANDS_ARRAY" "EXPECTED_LINTER_COMMANDS_ARRAY_FIX_MODE"; then
fatal "${FUNCTION_NAME} ${!FIX_LANGUAGE_VARIABLE_NAME}: ${FIX_LANGUAGE_VARIABLE_NAME} test failed"
fi
# shellcheck disable=SC2034
FIX_LANGUAGE_VARIABLE_NAME="false"
LINTER_COMMANDS_ARRAY=("LINTER_COMMANDS_ARRAY_FOR_LINTER_${LANGUAGE}_CHECK_ONLY_MODE_TEST")
# shellcheck disable=SC2034
EXPECTED_LINTER_COMMANDS_ARRAY_CHECK_ONLY_MODE=("${LINTER_COMMANDS_ARRAY[@]}")
local CHECK_ONLY_MODE_OPTIONS_VARIABLE_NAME="${LANGUAGE}_CHECK_ONLY_MODE_OPTIONS"
if [[ -v "${CHECK_ONLY_MODE_OPTIONS_VARIABLE_NAME}" ]]; then
local -n CHECK_ONLY_MODE_OPTIONS="${CHECK_ONLY_MODE_OPTIONS_VARIABLE_NAME}"
# shellcheck disable=SC2034
EXPECTED_LINTER_COMMANDS_ARRAY_CHECK_ONLY_MODE+=("${CHECK_ONLY_MODE_OPTIONS[@]}")
unset -n CHECK_ONLY_MODE_OPTIONS
fi
if ! InitFixModeOptionsAndCommands "${LANGUAGE}"; then
fatal "InitFixModeOptionsAndCommands for ${LANGUAGE} should have passed validation"
fi
if ! AssertArraysElementsContentMatch "LINTER_COMMANDS_ARRAY" "EXPECTED_LINTER_COMMANDS_ARRAY_CHECK_ONLY_MODE"; then
fatal "${FUNCTION_NAME} ${!FIX_LANGUAGE_VARIABLE_NAME}: ${FIX_LANGUAGE_VARIABLE_NAME} test failed"
fi
unset -n FIX_LANGUAGE_VARIABLE_NAME
unset -n LINTER_COMMANDS_ARRAY
done
notice "${FUNCTION_NAME} PASS"
}
function InitPowerShellCommandTest() {
local FUNCTION_NAME
FUNCTION_NAME="${FUNCNAME[0]}"
info "${FUNCTION_NAME} start"
# shellcheck disable=SC2034
EXPECTED_LINTER_COMMANDS_ARRAY_POWERSHELL=(pwsh -NoProfile -NoLogo -Command "\"${LINTER_COMMANDS_ARRAY_POWERSHELL[*]}; if (\\\${Error}.Count) { exit 1 }\"")
InitPowerShellCommand
if ! AssertArraysElementsContentMatch "LINTER_COMMANDS_ARRAY_POWERSHELL" "EXPECTED_LINTER_COMMANDS_ARRAY_POWERSHELL"; then
fatal "${FUNCTION_NAME} test failed"
fi
notice "${FUNCTION_NAME} PASS"
}
CommandOptionsTest() {
local FUNCTION_NAME
FUNCTION_NAME="${FUNCNAME[0]}"
info "${FUNCTION_NAME} start"
# Add custom arguments to linter commands that support them.
# If possible, use command option that can run without modifying the filesystem,
# and that don't need any input.
# shellcheck disable=SC2034
GITHUB_ACTIONS_COMMAND_ARGS="-color -debug -verbose -version"
# shellcheck disable=SC2034
JAVA_COMMAND_ARGS="-Dproperty1=value1 -version"
# shellcheck disable=SC2034
KUBERNETES_KUBECONFORM_OPTIONS="-debug -verbose -v"
# shellcheck disable=SC2034
PERL_PERLCRITIC_OPTIONS="--gentle --count test/linters/perl/perl_good_1.pl"
# shellcheck disable=SC2034
RUST_CLIPPY_COMMAND_OPTIONS="--verbose --help"
# Source the file again so it accounts for modifications
# shellcheck source=/dev/null
source "lib/functions/linterCommands.sh"
# Try running the commands
"${LINTER_COMMANDS_ARRAY_GITHUB_ACTIONS[@]}"
"${LINTER_COMMANDS_ARRAY_JAVA[@]}"
"${LINTER_COMMANDS_ARRAY_KUBERNETES_KUBECONFORM[@]}"
"${LINTER_COMMANDS_ARRAY_PERL[@]}"
# Rust Clippy is only available in the standard image, so we can't run it when
# testing the slim image
if IsStandardImage; then
"${LINTER_COMMANDS_ARRAY_RUST_CLIPPY[@]}"
fi
notice "${FUNCTION_NAME} PASS"
}
AddOptionsToCommandTest() {
local FUNCTION_NAME
FUNCTION_NAME="${FUNCNAME[0]}"
info "${FUNCTION_NAME} start"
local TEST_LINTER_COMMANDS_ARRAY_GITHUB_ACTIONS=("${BASE_LINTER_COMMANDS_ARRAY_GITHUB_ACTIONS[@]}")
AddOptionsToCommand "TEST_LINTER_COMMANDS_ARRAY_GITHUB_ACTIONS" "-color -debug -verbose -version"
"${TEST_LINTER_COMMANDS_ARRAY_GITHUB_ACTIONS[@]}"
local TEST_LINTER_COMMANDS_ARRAY_KUBERNETES_KUBECONFORM=("${BASE_LINTER_COMMANDS_ARRAY_KUBERNETES_KUBECONFORM[@]}")
AddOptionsToCommand "TEST_LINTER_COMMANDS_ARRAY_KUBERNETES_KUBECONFORM" "-debug -verbose -v"
"${TEST_LINTER_COMMANDS_ARRAY_KUBERNETES_KUBECONFORM[@]}"
local TEST_LINTER_COMMANDS_ARRAY_PERL=("${BASE_LINTER_COMMANDS_ARRAY_PERL[@]}")
AddOptionsToCommand "TEST_LINTER_COMMANDS_ARRAY_PERL" "--gentle --count test/linters/perl/perl_good_1.pl"
"${TEST_LINTER_COMMANDS_ARRAY_PERL[@]}"
# Rust Clippy is only available in the standard image, so we can't run it when
# testing the slim image
if IsStandardImage; then
local TEST_LINTER_COMMANDS_ARRAY_RUST_CLIPPY=("${BASE_LINTER_COMMANDS_ARRAY_RUST_CLIPPY[@]}")
AddOptionsToCommand "TEST_LINTER_COMMANDS_ARRAY_RUST_CLIPPY" "--verbose --help"
"${TEST_LINTER_COMMANDS_ARRAY_RUST_CLIPPY[@]}"
fi
notice "${FUNCTION_NAME} PASS"
}
LinterCommandPresenceTest
IgnoreGitIgnoredFilesJscpdCommandTest
JscpdCommandTest
EnableCommitlintStrictModeCommandTest
GitleaksCommandTest
GitleaksCommandCustomLogLevelTest
InitInputConsumeCommandsTest
InitFixModeOptionsAndCommandsTest
InitPowerShellCommandTest
CommandOptionsTest
AddOptionsToCommandTest