testlib.sh (4982B)
1 #!/bin/sh 2 3 PASS=0 4 FAIL=0 5 6 TESTROOT=${TESTROOT:-testroot} 7 TEST_LOCK_DIR="$TESTROOT/run/simplefiles/lock" 8 TEST_UPLOAD_DIR="$TESTROOT/run/simplefiles/uploads" 9 TEST_CONFIG="$TESTROOT/etc/simplefiles.conf" 10 TEST_CSRF_ORIGIN=${TEST_CSRF_ORIGIN:-http://localhost:8000} 11 12 # 13 # Reporting 14 # 15 16 ok() { 17 printf '%-60s ok\n' "$1" 18 PASS=$((PASS + 1)) 19 } 20 21 not_ok() { 22 printf '%-60s failed\n' "$1" 23 printf ' %s\n' "$2" 24 FAIL=$((FAIL + 1)) 25 } 26 27 # 28 # Run a command and capture its output. 29 # 30 # These are globals deliberately. POSIX sh has no local variables, 31 # and keeping command execution in one place makes the assertions 32 # simpler and safer with "set -e". 33 # 34 35 run() { 36 TEST_OUTPUT= 37 TEST_RC=0 38 39 if TEST_OUTPUT=$("$@" 2>&1); then 40 TEST_RC=0 41 else 42 TEST_RC=$? 43 fi 44 45 return 0 46 } 47 48 # 49 # HTTP helpers 50 # 51 52 status_from_output() { 53 printf '%s\n' "$1" | 54 sed -n 's/^Status: //p' | 55 sed -n '1p' | 56 tr -d '\r' 57 } 58 59 header_from_output() { 60 header_name=$1 61 output=$2 62 63 printf '%s\n' "$output" | 64 sed -n "s/^${header_name}: //p" | 65 sed -n '1p' | 66 tr -d '\r' 67 } 68 69 # 70 # Assertions 71 # 72 73 check_status() { 74 name=$1 75 expected=$2 76 shift 2 77 78 run "$@" 79 80 got=$(status_from_output "$TEST_OUTPUT") 81 82 if [ "$got" = "$expected" ]; then 83 ok "$name" 84 else 85 not_ok "$name" \ 86 "expected status: $expected (got: ${got:-none})" 87 printf '%s\n' "$TEST_OUTPUT" 88 fi 89 } 90 91 check_body() { 92 name=$1 93 expected=$2 94 shift 2 95 96 run "$@" 97 98 if printf '%s' "$TEST_OUTPUT" | grep -F "$expected" > /dev/null; then 99 ok "$name" 100 else 101 not_ok "$name" "missing body: $expected" 102 fi 103 } 104 105 check_not_body() { 106 name=$1 107 unexpected=$2 108 shift 2 109 110 run "$@" 111 112 if printf '%s' "$TEST_OUTPUT" | grep -F "$unexpected" > /dev/null; then 113 not_ok "$name" "unexpected body: $unexpected" 114 else 115 ok "$name" 116 fi 117 } 118 119 check_header() { 120 name=$1 121 header_name=$2 122 expected=$3 123 shift 3 124 125 run "$@" 126 127 got=$(header_from_output "$header_name" "$TEST_OUTPUT") 128 129 if [ "$got" = "$expected" ]; then 130 ok "$name" 131 else 132 not_ok "$name" \ 133 "expected $header_name: $expected (got: ${got:-none})" 134 fi 135 } 136 137 check_redirect() { 138 name=$1 139 expected_status=$2 140 expected_location=$3 141 shift 3 142 143 run "$@" 144 145 got_status=$(status_from_output "$TEST_OUTPUT") 146 147 if [ "$got_status" != "$expected_status" ]; then 148 not_ok "$name" \ 149 "expected status: $expected_status (got: ${got_status:-none})" 150 return 151 fi 152 153 got_location=$(header_from_output Location "$TEST_OUTPUT") 154 155 if [ "$got_location" != "$expected_location" ]; then 156 not_ok "$name" \ 157 "expected Location: $expected_location (got: ${got_location:-none})" 158 return 159 fi 160 161 ok "$name" 162 } 163 164 # 165 # Filesystem assertions 166 # 167 168 check_file() { 169 name=$1 170 file=$2 171 expected=$3 172 173 if [ ! -f "$file" ]; then 174 not_ok "$name" "missing file: $file" 175 return 176 fi 177 178 size=$(wc -c < "$file" | tr -d ' ') 179 180 # 181 # POSIX sh does not have ${#string}, so use wc for the expected 182 # byte count too. 183 # 184 expected_size=$(printf '%s' "$expected" | wc -c | tr -d ' ') 185 186 if [ "$size" -ne "$expected_size" ]; then 187 not_ok "$name" \ 188 "wrong size: expected $expected_size bytes, got $size" 189 return 190 fi 191 192 actual=$(cat "$file") 193 194 if [ "$actual" != "$expected" ]; then 195 not_ok "$name" "unexpected file contents" 196 return 197 fi 198 199 ok "$name" 200 } 201 202 check_file_exists() { 203 name=$1 204 file=$2 205 206 if [ -f "$file" ]; then 207 ok "$name" 208 else 209 not_ok "$name" "missing file: $file" 210 fi 211 } 212 213 check_file_missing() { 214 name=$1 215 file=$2 216 217 if [ ! -e "$file" ]; then 218 ok "$name" 219 else 220 not_ok "$name" "unexpected file: $file" 221 fi 222 } 223 224 check_dir_missing() { 225 name=$1 226 dir=$2 227 228 if [ ! -d "$dir" ]; then 229 ok "$name" 230 else 231 not_ok "$name" "unexpected directory: $dir" 232 fi 233 } 234 235 check_no_temp_uploads() { 236 name=$1 237 dir=$2 238 239 if [ ! -d "$dir" ]; then 240 ok "$name" 241 return 242 fi 243 244 if find "$dir" -name '.upload-*' -type f -print | 245 grep -q .; then 246 not_ok "$name" "temporary upload remains" 247 else 248 ok "$name" 249 fi 250 } 251 252 check_file_bytes() { 253 name=$1 254 file=$2 255 expected=$3 256 257 tmp=$(mktemp) 258 259 printf '%b' "$expected" > "$tmp" 260 261 if [ ! -f "$file" ]; then 262 rm -f "$tmp" 263 not_ok "$name" "file missing: $file" 264 return 265 fi 266 267 if cmp -s "$file" "$tmp"; then 268 ok "$name" 269 else 270 not_ok "$name" "file contents differ: $file" 271 fi 272 273 rm -f "$tmp" 274 } 275 276 # 277 # Test environment helpers 278 # 279 280 reset_testroot() { 281 rm -rf "$TESTROOT" 282 mkdir -p "$TESTROOT/storage/default" 283 mkdir -p "$TEST_LOCK_DIR" 284 mkdir -p "$TEST_UPLOAD_DIR" 285 mkdir -p "$TESTROOT/etc" 286 chmod go-w "$TEST_UPLOAD_DIR" 287 printf 'CSRF_ORIGIN=%s\n' "$TEST_CSRF_ORIGIN" > "$TEST_CONFIG" 288 chmod 644 "$TEST_CONFIG" 289 } 290 291 make_repo() { 292 mkdir -p "$TESTROOT/storage/$1" 293 } 294 295 write_global_config() { 296 printf '%s\n' "$1" > "$TEST_CONFIG" 297 } 298 299 write_default_repo_config() { 300 mkdir -p "$TESTROOT/storage/default" 301 printf '%s\n' "$1" > "$TESTROOT/storage/default/.config" 302 } 303 304 # 305 # Final result 306 # 307 308 test_result() { 309 printf '\n%d passed, %d failed\n' "$PASS" "$FAIL" 310 [ "$FAIL" -eq 0 ] 311 }