Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[chore] update e2e tests to use BATS assertions #121

Merged
merged 13 commits into from
May 16, 2023
Merged
11 changes: 6 additions & 5 deletions .github/workflows/kind.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ jobs:
uses: actions/setup-go@v4
with:
go-version: "1.20"
- name: Setup BATS
uses: mig4/setup-bats@v1
- name: Build auto-instrumentation
run: |
IMG=otel-go-instrumentation:latest make docker-build
Expand Down Expand Up @@ -68,9 +70,8 @@ jobs:
kubectl wait --for=condition=Complete --timeout=60s job/sample-job
- name: copy telemetry trace output
run: |
kubectl cp -c filecp default/test-opentelemetry-collector-0:tmp/trace.json ./test/e2e/${{ matrix.library }}/traces.json.tmp
jq 'del(.resourceSpans[].scopeSpans[].spans[].endTimeUnixNano, .resourceSpans[].scopeSpans[].spans[].startTimeUnixNano) | .resourceSpans[].scopeSpans[].spans[].spanId|= (if . != "" then "xxxxx" else . end) | .resourceSpans[].scopeSpans[].spans[].traceId|= (if . != "" then "xxxxx" else . end) | .resourceSpans[].scopeSpans|=sort_by(.scope.name)' ./test/e2e/${{ matrix.library }}/traces.json.tmp | jq --sort-keys . > ./test/e2e/${{ matrix.library }}/traces.json
robbkidd marked this conversation as resolved.
Show resolved Hide resolved
rm ./test/e2e/${{ matrix.library }}/traces.json.tmp
- name: verify output
kubectl cp -c filecp default/test-opentelemetry-collector-0:tmp/trace.json ./test/e2e/${{ matrix.library }}/traces-orig.json
rm -f ./test/e2e/${{ matrix.library }}/traces.json
- name: verify output and redact to traces.json
run: |
make check-clean-work-tree
bats ./test/e2e/${{ matrix.library }}/verify.bats
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ fixtures/%:
kubectl wait --for=condition=Ready --timeout=60s pod/test-opentelemetry-collector-0
kubectl -n default create -f .github/workflows/e2e/k8s/sample-job.yml
kubectl wait --for=condition=Complete --timeout=60s job/sample-job
kubectl cp -c filecp default/test-opentelemetry-collector-0:tmp/trace.json ./test/e2e/$(LIBRARY)/traces.json.tmp
jq 'del(.resourceSpans[].scopeSpans[].spans[].endTimeUnixNano, .resourceSpans[].scopeSpans[].spans[].startTimeUnixNano) | .resourceSpans[].scopeSpans[].spans[].spanId|= (if . != "" then "xxxxx" else . end) | .resourceSpans[].scopeSpans[].spans[].traceId|= (if . != "" then "xxxxx" else . end) | .resourceSpans[].scopeSpans|=sort_by(.scope.name)' ./test/e2e/$(LIBRARY)/traces.json.tmp | jq --sort-keys . > ./test/e2e/$(LIBRARY)/traces.json
rm ./test/e2e/$(LIBRARY)/traces.json.tmp
kubectl cp -c filecp default/test-opentelemetry-collector-0:tmp/trace.json ./test/e2e/$(LIBRARY)/traces-orig.json
rm -f ./test/e2e/$(LIBRARY)/traces.json
bats ./test/e2e/$(LIBRARY)/verify.bats
MrAlias marked this conversation as resolved.
Show resolved Hide resolved
kind delete cluster

.PHONY: prerelease
Expand Down
40 changes: 40 additions & 0 deletions test/e2e/gin/verify.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env bats

load ../../test_helpers/utilities

LIBRARY_NAME="github.com/gin-gonic/gin"

@test "go-auto :: includes service.name in resource attributes" {
result=$(resource_attributes_received | jq "select(.key == \"service.name\").value.stringValue")
assert_equal "$result" '"sample-app"'
}

@test "${LIBRARY_NAME} :: emits a span name '{http.method} {http.target}' (per semconv)" {
result=$(span_names_for ${LIBRARY_NAME})
assert_equal "$result" '"GET /hello-gin"'
}

@test "${LIBRARY_NAME} :: includes http.method attribute" {
result=$(span_attributes_for ${LIBRARY_NAME} | jq "select(.key == \"http.method\").value.stringValue")
assert_equal "$result" '"GET"'
}

@test "${LIBRARY_NAME} :: includes http.target attribute" {
result=$(span_attributes_for ${LIBRARY_NAME} | jq "select(.key == \"http.target\").value.stringValue")
assert_equal "$result" '"/hello-gin"'
}

@test "${LIBRARY_NAME} :: trace ID present and valid in all spans" {
trace_id=$(spans_from_scope_named ${LIBRARY_NAME} | jq ".traceId")
assert_regex "$trace_id" ${MATCH_A_TRACE_ID}
}

@test "${LIBRARY_NAME} :: span ID present and valid in all spans" {
span_id=$(spans_from_scope_named ${LIBRARY_NAME} | jq ".spanId")
assert_regex "$span_id" ${MATCH_A_SPAN_ID}
}

@test "${LIBRARY_NAME} :: expected (redacted) trace output" {
redact_json
assert_equal "$(git --no-pager diff ${BATS_TEST_DIRNAME}/traces.json)" ""
}
40 changes: 40 additions & 0 deletions test/e2e/gorillamux/verify.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env bats

load ../../test_helpers/utilities

LIBRARY_NAME="github.com/gorilla/mux"

@test "go-auto :: includes service.name in resource attributes" {
result=$(resource_attributes_received | jq "select(.key == \"service.name\").value.stringValue")
assert_equal "$result" '"sample-app"'
}

@test "${LIBRARY_NAME} :: emits a span name '{http.method} {http.target}' (per semconv)" {
result=$(span_names_for ${LIBRARY_NAME})
assert_equal "$result" '"GET /users/foo"'
}

@test "${LIBRARY_NAME} :: includes http.method attribute" {
result=$(span_attributes_for ${LIBRARY_NAME} | jq "select(.key == \"http.method\").value.stringValue")
assert_equal "$result" '"GET"'
}

@test "${LIBRARY_NAME} :: includes http.target attribute" {
result=$(span_attributes_for ${LIBRARY_NAME} | jq "select(.key == \"http.target\").value.stringValue")
assert_equal "$result" '"/users/foo"'
}

@test "${LIBRARY_NAME} :: trace ID present and valid in all spans" {
trace_id=$(spans_from_scope_named ${LIBRARY_NAME} | jq ".traceId")
assert_regex "$trace_id" ${MATCH_A_TRACE_ID}
}

@test "${LIBRARY_NAME} :: span ID present and valid in all spans" {
span_id=$(spans_from_scope_named ${LIBRARY_NAME} | jq ".spanId")
assert_regex "$span_id" ${MATCH_A_SPAN_ID}
}

@test "${LIBRARY_NAME} :: expected (redacted) trace output" {
redact_json
assert_equal "$(git --no-pager diff ${BATS_TEST_DIRNAME}/traces.json)" ""
}
40 changes: 40 additions & 0 deletions test/e2e/nethttp/verify.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env bats

load ../../test_helpers/utilities

LIBRARY_NAME="net/http"

@test "go-auto :: includes service.name in resource attributes" {
result=$(resource_attributes_received | jq "select(.key == \"service.name\").value.stringValue")
assert_equal "$result" '"sample-app"'
}

@test "${LIBRARY_NAME} :: emits a span name '{http.method} {http.target}' (per semconv)" {
result=$(span_names_for ${LIBRARY_NAME})
assert_equal "$result" '"GET /hello"'
}

@test "${LIBRARY_NAME} :: includes http.method attribute" {
result=$(span_attributes_for ${LIBRARY_NAME} | jq "select(.key == \"http.method\").value.stringValue")
assert_equal "$result" '"GET"'
}

@test "${LIBRARY_NAME} :: includes http.target attribute" {
result=$(span_attributes_for ${LIBRARY_NAME} | jq "select(.key == \"http.target\").value.stringValue")
assert_equal "$result" '"/hello"'
}

@test "${LIBRARY_NAME} :: trace ID present and valid in all spans" {
trace_id=$(spans_from_scope_named ${LIBRARY_NAME} | jq ".traceId")
assert_regex "$trace_id" ${MATCH_A_TRACE_ID}
}

@test "${LIBRARY_NAME} :: span ID present and valid in all spans" {
span_id=$(spans_from_scope_named ${LIBRARY_NAME} | jq ".spanId")
assert_regex "$span_id" ${MATCH_A_SPAN_ID}
}

@test "${LIBRARY_NAME} :: expected (redacted) trace output" {
redact_json
assert_equal "$(git --no-pager diff ${BATS_TEST_DIRNAME}/traces.json)" ""
}
109 changes: 109 additions & 0 deletions test/test_helpers/utilities.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# DATA RETRIEVERS

# Returns a list of span names emitted by a given library/scope
# $1 - library/scope name
span_names_for() {
spans_from_scope_named $1 | jq '.name'
}

# Returns a list of attributes emitted by a given library/scope
span_attributes_for() {
# $1 - library/scope name

spans_from_scope_named $1 | \
jq ".attributes[]"
}

# Returns a list of all resource attributes
resource_attributes_received() {
spans_received | jq ".resource.attributes[]?"
}

# Returns an array of all spans emitted by a given library/scope
# $1 - library/scope name
spans_from_scope_named() {
spans_received | jq ".scopeSpans[] | select(.scope.name == \"$1\").spans[]"
}

# Returns an array of all spans received
spans_received() {
json_output | jq ".resourceSpans[]?"
}

# Returns the content of the log file produced by a collector
# and located in the same directory as the BATS test file
# loading this helper script.
json_output() {
cat "${BATS_TEST_DIRNAME}/traces-orig.json"
}

redact_json() {
json_output | \
jq --sort-keys '
del(
.resourceSpans[].scopeSpans[].spans[].startTimeUnixNano,
.resourceSpans[].scopeSpans[].spans[].endTimeUnixNano
)
| .resourceSpans[].scopeSpans[].spans[].traceId|= (if
. // "" | test("^[A-Fa-f0-9]{32}$") then "xxxxx" else (. + "<-INVALID")
end)
| .resourceSpans[].scopeSpans[].spans[].spanId|= (if
. // "" | test("^[A-Fa-f0-9]{16}$") then "xxxxx" else (. + "<-INVALID")
end)
| .resourceSpans[].scopeSpans|=sort_by(.scope.name)
' > ${BATS_TEST_DIRNAME}/traces.json
}

# ASSERTION HELPERS

# expect a 32-digit hexadecimal string (in quotes)
MATCH_A_TRACE_ID=^"\"[A-Fa-f0-9]{32}\"$"

# expect a 16-digit hexadecimal string (in quotes)
MATCH_A_SPAN_ID=^"\"[A-Fa-f0-9]{16}\"$"

# Fail and display details if the expected and actual values do not
# equal. Details include both values.
#
# Inspired by bats-assert * bats-support, but dramatically simplified
assert_equal() {
if [[ $1 != "$2" ]]; then
{
echo
echo "-- 💥 values are not equal 💥 --"
echo "expected : $2"
echo "actual : $1"
echo "--"
echo
} >&2 # output error to STDERR
return 1
fi
}

assert_regex() {
if ! [[ $1 =~ $2 ]]; then
{
echo
echo "-- 💥 value does not match regular expression 💥 --"
echo "value : $1"
echo "pattern : $2"
echo "--"
echo
} >&2 # output error to STDERR
return 1
fi
}

assert_not_empty() {
EMPTY=(\"\")
if [[ "$1" == "${EMPTY}" ]]; then
{
echo
echo "-- 💥 value is empty 💥 --"
echo "value : $1"
echo "--"
echo
} >&2 # output error to STDERR
return 1
fi
}