testing-entrypoint.sh 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. #!/usr/bin/env bash
  2. # These tests are written to run in their own container, using the same image as the
  3. # actual postgres service. To run: `docker-compose up --build`
  4. set -euo pipefail
  5. source /weed.sh
  6. ERROR_COUNT=0
  7. FAILURE_COUNT=0
  8. # compare two sorted files
  9. function compare_files {
  10. local expected="$1"
  11. local actual="$2"
  12. declare -a missing
  13. local missing_index=0
  14. declare -a extra
  15. local extra_index=0
  16. old_ifs="$IFS"
  17. IFS=$'\n'
  18. for line in $(diff --suppress-common-lines "$expected" "$actual"); do
  19. if [[ $line =~ ^\< ]]; then
  20. missing[missing_index]=${line:1}
  21. missing_index=$((missing_index + 1))
  22. elif [[ $line =~ ^\> ]]; then
  23. extra[extra_index]=${line:1}
  24. extra_index=$((extra_index + 1))
  25. fi
  26. done
  27. IFS="$old_ifs"
  28. if [[ $((missing_index + extra_index)) -gt 0 ]]; then
  29. echo 'fail'
  30. if [[ missing_index -gt 0 ]]; then
  31. echo -e "\\t$missing_index missing files:"
  32. for index in $(seq 0 $((missing_index - 1))); do
  33. echo -e "\\t\\t${missing[index]}"
  34. done
  35. fi
  36. if [[ extra_index -gt 0 ]]; then
  37. echo -e "\\t$extra_index extra files:"
  38. for index in $(seq 0 $((extra_index - 1))); do
  39. echo -e "\\t\\t${extra[index]}"
  40. done
  41. fi
  42. FAILURE_COUNT=$((FAILURE_COUNT + 1))
  43. return 1
  44. fi
  45. }
  46. # This is a wrapper function that handles creating a directory with test files in it,
  47. # running weed_directory (as the function, as a dry run, then finally actually-deleting
  48. # files), marking the test as failed/errored as necessary, then cleaning up after
  49. # itself. the first three arguments passed are the thresholds to pass into
  50. # weed_directory. The remaining arguments are names of files to create for the test.
  51. # Bash isn't great at passing arrays so instead of separately passing in a list of
  52. # expected results, flag the files you expect to be deleted by prepending "DELETE:"
  53. # to the path.
  54. function perform_test {
  55. echo "${FUNCNAME[1]}" | sed 's/^test_\(.*\)$/\1/' | tr '_\n' ' :'
  56. echo -en '\t'
  57. local daily_threshold="$1"
  58. shift
  59. local weekly_threshold="$1"
  60. shift
  61. local monthly_threshold="$1"
  62. shift
  63. # We might as well name the files we're using for running tests in as inflamatory a
  64. # way as possible to increase the chances that bad filtering by weed_directory
  65. # results in tests failing.
  66. local expected="/testing/expected/backup__2020-02-02.sql"
  67. local actual="/testing/backup__2020-02-02.sql.actual"
  68. local remaining="/testing/remainbackup__2020-02-02.sql"
  69. local temp="/testing/backup__2020-TE-MP.sql"
  70. # create test files
  71. mkdir -p /testing/expected
  72. if [[ -e "$expected" ]]; then
  73. rm "$expected"
  74. fi
  75. touch "$expected"
  76. echo -e "$expected\\n$actual\\n$remaining\\n$temp" > "$remaining"
  77. while [[ "$#" -gt 0 ]]; do
  78. if [[ "$1" =~ ^DELETE: ]]; then
  79. path="/testing/${1:7}"
  80. echo "$path" >> "$expected"
  81. else
  82. path="/testing/$1"
  83. echo "$path" >> "$remaining"
  84. fi
  85. directory=$(dirname "$path")
  86. mkdir -p "$directory"
  87. touch "$path"
  88. shift
  89. done
  90. # We don't make any promise about the order files will be listed in by
  91. # weed_directory (it is currently reverse-chronological). We should sort the output
  92. # and the expected file instead of forcing tests to list files in that order (or
  93. # causing tests to fail if weed_directory's order changes)
  94. sort "$expected" > "$temp"
  95. mv "$temp" "$expected"
  96. sort "$remaining" > "$temp"
  97. mv "$temp" "$remaining"
  98. # Part one: call the function directly
  99. set +e
  100. (
  101. weed_directory \
  102. "/testing" \
  103. "$daily_threshold" \
  104. "$weekly_threshold" \
  105. "$monthly_threshold" \
  106. 2> "$temp" \
  107. | sort > "$actual"
  108. )
  109. local result="$?"
  110. set -e
  111. if [[ "$result" -ne 0 ]]; then
  112. echo 'error'
  113. ERROR_COUNT=$((ERROR_COUNT + 1))
  114. if [[ -s "$temp" ]]; then
  115. echo 'stderr:'
  116. cat "$temp"
  117. fi
  118. else
  119. set +e
  120. compare_files "$expected" "$actual"
  121. result="$?"
  122. set -e
  123. if [[ "$result" -eq 0 ]]; then
  124. # Part two: as a script with the dry-run flag (-l)
  125. set +e
  126. (
  127. "/weed.sh" \
  128. "-d" "$daily_threshold" \
  129. "-w" "$weekly_threshold" \
  130. "-m" "$monthly_threshold" \
  131. "-l" \
  132. "/testing" \
  133. 2> "$temp" \
  134. | sort > "$actual"
  135. )
  136. local result="$?"
  137. set -e
  138. if [[ "$result" -ne 0 ]]; then
  139. echo 'error'
  140. ERROR_COUNT=$((ERROR_COUNT + 1))
  141. if [[ -s "$temp" ]]; then
  142. echo 'stderr:'
  143. cat "$temp"
  144. fi
  145. else
  146. set +e
  147. compare_files "$expected" "$actual"
  148. result="$?"
  149. set -e
  150. if [[ "$result" -eq 0 ]]; then
  151. # Part three: let's try actually deleting files
  152. set +e
  153. (
  154. "/weed.sh" \
  155. "-d" "$daily_threshold" \
  156. "-w" "$weekly_threshold" \
  157. "-m" "$monthly_threshold" \
  158. "/testing" \
  159. 2> "$temp"
  160. )
  161. local result="$?"
  162. set -e
  163. if [[ "$result" -ne 0 ]]; then
  164. echo 'error'
  165. ERROR_COUNT=$((ERROR_COUNT + 1))
  166. if [[ -s "$temp" ]]; then
  167. echo 'stderr:'
  168. cat "$temp"
  169. fi
  170. else
  171. find /testing -type f | sort > "$actual"
  172. set +e
  173. compare_files "$remaining" "$actual"
  174. result="$?"
  175. set -e
  176. if [[ "$result" -eq 0 ]]; then
  177. echo 'pass'
  178. elif [[ -s "$temp" ]]; then
  179. echo 'stderr:'
  180. cat "$temp"
  181. fi
  182. fi
  183. elif [[ -s "$temp" ]]; then
  184. echo 'stderr:'
  185. cat "$temp"
  186. fi
  187. fi
  188. elif [[ -s "$temp" ]]; then
  189. echo 'stderr:'
  190. cat "$temp"
  191. fi
  192. fi
  193. rm -rf /testing
  194. }
  195. # actual tests
  196. function test_shellcheck {
  197. echo -en 'running shellcheck on scripts:\t'
  198. shellcheck /weed.sh
  199. # Test the tests too! Writing bash is hard
  200. shellcheck -x /testing-entrypoint.sh
  201. echo 'pass'
  202. }
  203. function test_empty_directory {
  204. perform_test 1 2 3
  205. }
  206. function test_single_file {
  207. perform_test 1 2 3 "backup__2021-02-02.sql"
  208. }
  209. function test_keep_everything {
  210. perform_test -1 0 0 "backup__2021-02-02.sql" "backup__2021-02-01.sql" "backup__2021-01-31.sql"
  211. }
  212. function test_keep_one {
  213. perform_test 1 0 0 "backup__2021-02-02.sql" "DELETE:backup__2021-02-01.sql" "DELETE:backup__2021-01-31.sql"
  214. }
  215. function test_weekly {
  216. # weed.sh follows ISO 8601 and uses %W for day of week, so Monday is the first day
  217. # of the week.
  218. # backup__2021-03-08.sql: Monday (keep)
  219. # backup__2021-03-07.sql: Sunday (keep)
  220. # backup__2021-02-28.sql: Sunday (keep)
  221. # backup__2021-02-22.sql: Monday (delete)
  222. # backup__2021-02-20.sql: Saturday (keep)
  223. # backup__2021-02-16.sql: Tuesday (delete)
  224. # backup__2021-02-15.sql: Monday (delete)
  225. # backup__2021-02-14.sql: Sunday (keep)
  226. # backup__2020-02-14.sql: Sunday (same week of year) (keep)
  227. perform_test 0 -1 0 \
  228. "backup__2021-03-08.sql" \
  229. "backup__2021-03-07.sql" \
  230. "backup__2021-02-28.sql" \
  231. "DELETE:backup__2021-02-22.sql" \
  232. "backup__2021-02-20.sql" \
  233. "DELETE:backup__2021-02-16.sql" \
  234. "DELETE:backup__2021-02-15.sql" \
  235. "backup__2021-02-14.sql" \
  236. "backup__2020-02-14.sql"
  237. }
  238. function test_monthly {
  239. perform_test 1 0 -1 \
  240. "backup__2021-03-08.sql" \
  241. "DELETE:backup__2021-03-07.sql" \
  242. "backup__2021-02-28.sql" \
  243. "DELETE:backup__2021-02-22.sql" \
  244. "DELETE:backup__2021-02-20.sql" \
  245. "DELETE:backup__2021-02-16.sql" \
  246. "DELETE:backup__2021-02-15.sql" \
  247. "DELETE:backup__2021-02-14.sql" \
  248. "backup__2021-01-14.sql" \
  249. "backup__2020-01-13.sql"
  250. }
  251. function test_annual {
  252. perform_test 0 0 0 \
  253. "backup__2021-03-08.sql" \
  254. "DELETE:backup__2021-03-07.sql" \
  255. "DELETE:backup__2021-02-28.sql" \
  256. "DELETE:backup__2021-02-22.sql" \
  257. "DELETE:backup__2021-02-20.sql" \
  258. "DELETE:backup__2021-02-16.sql" \
  259. "DELETE:backup__2021-02-15.sql" \
  260. "DELETE:backup__2021-02-14.sql" \
  261. "DELETE:backup__2021-01-14.sql" \
  262. "backup__2020-01-13.sql" \
  263. "backup__2019-12-31.sql" \
  264. "DELETE:backup__2019-01-13.sql"
  265. }
  266. # Will not pass while maxdepth is set to 1.
  267. function skip_test_sort_order {
  268. perform_test 0 0 1 \
  269. "a/backup__2021-03-08.sql" \
  270. "DELETE:b/backup__2021-03-07.sql" \
  271. "DELETE:a/backup__2021-02-28.sql" \
  272. "DELETE:b/backup__2021-02-22.sql" \
  273. "DELETE:a/backup__2021-02-20.sql" \
  274. "DELETE:b/backup__2021-02-16.sql" \
  275. "DELETE:a/backup__2021-02-15.sql" \
  276. "DELETE:b/backup__2021-02-14.sql" \
  277. "DELETE:a/backup__2021-01-14.sql" \
  278. "b/backup__2020-01-13.sql" \
  279. "a/backup__2019-12-31.sql" \
  280. "DELETE:b/backup__2019-01-13.sql"
  281. }
  282. function test_ignore_subdirectories {
  283. perform_test 0 0 0 "a/backup__2021-03-08.sql" "backup__2021-03-07.sql"
  284. }
  285. function test_standard {
  286. perform_test 14 4 1 \
  287. "backup__2021-03-08.sql" \
  288. "backup__2021-03-07.sql" \
  289. "backup__2021-03-06.sql" \
  290. "backup__2021-03-05.sql" \
  291. "backup__2021-03-04.sql" \
  292. "backup__2021-03-03.sql" \
  293. "backup__2021-03-02.sql" \
  294. "backup__2021-03-01.sql" \
  295. "backup__2021-02-28.sql" \
  296. "backup__2021-02-27.sql" \
  297. "backup__2021-02-26.sql" \
  298. "backup__2021-02-25.sql" \
  299. "backup__2021-02-24.sql" \
  300. "backup__2021-02-23.sql" \
  301. "DELETE:backup__2021-02-22.sql" \
  302. "backup__2021-02-21.sql" \
  303. "DELETE:backup__2021-02-20.sql" \
  304. "DELETE:backup__2021-02-19.sql" \
  305. "DELETE:backup__2021-02-18.sql" \
  306. "DELETE:backup__2021-02-17.sql" \
  307. "DELETE:backup__2021-02-16.sql" \
  308. "DELETE:backup__2021-02-15.sql" \
  309. "backup__2021-02-14.sql" \
  310. "DELETE:backup__2021-02-13.sql" \
  311. "DELETE:backup__2021-02-12.sql" \
  312. "DELETE:backup__2021-02-11.sql" \
  313. "DELETE:backup__2021-02-10.sql" \
  314. "DELETE:backup__2021-02-09.sql" \
  315. "DELETE:backup__2021-02-08.sql" \
  316. "backup__2021-02-07.sql" \
  317. "DELETE:backup__2021-02-06.sql" \
  318. "DELETE:backup__2021-02-05.sql" \
  319. "DELETE:backup__2021-02-04.sql" \
  320. "DELETE:backup__2021-02-03.sql" \
  321. "DELETE:backup__2021-02-02.sql" \
  322. "DELETE:backup__2021-02-01.sql" \
  323. "backup__2021-01-31.sql" \
  324. "DELETE:backup__2021-01-30.sql" \
  325. "DELETE:backup__2021-01-29.sql" \
  326. "DELETE:backup__2021-01-28.sql" \
  327. "DELETE:backup__2021-01-27.sql" \
  328. "DELETE:backup__2021-01-26.sql" \
  329. "DELETE:backup__2021-01-25.sql" \
  330. "DELETE:backup__2021-01-24.sql" \
  331. "DELETE:backup__2021-01-23.sql" \
  332. "DELETE:backup__2021-01-22.sql" \
  333. "DELETE:backup__2021-01-21.sql" \
  334. "DELETE:backup__2021-01-20.sql" \
  335. "DELETE:backup__2021-01-19.sql" \
  336. "DELETE:backup__2021-01-18.sql" \
  337. "DELETE:backup__2021-01-17.sql" \
  338. "DELETE:backup__2021-01-16.sql" \
  339. "DELETE:backup__2021-01-15.sql" \
  340. "DELETE:backup__2021-01-14.sql" \
  341. "DELETE:backup__2021-01-13.sql" \
  342. "DELETE:backup__2021-01-12.sql" \
  343. "DELETE:backup__2021-01-11.sql" \
  344. "DELETE:backup__2021-01-10.sql" \
  345. "DELETE:backup__2021-01-09.sql" \
  346. "DELETE:backup__2021-01-08.sql" \
  347. "DELETE:backup__2021-01-07.sql" \
  348. "DELETE:backup__2021-01-06.sql" \
  349. "DELETE:backup__2021-01-05.sql" \
  350. "DELETE:backup__2021-01-04.sql" \
  351. "DELETE:backup__2021-01-03.sql" \
  352. "DELETE:backup__2021-01-02.sql" \
  353. "DELETE:backup__2021-01-01.sql" \
  354. "backup__2020-12-31.sql"
  355. }
  356. function tests {
  357. # Run all functions named test_... in this file in definition order
  358. count=0
  359. while read -r test; do
  360. eval "$test"
  361. count=$((count + 1))
  362. done < <(awk '$1 == "function" && $2 ~ "^test_" {print $2}' "${BASH_SOURCE[0]}")
  363. echo "------------------"
  364. echo "$((count - ERROR_COUNT - FAILURE_COUNT))/$count tests passed"
  365. if [[ $((FAILURE_COUNT + ERROR_COUNT)) -gt 0 ]]; then
  366. if [[ "$ERROR_COUNT" -gt 0 ]]; then
  367. echo "$ERROR_COUNT tests errored"
  368. fi
  369. if [[ "$FAILURE_COUNT" -gt 0 ]]; then
  370. echo "$FAILURE_COUNT tests failed"
  371. fi
  372. echo 'failure'
  373. else
  374. echo 'success'
  375. fi
  376. }
  377. if [ "${BASH_SOURCE[0]}" -ef "$0" ]; then
  378. trap 'echo -e "\\terror (in ${FUNCNAME[1]} ${BASH_SOURCE[1]}:${BASH_LINENO[1]})\naborting"' EXIT
  379. tests
  380. trap - EXIT
  381. if [[ $((FAILURE_COUNT + ERROR_COUNT)) -gt 0 ]]; then
  382. exit 1
  383. fi
  384. fi