mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-02-03 17:50:41 +00:00
c73bc49cf0
Include local (my) and remote (their) SIDs in JSON responses for conversationlist.json and messagelist.json, so that the same JSON structures can be used in future for non-SID-specific queries. Refactor MeshMS message iterator, rename "sender" and "recipient" fields to "my" and "their", for consistency with the rest of the MeshMS source code, and because "sender" and "recipient" are properties that apply to a single message or single ply, not to a message thread (conversation).
1047 lines
37 KiB
Bash
Executable File
1047 lines
37 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Tests for Serval DNA HTTP RESTful interface
|
|
#
|
|
# Copyright 2013 Serval Project, Inc.
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
source "${0%/*}/../testframework.sh"
|
|
source "${0%/*}/../testdefs.sh"
|
|
source "${0%/*}/../testdefs_rhizome.sh"
|
|
|
|
shopt -s extglob
|
|
|
|
assertJq() {
|
|
local json="$1"
|
|
local jqscript="$2"
|
|
assert --message="$jqscript" --dump-on-fail="$json" [ "$(jq "$jqscript" "$json")" = true ]
|
|
}
|
|
|
|
assertJqGrep() {
|
|
local opts=()
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--) shift; break;;
|
|
--*) opts+=("$1"); shift;;
|
|
*) break;;
|
|
esac
|
|
done
|
|
[ $# -eq 3 ] || error "invalid arguments"
|
|
local json="$1"
|
|
local jqscript="$2"
|
|
local pattern="$3"
|
|
jq "$jqscript" "$json" >"$TFWTMP/jqgrep.tmp"
|
|
assertGrep "${opts[@]}" "$TFWTMP/jqgrep.tmp" "$pattern"
|
|
}
|
|
|
|
setup() {
|
|
CR='
|
|
'
|
|
VT=' '
|
|
setup_curl 7
|
|
setup_jq 1.3
|
|
setup_servald
|
|
set_instance +A
|
|
set_rhizome_config
|
|
executeOk_servald config \
|
|
set rhizome.api.restful.users.harry.password potter \
|
|
set rhizome.api.restful.users.ron.password weasley \
|
|
set rhizome.api.restful.users.hermione.password grainger
|
|
if [ -z "$IDENTITY_COUNT" ]; then
|
|
create_single_identity
|
|
else
|
|
create_identities $IDENTITY_COUNT
|
|
fi
|
|
start_servald_instances +A
|
|
wait_until rhizome_http_server_started +A
|
|
get_rhizome_server_port PORTA +A
|
|
}
|
|
|
|
finally() {
|
|
stop_all_servald_servers
|
|
}
|
|
|
|
teardown() {
|
|
kill_all_servald_processes
|
|
assert_no_servald_processes
|
|
report_all_servald_servers
|
|
}
|
|
|
|
set_rhizome_config() {
|
|
executeOk_servald config \
|
|
set debug.httpd on \
|
|
set debug.rhizome_httpd on \
|
|
set debug.rhizome_manifest on \
|
|
set debug.externalblobs on \
|
|
set debug.rhizome on \
|
|
set debug.verbose on \
|
|
set log.console.level debug
|
|
}
|
|
|
|
doc_AuthBasicMissing="HTTP RESTful missing Basic Authentication credentials"
|
|
test_AuthBasicMissing() {
|
|
executeOk curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output http.output \
|
|
--dump-header http.headers \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
|
assertStdoutIs '401'
|
|
assertGrep http.headers "^WWW-Authenticate: Basic realm=\"Serval Rhizome\"$CR\$"
|
|
assertJq http.output 'contains({"http_status_code": 401})'
|
|
assertJq http.output 'contains({"http_status_message": ""})'
|
|
}
|
|
teardown_AuthBasicMissing() {
|
|
tfw_cat http.headers http.output
|
|
teardown
|
|
}
|
|
|
|
doc_AuthBasicWrong="HTTP RESTful incorrect Basic Authentication credentials"
|
|
test_AuthBasicWrong() {
|
|
executeOk curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output http.output \
|
|
--dump-header http.headers \
|
|
--basic --user fred:nurks \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
|
assertStdoutIs '401'
|
|
assertGrep http.headers "^WWW-Authenticate: Basic realm=\"Serval Rhizome\"$CR\$"
|
|
assertJq http.output 'contains({"http_status_code": 401})'
|
|
assertJq http.output 'contains({"http_status_message": ""})'
|
|
executeOk curl \
|
|
--silent --fail --show-error --write-out '%{http_code}' \
|
|
--output http.output \
|
|
--dump-header http.headers \
|
|
--basic --user ron:weasley \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
|
assertStdoutIs '200'
|
|
}
|
|
teardown_AuthBasicWrong() {
|
|
tfw_cat http.headers http.output
|
|
teardown
|
|
}
|
|
|
|
add_bundles() {
|
|
local encrypted=false
|
|
case "$1" in
|
|
--encrypted) encrypted=true; shift;;
|
|
esac
|
|
local n
|
|
for ((n = $1; n <= $2; ++n)); do
|
|
create_file file$n $((1000 + $n))
|
|
if $encrypted; then
|
|
echo "crypt=1" >file$n.manifest
|
|
fi
|
|
executeOk_servald rhizome add file $SIDA file$n file$n.manifest
|
|
extract_stdout_manifestid BID[$n]
|
|
extract_stdout_version VERSION[$n]
|
|
extract_stdout_filesize SIZE[$n]
|
|
extract_stdout_filehash HASH[$n]
|
|
extract_stdout_date DATE[$n]
|
|
extract_stdout_BK BK[$n]
|
|
extract_stdout_rowid ROWID[$n]
|
|
extract_stdout_author AUTHOR[$n]
|
|
extract_stdout_secret SECRET[$n]
|
|
extract_stdout_inserttime INSERTTIME[$n]
|
|
NAME[$n]=file$n
|
|
if $encrypted; then
|
|
extract_stdout_crypt CRYPT[$n]
|
|
assert [ "${CRYPT[$n]}" = 1 ]
|
|
else
|
|
CRYPT[$n]=
|
|
fi
|
|
executeOk_servald rhizome export file ${HASH[$n]} raw$n
|
|
if $encrypted; then
|
|
assert ! cmp file$n raw$n
|
|
else
|
|
assert cmp file$n raw$n
|
|
fi
|
|
[ "${ROWID[$n]}" -gt "${ROWID_MAX:-0}" ] && ROWID_MAX=${ROWID[$n]}
|
|
done
|
|
}
|
|
|
|
transform_list_json() {
|
|
# The following jq(1) incantation transforms a JSON array in from the
|
|
# following form (which is optimised for transmission size):
|
|
# {
|
|
# "header":[ "label1", "label2", "label3", ... ],
|
|
# "rows":[
|
|
# [ row1value1, row1value2, row1value3, ... ],
|
|
# [ row2value1, row2value2, row2value3, ... ],
|
|
# ...
|
|
# [ rowNvalue1, rowNvalue2, rowNvalue3, ... ]
|
|
# ]
|
|
# }
|
|
#
|
|
# into an array of JSON objects:
|
|
# [
|
|
# {
|
|
# "label1": row1value1,
|
|
# "label2": row1value2,
|
|
# "label3": row1value3,
|
|
# ...
|
|
# },
|
|
# {
|
|
# "label1": row2value1,
|
|
# "label2": row2value2,
|
|
# "label3": row2value3,
|
|
# ...
|
|
# },
|
|
# ...
|
|
# {
|
|
# "label1": rowNvalue1,
|
|
# "label2": rowNvalue2,
|
|
# "label3": rowNvalue3,
|
|
# ...
|
|
# }
|
|
# ]
|
|
# which is much easier to test with jq(1) expressions.
|
|
jq '
|
|
[
|
|
.header as $header |
|
|
.rows as $rows |
|
|
$rows | keys | .[] as $index |
|
|
[ $rows[$index] as $d | $d | keys | .[] as $i | {key:$header[$i], value:$d[$i]} ] |
|
|
from_entries |
|
|
.["__index"] = $index
|
|
]
|
|
' "$1" >"$2"
|
|
}
|
|
|
|
doc_RhizomeList="HTTP RESTful list Rhizome bundles as JSON"
|
|
setup_RhizomeList() {
|
|
setup
|
|
NBUNDLES=100
|
|
add_bundles 0 $((NBUNDLES-1))
|
|
assert [ "$ROWID_MAX" -ge "$NBUNDLES" ]
|
|
}
|
|
test_RhizomeList() {
|
|
executeOk curl \
|
|
--silent --fail --show-error \
|
|
--output bundlelist.json \
|
|
--dump-header http.headers \
|
|
--basic --user harry:potter \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
|
tfw_cat http.headers bundlelist.json
|
|
tfw_preserve bundlelist.json
|
|
assert [ "$(jq '.rows | length' bundlelist.json)" = $NBUNDLES ]
|
|
transform_list_json bundlelist.json array_of_objects.json
|
|
tfw_preserve array_of_objects.json
|
|
for ((n = 0; n != NBUNDLES; ++n)); do
|
|
if [ "${ROWID[$n]}" -eq "$ROWID_MAX" ]; then
|
|
# The first row must contain a non-null token string.
|
|
token=',".token":"","__index":0,'
|
|
else
|
|
token=',".token":null,'
|
|
fi
|
|
assertJq array_of_objects.json \
|
|
"contains([
|
|
{ name:\"file$n\",
|
|
service:\"file\",
|
|
id:\"${BID[$n]}\",
|
|
version:${VERSION[$n]},
|
|
filesize:${SIZE[$n]},
|
|
filehash:\"${HASH[$n]}\",
|
|
date:${DATE[$n]},
|
|
_id:${ROWID[$n]},
|
|
\".fromhere\":1,
|
|
\".author\":\"$SIDA\"
|
|
$token
|
|
}
|
|
])"
|
|
done
|
|
}
|
|
|
|
curl_newsince() {
|
|
curl \
|
|
--silent --fail --show-error \
|
|
--no-buffer \
|
|
--output "$1" \
|
|
--basic --user harry:potter \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/newsince/$token/bundlelist.json"
|
|
}
|
|
|
|
doc_RhizomeNewSince="HTTP RESTful list Rhizome bundles since token as JSON"
|
|
setup_RhizomeNewSince() {
|
|
setup
|
|
executeOk_servald config set rhizome.api.restful.newsince_timeout 60s
|
|
executeOk_servald config set rhizome.api.restful.newsince_poll_ms 500
|
|
add_bundles 0 5
|
|
executeOk curl \
|
|
--silent --fail --show-error \
|
|
--output bundlelist.json \
|
|
--dump-header http.headers \
|
|
--basic --user harry:potter \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
|
assert [ "$(jq '.rows | length' bundlelist.json)" = 6 ]
|
|
transform_list_json bundlelist.json array_of_objects.json
|
|
token=$(jq --raw-output '.[0][".token"]' array_of_objects.json)
|
|
assert [ -n "$token" ]
|
|
}
|
|
test_RhizomeNewSince() {
|
|
fork %curl1 curl_newsince newsince1.json
|
|
fork %curl2 curl_newsince newsince2.json
|
|
fork %curl3 curl_newsince newsince3.json
|
|
wait_until [ -e newsince1.json -a -e newsince2.json -a -e newsince3.json ]
|
|
add_bundles 6 10
|
|
wait_until --timeout=10 grep "${BID[10]}" newsince1.json
|
|
wait_until --timeout=10 grep "${BID[10]}" newsince2.json
|
|
wait_until --timeout=10 grep "${BID[10]}" newsince3.json
|
|
fork_terminate_all
|
|
fork_wait_all
|
|
for i in 1 2 3; do
|
|
if [ $(jq . newsince$i | wc -c) -eq 0 ]; then
|
|
echo ']}' >>newsince$i.json
|
|
assert [ $(jq . newsince$i.json | wc -c) -ne 0 ]
|
|
fi
|
|
transform_list_json newsince$i.json objects$i.json
|
|
tfw_preserve newsince$i.json objects$i.json
|
|
for ((n = 0; n <= 5; ++n)); do
|
|
assertJq objects$i.json "contains([{id:\"${BID[$n]}\"}]) | not"
|
|
done
|
|
for ((n = 6; n <= 10; ++n)); do
|
|
assertJq objects$i.json \
|
|
"contains([
|
|
{ name:\"file$n\",
|
|
service:\"file\",
|
|
id:\"${BID[$n]}\",
|
|
version:${VERSION[$n]},
|
|
filesize:${SIZE[$n]},
|
|
filehash:\"${HASH[$n]}\",
|
|
date:${DATE[$n]},
|
|
_id:${ROWID[$n]},
|
|
\".fromhere\":1,
|
|
\".author\":\"$SIDA\",
|
|
\".token\":\"\"
|
|
}
|
|
])"
|
|
done
|
|
done
|
|
}
|
|
|
|
assert_http_response_headers() {
|
|
local n=$1
|
|
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Id: ${BID[$n]}$CR\$"
|
|
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Version: ${VERSION[$n]}$CR\$"
|
|
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Filesize: ${SIZE[$n]}$CR\$"
|
|
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Filehash: ${HASH[$n]}$CR\$"
|
|
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-BK: ${BK[$n]}$CR\$"
|
|
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Date: ${DATE[$n]}$CR\$"
|
|
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Name: \"${NAME[$n]}\"$CR\$"
|
|
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Service: file$CR\$"
|
|
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Author: ${AUTHOR[$n]}$CR\$"
|
|
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Secret: ${SECRET[$n]}$CR\$"
|
|
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Inserttime: ${INSERTTIME[$n]}$CR\$"
|
|
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Rowid: ${ROWID[$n]}$CR\$"
|
|
}
|
|
|
|
doc_RhizomeManifest="HTTP RESTful fetch Rhizome manifest"
|
|
setup_RhizomeManifest() {
|
|
setup
|
|
add_bundles 0 2
|
|
}
|
|
test_RhizomeManifest() {
|
|
for n in 0 1 2; do
|
|
executeOk curl \
|
|
--silent --fail --show-error \
|
|
--output bundle$n.rhm \
|
|
--dump-header http.headers$n \
|
|
--basic --user harry:potter \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/${BID[$n]}.rhm"
|
|
tfw_cat http.headers$n bundle$n.rhm
|
|
tfw_preserve bundle$n.rhm
|
|
done
|
|
for n in 0 1 2; do
|
|
assert diff file$n.manifest bundle$n.rhm
|
|
assert_http_response_headers $n
|
|
done
|
|
}
|
|
|
|
doc_RhizomePayloadRaw="HTTP RESTful fetch Rhizome raw payload"
|
|
setup_RhizomePayloadRaw() {
|
|
setup
|
|
add_bundles 0 1
|
|
add_bundles --encrypted 2 3
|
|
}
|
|
test_RhizomePayloadRaw() {
|
|
for n in 0 1 2 3; do
|
|
executeOk curl \
|
|
--silent --fail --show-error \
|
|
--output raw.bin$n \
|
|
--dump-header http.headers$n \
|
|
--basic --user harry:potter \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/${BID[$n]}/raw.bin"
|
|
tfw_cat http.headers$n raw.bin$n
|
|
done
|
|
for n in 0 1 2 3; do
|
|
assert cmp raw$n raw.bin$n
|
|
assert_http_response_headers $n
|
|
done
|
|
}
|
|
|
|
doc_RhizomePayloadDecrypted="HTTP RESTful fetch Rhizome decrypted payload"
|
|
setup_RhizomePayloadDecrypted() {
|
|
setup
|
|
add_bundles 0 1
|
|
add_bundles --encrypted 2 3
|
|
}
|
|
test_RhizomePayloadDecrypted() {
|
|
for n in 0 1 2 3; do
|
|
executeOk curl \
|
|
--silent --fail --show-error \
|
|
--output decrypted.bin$n \
|
|
--dump-header http.headers$n \
|
|
--basic --user harry:potter \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/${BID[$n]}/decrypted.bin"
|
|
tfw_cat http.headers$n decrypted.bin$n
|
|
done
|
|
for n in 0 1 2 3; do
|
|
assert cmp file$n decrypted.bin$n
|
|
assert_http_response_headers $n
|
|
done
|
|
}
|
|
|
|
extract_http_header() {
|
|
local __var="$1"
|
|
local __headerfile="$2"
|
|
local __header="$3"
|
|
local __rexp="$4"
|
|
local __value=$($SED -n -e "/^$__header:[ $VT]*$__rexp$CR\$/s/^$__header:[ $VT]*\(.*\)$CR\$/\1/p" "$__headerfile")
|
|
assert --message="$__headerfile contains valid '$__header' header" \
|
|
--dump-on-fail="$__headerfile" \
|
|
[ -n "$__value" ]
|
|
[ -n "$__var" ] && eval $__var=\"\$__value\"
|
|
}
|
|
|
|
http_unquote_string() {
|
|
local __var="$1"
|
|
local __unq="$(eval echo '"${'$__var'}"' | sed -e 's/^"//' -e 's/"$//' -e 's/\\\(.\)/\1/g')"
|
|
eval $__var=\"\$__unq\"
|
|
}
|
|
|
|
doc_RhizomeInsert="HTTP RESTful insert new Rhizome bundles"
|
|
setup_RhizomeInsert() {
|
|
IDENTITY_COUNT=3
|
|
SIDA4=
|
|
setup
|
|
for n in 1 2 3 4; do
|
|
create_file file$n $((1000 + $n))
|
|
create_file nfile$n $((1100 + $n))
|
|
payload_filename[$n]=
|
|
eval author[$n]=\$SIDA$n
|
|
service[$n]=file
|
|
done
|
|
name[1]=blarg
|
|
echo "name=blarg" >manifest1
|
|
name[2]=file2
|
|
echo "crypt=1" >manifest2
|
|
name[3]=kibble
|
|
payload_filename[3]=kibble
|
|
>manifest3
|
|
name[4]=
|
|
service[4]=wah
|
|
echo -e "service=wah\ncrypt=0" >manifest4
|
|
}
|
|
test_RhizomeInsert() {
|
|
for n in 1 2 3 4; do
|
|
authorargs=()
|
|
[ -n "${author[$n]}" ] && authorargs=(--form "bundle-author=${author[$n]}")
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output file$n.manifest \
|
|
--dump-header http.header$n \
|
|
--basic --user harry:potter \
|
|
"${authorargs[@]}" \
|
|
--form "manifest=@manifest$n;type=rhizome-manifest/text" \
|
|
--form "payload=@file$n${payload_filename[$n]:+;filename=\"${payload_filename[$n]}\"}" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header$n file$n.manifest
|
|
assertExitStatus == 0
|
|
assertStdoutIs 201
|
|
extract_http_header BID[$n] http.header$n Serval-Rhizome-Bundle-Id "$rexp_manifestid"
|
|
extract_http_header VERSION[$n] http.header$n Serval-Rhizome-Bundle-Version "$rexp_version"
|
|
extract_http_header SIZE[$n] http.header$n Serval-Rhizome-Bundle-Filesize "$rexp_filesize"
|
|
extract_http_header HASH[$n] http.header$n Serval-Rhizome-Bundle-Filehash "$rexp_filehash"
|
|
extract_http_header DATE[$n] http.header$n Serval-Rhizome-Bundle-Date "$rexp_date"
|
|
extract_http_header ROWID[$n] http.header$n Serval-Rhizome-Bundle-Rowid "$rexp_rowid"
|
|
extract_http_header INSERTTIME[$n] http.header$n Serval-Rhizome-Bundle-Inserttime "$rexp_date"
|
|
extract_http_header SECRET[$n] http.header$n Serval-Rhizome-Bundle-Secret "$rexp_bundlesecret"
|
|
extract_http_header SERVICE[$n] http.header$n Serval-Rhizome-Bundle-Service ".*"
|
|
assert [ ${SIZE[$n]} -eq $((1000 + $n)) ]
|
|
assert [ "${SERVICE[$n]}" = "${service[$n]}" ]
|
|
extract_manifest_id BID file$n.manifest
|
|
extract_manifest_version VERSION file$n.manifest
|
|
extract_manifest_filesize SIZE file$n.manifest
|
|
extract_manifest_filehash HASH file$n.manifest
|
|
extract_manifest_date DATE file$n.manifest
|
|
extract_manifest_service SERVICE file$n.manifest
|
|
assert [ "$BID" = "${BID[$n]}" ]
|
|
assert [ "$VERSION" = "${VERSION[$n]}" ]
|
|
assert [ "$SIZE" = "${SIZE[$n]}" ]
|
|
assert [ "$HASH" = "${HASH[$n]}" ]
|
|
assert [ "$DATE" = "${DATE[$n]}" ]
|
|
assert [ "$SERVICE" = "${SERVICE[$n]}" ]
|
|
if [ -n "${name[$n]}" ]; then
|
|
extract_http_header NAME[$n] http.header$n Serval-Rhizome-Bundle-Name ".*"
|
|
http_unquote_string NAME[$n]
|
|
assert [ "${NAME[$n]}" = "${name[$n]}" ]
|
|
extract_manifest_name NAME file$n.manifest
|
|
assert [ "$NAME" = "${NAME[$n]}" ]
|
|
fi
|
|
if [ -n "${author[$n]}" ]; then
|
|
extract_http_header AUTHOR[$n] http.header$n Serval-Rhizome-Bundle-Author "$rexp_sid"
|
|
assert [ "${AUTHOR[$n]}" = "${author[$n]}" ]
|
|
extract_http_header BK[$n] http.header$n Serval-Rhizome-Bundle-BK "$rexp_bundlekey"
|
|
extract_manifest_BK BK file$n.manifest
|
|
assert [ "$BK" = "${BK[$n]}" ]
|
|
fi
|
|
done
|
|
executeOk_servald rhizome list
|
|
assert_rhizome_list \
|
|
--fromhere=1 \
|
|
--author=${author[1]} file1 \
|
|
--author=${author[2]} file2 \
|
|
--author=${author[3]} file3 \
|
|
--fromhere=0 \
|
|
--author=${author[4]} file4
|
|
for n in 1 2 3 4; do
|
|
executeOk_servald rhizome extract bundle ${BID[$n]} xfile$n.manifest xfile$n
|
|
assert diff xfile$n.manifest file$n.manifest
|
|
assert diff file$n xfile$n
|
|
done
|
|
for n in 1 2 3 4; do
|
|
$SED -e '/^version=/d;/^date=/d;/^filehash=/d;/^filesize=/d' xfile$n.manifest >nmanifest$n
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output nfile$n.manifest \
|
|
--dump-header http.header$n \
|
|
--basic --user harry:potter \
|
|
--form "manifest=@nmanifest$n;type=rhizome-manifest/text" \
|
|
--form "payload=@nfile$n;filename=\"nfile$n\"" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header$n nfile$n.manifest
|
|
assertExitStatus == 0
|
|
if [ -n "${author[$n]}" ]; then
|
|
assertStdoutIs 201
|
|
else
|
|
assertStdoutIs 403
|
|
assertJq nfile$n.manifest 'contains({"http_status_code": 403})'
|
|
assertJqGrep --ignore-case nfile$n.manifest '.http_status_message' "missing bundle secret"
|
|
fi
|
|
done
|
|
}
|
|
|
|
doc_RhizomeInsertAnon="HTTP RESTful update anonymous Rhizome bundle"
|
|
setup_RhizomeInsertAnon() {
|
|
setup
|
|
create_file file1 1001
|
|
executeOk_servald rhizome add file '' file1 file1.manifest
|
|
extract_stdout_secret SECRET
|
|
assertGrep --matches=0 file1.manifest '^BK='
|
|
$SED -e '/^version=/d;/^date=/d;/^filehash=/d;/^filesize=/d' file1.manifest >file2.manifest
|
|
create_file file2 1002
|
|
}
|
|
test_RhizomeInsertAnon() {
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output ifile2.manifest \
|
|
--dump-header http.header \
|
|
--basic --user harry:potter \
|
|
--form "bundle-secret=$SECRET" \
|
|
--form "manifest=@file2.manifest;type=rhizome-manifest/text" \
|
|
--form "payload=@file2" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header ifile2.manifest
|
|
assertExitStatus == 0
|
|
assertStdoutIs 201
|
|
executeOk_servald rhizome list
|
|
assert_rhizome_list --fromhere=0 --manifest=ifile2.manifest file2
|
|
}
|
|
|
|
doc_RhizomeInsertEmpty="HTTP RESTful insert empty Rhizome bundle"
|
|
setup_RhizomeInsertEmpty() {
|
|
setup
|
|
>empty
|
|
assert [ ! -s empty ]
|
|
}
|
|
test_RhizomeInsertEmpty() {
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output empty.manifest \
|
|
--dump-header http.header \
|
|
--basic --user harry:potter \
|
|
--form "manifest=;type=rhizome-manifest/text" \
|
|
--form "payload=@empty;filename=\"lucky\"" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header empty.manifest
|
|
assertExitStatus == 0
|
|
assertStdoutIs 201
|
|
extract_manifest_id BID empty.manifest
|
|
executeOk_servald rhizome list
|
|
assert_rhizome_list empty
|
|
executeOk_servald rhizome extract bundle $BID xempty.manifest xempty
|
|
assert [ ! -e xempty ]
|
|
assert diff xempty.manifest empty.manifest
|
|
}
|
|
|
|
doc_RhizomeInsertLarge="HTTP RESTful insert 50 MiB Rhizome bundle"
|
|
setup_RhizomeInsertLarge() {
|
|
setup
|
|
create_file file1 50m
|
|
}
|
|
test_RhizomeInsertLarge() {
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output file1.manifest \
|
|
--dump-header http.header \
|
|
--basic --user harry:potter \
|
|
--form "manifest=;type=rhizome-manifest/text" \
|
|
--form "payload=@file1" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header file1.manifest
|
|
assertExitStatus == 0
|
|
assertStdoutIs 201
|
|
extract_manifest_id BID file1.manifest
|
|
executeOk_servald rhizome list
|
|
assert_rhizome_list file1
|
|
executeOk_servald rhizome extract bundle $BID xfile1.manifest xfile1
|
|
assert diff xfile1.manifest file1.manifest
|
|
assert cmp file1 xfile1
|
|
}
|
|
|
|
doc_RhizomeInsertMissingManifest="HTTP RESTful insert missing 'manifest' form part"
|
|
setup_RhizomeInsertMissingManifest() {
|
|
setup
|
|
echo 'File one' >file1
|
|
}
|
|
test_RhizomeInsertMissingManifest() {
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output http.body \
|
|
--dump-header http.header \
|
|
--basic --user harry:potter \
|
|
--form "payload=@file1" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header http.body
|
|
assertExitStatus == 0
|
|
assertStdoutIs 403
|
|
assertJq http.body 'contains({"http_status_code": 403})'
|
|
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*manifest.*form.*part'
|
|
executeOk_servald rhizome list
|
|
assert_rhizome_list
|
|
}
|
|
|
|
doc_RhizomeInsertIncorrectManifestType="HTTP RESTful insert incorrect 'manifest' content type"
|
|
setup_RhizomeInsertIncorrectManifestType() {
|
|
setup
|
|
echo 'File one' >file1
|
|
}
|
|
test_RhizomeInsertIncorrectManifestType() {
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output http.body \
|
|
--dump-header http.header \
|
|
--basic --user harry:potter \
|
|
--form "manifest=;type=rhizome-manifest/something" \
|
|
--form "payload=@file1" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header http.body
|
|
assertExitStatus == 0
|
|
assertStdoutIs 403
|
|
assertJq http.body 'contains({"http_status_code": 403})'
|
|
assertJqGrep --ignore-case http.body '.http_status_message' 'unsupported content-type.*manifest.*form.*part'
|
|
executeOk_servald rhizome list
|
|
assert_rhizome_list
|
|
}
|
|
|
|
doc_RhizomeInsertDuplicateManifest="HTTP RESTful insert duplicate 'manifest' form part"
|
|
setup_RhizomeInsertDuplicateManifest() {
|
|
setup
|
|
echo 'File one' >file1
|
|
echo 'name=wah' >file1.manifest
|
|
echo 'name=bee' >file2.manifest
|
|
}
|
|
test_RhizomeInsertDuplicateManifest() {
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output http.body \
|
|
--dump-header http.header \
|
|
--basic --user harry:potter \
|
|
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
|
--form "manifest=@file2.manifest;type=rhizome-manifest/text" \
|
|
--form "payload=@file1" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header http.body
|
|
assertExitStatus == 0
|
|
assertStdoutIs 403
|
|
assertJq http.body 'contains({"http_status_code": 403})'
|
|
assertJqGrep --ignore-case http.body '.http_status_message' 'duplicate.*manifest.*form.*part'
|
|
executeOk_servald rhizome list
|
|
assert_rhizome_list
|
|
}
|
|
|
|
doc_RhizomeInsertJournal="HTTP RESTful insert does not support journals"
|
|
setup_RhizomeInsertJournal() {
|
|
setup
|
|
echo 'File one' >file1
|
|
echo 'tail=0' >file1.manifest
|
|
}
|
|
test_RhizomeInsertJournal() {
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output http.body \
|
|
--dump-header http.header \
|
|
--basic --user harry:potter \
|
|
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
|
--form "payload=@file1" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header http.body
|
|
assertExitStatus == 0
|
|
assertStdoutIs 403
|
|
assertJq http.body 'contains({"http_status_code": 403})'
|
|
assertJqGrep --ignore-case http.body '.http_status_message' 'not supported.*journal'
|
|
executeOk_servald rhizome list
|
|
assert_rhizome_list
|
|
}
|
|
|
|
doc_RhizomeInsertMissingPayload="HTTP RESTful insert missing 'payload' form part"
|
|
setup_RhizomeInsertMissingPayload() {
|
|
setup
|
|
echo 'File one' >file1
|
|
echo 'name=wah' >file1.manifest
|
|
}
|
|
test_RhizomeInsertMissingPayload() {
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output http.body \
|
|
--dump-header http.header \
|
|
--basic --user harry:potter \
|
|
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header http.body
|
|
assertExitStatus == 0
|
|
assertStdoutIs 403
|
|
assertJq http.body 'contains({"http_status_code": 403})'
|
|
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*payload.*form.*part'
|
|
executeOk_servald rhizome list
|
|
assert_rhizome_list
|
|
}
|
|
|
|
doc_RhizomeInsertDuplicatePayload="HTTP RESTful insert duplicate 'payload' form part"
|
|
setup_RhizomeInsertDuplicatePayload() {
|
|
setup
|
|
echo 'File one' >file1
|
|
echo 'File two' >file2
|
|
echo 'name=wah' >file1.manifest
|
|
}
|
|
test_RhizomeInsertDuplicatePayload() {
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output http.body \
|
|
--dump-header http.header \
|
|
--basic --user harry:potter \
|
|
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
|
--form "payload=@file1" \
|
|
--form "payload=@file2" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header http.body
|
|
assertExitStatus == 0
|
|
assertStdoutIs 403
|
|
assertJq http.body 'contains({"http_status_code": 403})'
|
|
assertJqGrep --ignore-case http.body '.http_status_message' 'duplicate.*payload.*form.*part'
|
|
executeOk_servald rhizome list
|
|
assert_rhizome_list
|
|
}
|
|
|
|
doc_RhizomeInsertPartOrder="HTTP RESTful insert 'payload' form part before 'manifest'"
|
|
setup_RhizomeInsertPartOrder() {
|
|
setup
|
|
echo 'File one' >file1
|
|
echo 'name=wah' >file1.manifest
|
|
}
|
|
test_RhizomeInsertPartOrder() {
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output http.body \
|
|
--dump-header http.header \
|
|
--basic --user harry:potter \
|
|
--form "payload=@file1" \
|
|
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header http.body
|
|
assertExitStatus == 0
|
|
assertStdoutIs 403
|
|
assertJq http.body 'contains({"http_status_code": 403})'
|
|
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*manifest.*form.*part'
|
|
executeOk_servald rhizome list
|
|
assert_rhizome_list
|
|
}
|
|
|
|
doc_RhizomeInsertPartUnsupported="HTTP RESTful insert unsupported form part"
|
|
setup_RhizomeInsertPartUnsupported() {
|
|
setup
|
|
echo 'File one' >file1
|
|
echo 'name=wah' >file1.manifest
|
|
}
|
|
test_RhizomeInsertPartUnsupported() {
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output http.body \
|
|
--dump-header http.header \
|
|
--basic --user harry:potter \
|
|
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
|
--form "payload=@file1" \
|
|
--form "happyhappy=joyjoy" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header http.body
|
|
assertExitStatus == 0
|
|
assertStdoutIs 403
|
|
assertJq http.body 'contains({"http_status_code": 403})'
|
|
assertJqGrep --ignore-case http.body '.http_status_message' 'unsupported.*form.*part'
|
|
assertJqGrep http.body '.http_status_message' 'happyhappy'
|
|
executeOk_servald rhizome list
|
|
assert_rhizome_list
|
|
}
|
|
|
|
doc_RhizomeInsertIncorrectFilesize="HTTP RESTful insert with incorrect filesize"
|
|
setup_RhizomeInsertIncorrectFilesize() {
|
|
setup
|
|
echo 'File one' >file1
|
|
echo 'filesize=6' >file1.manifest
|
|
echo 'File two' >file2
|
|
echo 'filesize=100' >file2.manifest
|
|
}
|
|
test_RhizomeInsertIncorrectFilesize() {
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output http.body \
|
|
--dump-header http.header \
|
|
--basic --user harry:potter \
|
|
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
|
--form "payload=@file1" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header http.body
|
|
assertExitStatus == 0
|
|
assertStdoutIs 403
|
|
assertJq http.body 'contains({"http_status_code": 403})'
|
|
assertJqGrep --ignore-case http.body '.http_status_message' 'payload size.*contradicts manifest'
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output http.body \
|
|
--dump-header http.header \
|
|
--basic --user harry:potter \
|
|
--form "manifest=@file2.manifest;type=rhizome-manifest/text" \
|
|
--form "payload=@file2" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header http.body
|
|
assertExitStatus == 0
|
|
assertStdoutIs 403
|
|
assertGrep --ignore-case http.body 'payload size.*contradicts manifest'
|
|
executeOk_servald rhizome list
|
|
assert_rhizome_list
|
|
}
|
|
|
|
doc_RhizomeInsertIncorrectFilehash="HTTP RESTful insert with incorrect filehash"
|
|
setup_RhizomeInsertIncorrectFilehash() {
|
|
setup
|
|
echo 'File one' >file1
|
|
echo 'filehash=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' >file1.manifest
|
|
}
|
|
test_RhizomeInsertIncorrectFilehash() {
|
|
execute curl \
|
|
--silent --show-error --write-out '%{http_code}' \
|
|
--output http.body \
|
|
--dump-header http.header \
|
|
--basic --user harry:potter \
|
|
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
|
--form "payload=@file1" \
|
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
|
tfw_cat http.header http.body
|
|
assertExitStatus == 0
|
|
assertStdoutIs 403
|
|
assertJq http.body 'contains({"http_status_code": 403})'
|
|
assertJqGrep --ignore-case http.body '.http_status_message' 'payload hash.*contradicts manifest'
|
|
executeOk_servald rhizome list
|
|
assert_rhizome_list
|
|
}
|
|
|
|
doc_MeshmsListConversations="HTTP RESTful list MeshMS conversations as JSON"
|
|
setup_MeshmsListConversations() {
|
|
IDENTITY_COUNT=5
|
|
setup
|
|
# create 3 threads, with all permutations of incoming and outgoing messages
|
|
executeOk_servald meshms send message $SIDA1 $SIDA2 "Message1"
|
|
executeOk_servald meshms send message $SIDA3 $SIDA1 "Message2"
|
|
executeOk_servald meshms send message $SIDA1 $SIDA4 "Message3"
|
|
executeOk_servald meshms send message $SIDA4 $SIDA1 "Message4"
|
|
}
|
|
test_MeshmsListConversations() {
|
|
executeOk curl \
|
|
--silent --fail --show-error \
|
|
--output conversationlist1.json \
|
|
--dump-header http.headers \
|
|
--basic --user harry:potter \
|
|
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA1/conversationlist.json"
|
|
tfw_cat http.headers conversationlist1.json
|
|
tfw_preserve conversationlist1.json
|
|
assert [ "$(jq '.rows | length' conversationlist1.json)" = 3 ]
|
|
transform_list_json conversationlist1.json conversations1.json
|
|
tfw_preserve conversations1.json
|
|
assertJq conversations1.json \
|
|
"contains([
|
|
{ my_sid: \"$SIDA1\",
|
|
their_sid: \"$SIDA2\",
|
|
read: true,
|
|
last_message: 0,
|
|
read_offset: 0
|
|
}
|
|
])"
|
|
assertJq conversations1.json \
|
|
"contains([
|
|
{ my_sid: \"$SIDA1\",
|
|
their_sid: \"$SIDA3\",
|
|
read: false,
|
|
last_message: 11,
|
|
read_offset: 0
|
|
}
|
|
])"
|
|
assertJq conversations1.json \
|
|
"contains([
|
|
{ my_sid: \"$SIDA1\",
|
|
their_sid: \"$SIDA4\",
|
|
read: false,
|
|
last_message: 14,
|
|
read_offset: 0
|
|
}
|
|
])"
|
|
# mark all incoming messages as read
|
|
executeOk_servald meshms read messages $SIDA1
|
|
tfw_cat --stderr
|
|
executeOk curl \
|
|
--silent --fail --show-error \
|
|
--output conversationlist2.json \
|
|
--dump-header http.headers \
|
|
--basic --user harry:potter \
|
|
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA1/conversationlist.json"
|
|
tfw_cat http.headers conversationlist2.json
|
|
tfw_preserve conversationlist2.json
|
|
assert [ "$(jq '.rows | length' conversationlist2.json)" = 3 ]
|
|
transform_list_json conversationlist2.json conversations2.json
|
|
tfw_preserve conversations2.json
|
|
assertJq conversations2.json \
|
|
"contains([
|
|
{ my_sid: \"$SIDA1\",
|
|
their_sid: \"$SIDA2\",
|
|
read: true,
|
|
last_message: 0,
|
|
read_offset: 0
|
|
}
|
|
])"
|
|
assertJq conversations2.json \
|
|
"contains([
|
|
{ my_sid: \"$SIDA1\",
|
|
their_sid: \"$SIDA3\",
|
|
read: true,
|
|
last_message: 11,
|
|
read_offset: 11
|
|
}
|
|
])"
|
|
assertJq conversations2.json \
|
|
"contains([
|
|
{ my_sid: \"$SIDA1\",
|
|
their_sid: \"$SIDA4\",
|
|
read: true,
|
|
last_message: 14,
|
|
read_offset: 14
|
|
}
|
|
])"
|
|
}
|
|
|
|
doc_MeshmsListMessages="HTTP RESTful list MeshMS messages in one conversation as JSON"
|
|
setup_MeshmsListMessages() {
|
|
IDENTITY_COUNT=2
|
|
setup
|
|
text=(
|
|
"One banana"
|
|
"Two apples"
|
|
"Three watermelons"
|
|
"Four oranges"
|
|
"Five grapes"
|
|
""
|
|
"Six lychees"
|
|
"Seven raspberries"
|
|
"Eight lemons"
|
|
)
|
|
direction=('>' '>' '<' '>' '>' 'ACK' '<' '<' '>')
|
|
for ((i = 0; i < ${#direction[*]}; ++i)); do
|
|
case ${direction[$i]} in
|
|
'>') executeOk_servald meshms send message $SIDA1 $SIDA2 "${text[$i]}";;
|
|
'<') executeOk_servald meshms send message $SIDA2 $SIDA1 "${text[$i]}";;
|
|
'ACK') executeOk_servald meshms list messages $SIDA2 $SIDA1;;
|
|
esac
|
|
done
|
|
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
|
tfw_cat --stdout
|
|
delivered_offset=$(sed -n -e '/^[0-9]\+:[0-9]\+:ACK:delivered$/{n;s/^[0-9]\+:\([0-9]\+\):>:.*/\1/p;q}' "$TFWSTDOUT")
|
|
[ -z "$delivered_offset" ] && delivered_offset=0
|
|
read_offset=$(sed -n -e 's/^[0-9]\+:\([0-9]\+\):MARK:read$/\1/p' "$TFWSTDOUT")
|
|
[ -z "$read_offset" ] && read_offset=0
|
|
}
|
|
test_MeshmsListMessages() {
|
|
executeOk curl \
|
|
--silent --fail --show-error \
|
|
--output messagelist.json \
|
|
--dump-header http.headers \
|
|
--basic --user harry:potter \
|
|
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA1/$SIDA2/messagelist.json"
|
|
tfw_cat http.headers messagelist.json
|
|
tfw_preserve messagelist.json
|
|
assert [ "$(jq '.rows | length' messagelist.json)" = ${#direction[*]} ]
|
|
transform_list_json messagelist.json messages.json
|
|
tfw_preserve messages.json
|
|
for ((i = 0; i < ${#direction[*]}; ++i)); do
|
|
let j=${#text[*]}-$i-1
|
|
assertJq messages.json '(.['$i'].token | length) > 0'
|
|
assertJq messages.json '.['$i'].type == "'"${direction[$j]}"'"'
|
|
assertJq messages.json '.['$i'].my_sid == "'$SIDA1'"'
|
|
assertJq messages.json '.['$i'].their_sid == "'$SIDA2'"'
|
|
case ${direction[$j]} in
|
|
'>')
|
|
assertJq messages.json '.['$i'].text == "'"${text[$j]}"'"'
|
|
assertJq messages.json '.['$i'].delivered == (.['$i'].offset <= '$delivered_offset')'
|
|
;;
|
|
'<')
|
|
assertJq messages.json '.['$i'].text == "'"${text[$j]}"'"'
|
|
assertJq messages.json '.['$i'].read == (.['$i'].offset <= '$read_offset')'
|
|
;;
|
|
'ACK')
|
|
assertJq messages.json '.['$i'].text == null'
|
|
assertJq messages.json '.['$i'].ack_offset == '$delivered_offset
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
doc_MeshmsListMessagesSince="HTTP RESTful list MeshMS messages in one conversation since token as JSON"
|
|
Xtest_MeshmsListMessagesSince() {
|
|
:
|
|
}
|
|
|
|
doc_MeshmsSend="HTTP RESTful send MeshMS message"
|
|
Xtest_MeshmsSend() {
|
|
:
|
|
}
|
|
|
|
runTests "$@"
|