#!/bin/bash

# This test clones a repo that has intentional violations in every single `js` file.
#
# It expects 3 arguments:
# `$1`: A command to run to generate a SARIF file that will be evaluated during the test.
# `$2`: A path to a folder that will be written to during the test. This folder's
#       contents will be cleared before and after running.
# `$3`: A path to the SARIF file generated by `$1`.
#
# It tests that:
# * Each `js` file has exactly one detection.
# * The SARIF `artifacts` array contains exactly one object for each file.
# * A file name that starts with "should_be_t_file" should have its SARIF artifact tagged as a test file
#   (Note that the analyzer itself does not use this specific name as a detection -- this naming schema
#   is used as a way to declaratively and robustly define the test corpus).
# * A file name not starting with that string should not have its SARIF artifact tagged as a test file.

# The SARIF property bag tag that a test file artifact should have.
TEST_FILE_TAG="DATADOG_ARTIFACT_IS_TEST_FILE"

# A string used to declaratively specify which files should trigger a test file classification.
TEST_FILE_INDICATOR="should_be_t_file"

if [ "$#" -lt 3 ]; then
    echo "Incorrect number of inputs. See documentation"
    exit 1
fi

ANALYSIS_CMD=$1
REPO_DIR=$2
RESULTS_FILE=$3

rm -rf "${REPO_DIR}"
git clone --depth 1 https://github.com/muh-nee/classification-tests.git "${REPO_DIR}"

# Gather paths for all "js" files.
js_files=()
while IFS= read -r -d $'\0' file; do
    relative_path="${file#$REPO_DIR/}"
    js_files+=("$relative_path")
done < <(find "${REPO_DIR}" -type f -name "*.js" -print0)
IFS=$'\n' sorted_js_files=($(sort <<<"${js_files[*]}"))
unset IFS

# This is _technically_ a command injection, however, this is only used for tests, and
# all input is expected to be trusted.
# no-dd-sa
eval "${ANALYSIS_CMD}"
# (We ignore a non-zero exit code because it may be intentional

violation_paths=($(jq -r '.runs[0].results[] | .locations[0].physicalLocation.artifactLocation.uri' "${RESULTS_FILE}"))
artifact_paths=($(jq -r '.runs[0].artifacts[] | .location.uri' "${RESULTS_FILE}"))
IFS=$'\n' sorted_violation_paths=($(sort <<<"${violation_paths[*]}"))
IFS=$'\n' sorted_artifact_paths=($(sort <<<"${artifact_paths[*]}"))
unset IFS

# Test invariant: each JavaScript file will have exactly 1 violation.
# The most straightforward way to do this is compare a (sorted) list of paths from the violations and artifacts with
# the (sorted) list of paths we discovered earlier with the `find` command.
for i in "${!sorted_js_files[@]}"; do
    if [[ "${sorted_js_files[i]}" != "${sorted_violation_paths[i]}" ]]; then
        echo "Test invariant broken: each js file must have exactly 1 violation. missing one for \"${sorted_js_files[i]}\""
        exit 1
    fi
    if [[ "${sorted_js_files[i]}" != "${sorted_artifact_paths[i]}" ]]; then
        echo "Test invariant broken: each js file should be a SARIF artifact. missing one for \"${sorted_js_files[i]}\""
        exit 1
    fi
done

file_count=${#sorted_js_files[@]}
test_file_count=0
for ((i = 0; i < file_count; i++)); do
    uri=$(jq -r ".runs[0].artifacts[${i}].location.uri" ${RESULTS_FILE})
    filename=$(basename "${uri}")

    has_tag=false
    if jq -e --arg expected_tag "${TEST_FILE_TAG}" ".runs[0].artifacts[${i}].properties.tags | index(\$expected_tag) != null" ${RESULTS_FILE} >/dev/null; then
        has_tag=true
    fi

    # Test invariant: files starting with "should_be_t_file" are expected to have a "test file" classification.
    # Others should not.
    if [[ "${filename}" == ${TEST_FILE_INDICATOR}* ]]; then
        test_file_count=$((test_file_count + 1))
        if [[ "${has_tag}" == false ]]; then
            echo "${uri} should have \`${TEST_FILE_TAG}\` property tag"
            exit 1
        fi
    else
        if [[ "${has_tag}" == true ]]; then
            echo "${uri} should not have \`${TEST_FILE_TAG}\` property tag"
            exit 1
        fi
    fi
done

# Test invariant: there must be at least "should_be_t_file" file
if [[ "${test_file_count}" -eq 0 ]]; then
    echo "repo should have at least one file starting with \"${TEST_FILE_INDICATOR}\""
    exit 1
fi

rm -rf "${REPO_DIR}/*"
# Success
exit 0
