;;;; tests/main.lisp (in-package #:ritherdon-rest-tests) (def-suite all-tests :description "The master suite of all ritherdon-rest tests.") (in-suite all-tests) ;;; HAD TO REVERT BACK TO FULL PACKAGE NAMES ;;; ======================================== ;;; BELOW EXPLAINS HOW YOU CAN OMIT PACKAGE NAMES FROM FUNCTION ;;; CALLS. BUT, I'VE ADDED THE 'RATIFY' PACKAGE SINCE THEN AND I WAS ;;; GETTING NAMING CONFLICTS (WITH 'TEST'). SO, I'VE HAD TO USE THE ;;; FULL 'FIVEAM:TEST' FUNCTION CALLS. I'VE KEPT THE NOT FOR FUTURE ;;; REFERENCE AND ADDED THIS AS EXTRA CONTEXT FOR WHEN/HOW TO OMIT ;;; PACKAGE NAMES. ;;; THIS BIT KINDA OUT-OF-DATE... ;;; These two examples show the 'full' call to the fiveAm test ;;; functions. This is just for reference. The 'namespace' is already ;;; 'imported' in 'package.lisp'. ;;; (fiveam:test sum-1 ;;; (fiveam:is (= 3 (+ 1 2)))) ;;; (fiveam:run!) ;;; How you would normally create the tests -- with fiveAM already ;;; set-up in 'package.lisp' and not needing to be explicit about it ;;; here. This is similar to 'using static' in C#. (defun test-quasi() (run! 'all-tests)) ;;; REST API Used Here is Temporary ;;; =============================== ;;; This REST API is a temporary one -- it's part of an artwork for ;;; the Return to Ritherdon project. The intention is to retire the ;;; API when the exhibition ends. So, these tests might fail because ;;; the API is no longer active. ;;; DEVICE STATUS TESTS ;;; =================== ;;; These tests are checking the 'shape' of the data returned by the ;;; HTTP (REST) request matches what is expected. These tests are ;;; expecting JSON data which looks similar to: ;;; ((:ID . 79) (:STATUS . "off") (:TIME . "2021-07-01T16:00:001")). ;;; ID: The row Id. in the database. ;;; STATUS: The current power state of the device (I.E. on or off). ;;; TIME: The timestamp when the status was recorded. ;;; Usually, these updates are logged when each device is either just ;;; finished powering up or as they are about to power down. (fiveam:test factory-1-status-data :description "Validates the (status) data produced by `FACTORY1'." (let ((data (ritherdon-rest:parse-request "/status/latest/1"))) ;; Data should look something like: ;; ((:ID . 79) (:STATUS . "off") (:TIME . "2021-07-01T16:00:001")). (is (= 3 (length data))) (is (equal t (consp data))) (is (equal ':id (first (nth 0 data)))) (is (equal ':status (first (nth 1 data)))) (is (equal ':time (first (nth 2 data)))) (is (equal t (typep (cdr (car data)) 'integer))) ; row :id number (is (> (cdr (car data)) 0)) (is (equal t (typep (cdr (car (cdr data))) 'string))) ; :status value (is (equal t (or (string-equal (cdr (car (cdr data))) "off") (string-equal (cdr (car (cdr data))) "on")))))) (fiveam:test factory-2-status-data :description "Validates the (status) data produced by `FACTORY2'." (let ((data (ritherdon-rest:parse-request "/status/latest/2"))) ;; Data should look something like: ;; ((:ID . 79) (:STATUS . "off") (:TIME . "2021-07-01T16:00:001")). (is (= 3 (length data))) (is (equal t (consp data))) (is (equal ':id (first (nth 0 data)))) (is (equal ':status (first (nth 1 data)))) (is (equal ':time (first (nth 2 data)))) (is (equal t (typep (cdr (car data)) 'integer))) ; row :id number (is (> (cdr (car data)) 0)) (is (equal t (typep (cdr (car (cdr data))) 'string))) ; :status value (is (equal t (or (string-equal (cdr (car (cdr data))) "off") (string-equal (cdr (car (cdr data))) "on")))))) (fiveam:test factory-3-status-data :description "Validates the (status) data produced by `FACTORY3'." (let ((data (ritherdon-rest:parse-request "/status/latest/3"))) ;; Ritherdon has a third welding booth but it was not included in ;; the art project so no light sensor was installed. Therefore, ;; this should always return the 'default/initial/seed' data ;; readings. ;; Data should still take the same shape as factory1 and factory 2: ;; ((:ID . 1) (:STATUS . "off") (:TIME . "2021-04-26T20:42:19.400868")). (is (= 3 (length data))) (is (equal t (consp data))) (is (equal ':id (first (nth 0 data)))) (is (equal ':status (first (nth 1 data)))) (is (equal ':time (first (nth 2 data)))) (is (equal t (typep (cdr (car data)) 'integer))) ; row :id number (is (> (cdr (car data)) 0)) (is (equal t (typep (cdr (car (cdr data))) 'string))) ; :status value (is (equal t (or (string-equal (cdr (car (cdr data))) "off") (string-equal (cdr (car (cdr data))) "on")))))) (fiveam:test gallery-1-status-data :description "Validates the (status) data produced by `GALLERY1'." ;; Data should still take the same shape as others: ;; ((:ID . 1) (:STATUS . "off") (:TIME . "2021-04-26T20:42:19.400868")) (let ((data (ritherdon-rest:parse-request "/status/latest/4"))) (is (= 3 (length data))) (is (equal t (consp data))) (is (equal ':id (first (nth 0 data)))) (is (equal ':status (first (nth 1 data)))) (is (equal ':time (first (nth 2 data)))) (is (equal t (typep (cdr (car data)) 'integer))) ; row :id number (is (> (cdr (car data)) 0)) (is (equal t (typep (cdr (car (cdr data))) 'string))) ; :status value (is (equal t (or (string-equal (cdr (car (cdr data))) "off") (string-equal (cdr (car (cdr data))) "on")))))) (fiveam:test gallery-2-status-data :description "Validates the (status) data produced by `GALLERY2'." ;; Data should still take the same shape as others: ;; ((:ID . 1) (:STATUS . "off") (:TIME . "2021-04-26T20:42:19.400868")) (let ((data (ritherdon-rest:parse-request "/status/latest/5"))) (is (= 3 (length data))) (is (equal t (consp data))) (is (equal ':id (first (nth 0 data)))) (is (equal ':status (first (nth 1 data)))) (is (equal ':time (first (nth 2 data)))) (is (equal t (typep (cdr (car data)) 'integer))) ; row :id number (is (> (cdr (car data)) 0)) (is (equal t (typep (cdr (car (cdr data))) 'string))) ; :status value (is (equal t (or (string-equal (cdr (car (cdr data))) "off") (string-equal (cdr (car (cdr data))) "on")))))) (fiveam:test gallery-3-status-data :description "Validates the (status) data produced by `GALLERY3'." ;; This corresponds to 'factory3' -- no light sensor in welding ;; booth 3 in Ritherdon. Therefore, no data to send. Because of ;; this, the data returned here should be the 'seed data' unless ;; I've manually updated it as part of another test. ;; Data should still take the same shape as others: ;; ((:ID . 1) (:STATUS . "off") (:TIME . "2021-04-26T20:42:19.400868")). (let ((data (ritherdon-rest:parse-request "/status/latest/6"))) (is (= 3 (length data))) (is (equal t (consp data))) (is (equal ':id (first (nth 0 data)))) (is (equal ':status (first (nth 1 data)))) (is (equal ':time (first (nth 2 data)))) (is (equal t (typep (cdr (car data)) 'integer))) ; row :id number (is (> (cdr (car data)) 0)) (is (equal t (typep (cdr (car (cdr data))) 'string))) ; :status value (is (equal t (or (string-equal (cdr (car (cdr data))) "off") (string-equal (cdr (car (cdr data))) "on")))))) ;;; LIGHT READINGS TESTS ;;; ==================== ;;; Theses tests check the 'shape' of the light meter readings ;;; returned by the HTTP (REST) requests. They make sure the data ;;; matches what's expected. These tests are expecting JSON data along ;;; the lines of: ;;; ((:ID . 1855109) (:READING . 16) (:TIME . "2021-07-02T13:42:16")) ;;; ID: The row Id. in the database. ;;; READING: The amount of light recorded. ;;; TIME: The timestamp when the reading was taken. ;;; Because of how the light meters work, they do not always take ;;; light meter readings at consistent intervals. But, both devices ;;; aim to take a reading for as long as they are on. Also, Ritherdon ;;; has three welding booths and the system has the capacity to record ;;; all three booths. The Return to Ritherdon project decided on only ;;; recording two of the booths so 'factory3' should only return it ;;; seed data -- unless I've manually updated it (for another test ;;; most likely). (fiveam:test factory-1-reading-data ; Had to add fiveam here because ; of naming conflict with ratify ; package. Comment at top of file ; explaining further. :description "Validates the (light reading) data produced by `FACTORY1'." ;; ((:ID . 1855109) (:READING . 16) (:TIME . "2021-07-02T13:42:16")) (let ((data (ritherdon-rest:parse-request "/readings/latest/1"))) (is-true (= 3 (length data))) (is-true (consp data)) (is (equal ':id (caar data))) (is (equal ':reading (caadr data))) (is (equal ':time (caaddr data))) (is-true (> (cdar data) 0)) ; Db Id numbers start at 0. (is-true (typep (cdadr data) 'integer)) ; reading can be <=> 0. ;; Returns the date-time if data could be parsed. Returns `NIL' if ;; it could not. Don't need to check if string. If system can't ;; parse it doesn't matter what form the date-time is in. (is-false (equal (ratify:datetime-p (cdaddr data)) nil)))) ; time value. (fiveam:test factory-2-reading-data :description "Validates the (light reading) data produced by `FACTORY2'." ;; ((:ID . 1855109) (:READING . 16) (:TIME . "2021-07-02T13:42:16")) (let ((data (ritherdon-rest:parse-request "/readings/latest/2"))) (is-true (= 3 (length data))) (is-true (consp data)) (is (equal ':id (caar data))) (is (equal ':reading (caadr data))) (is (equal ':time (caaddr data))) (is-true (> (cdar data) 0)) ; Db Id numbers start at 0. (is-true (typep (cdadr data) 'integer)) ; reading can be <=> 0. ;; Returns the date-time if data could be parsed. Returns `NIL' if ;; it could not. Don't need to check if string. If system can't ;; parse it doesn't matter what form the date-time is in. (is-false (equal (ratify:datetime-p (cdaddr data)) nil)))) ; time value. (fiveam:test factory-3-reading-data :description "Validates the (light reading) data produced by `FACTORY3'." ;; ((:ID . 1855109) (:READING . 16) (:TIME . "2021-07-02T13:42:16")) (let ((data (ritherdon-rest:parse-request "/readings/latest/3"))) (is-true (= 3 (length data))) (is-true (consp data)) (is (equal ':id (caar data))) (is (equal ':reading (caadr data))) (is (equal ':time (caaddr data))) (is-true (> (cdar data) 0)) ; Db Id numbers start at 0. (is-true (typep (cdadr data) 'integer)) ; reading can be <=> 0. ;; This device is not active so the seed data should be ;; returned. This data should not be produce `NIL' when Ratify ;; tries to parse it (not a valid timestamp). If the timestamp can ;; be parsed it most likely means I've updated the latest reading ;; data (manually most likely) which should result in this failing ;; because Ratify will return the parsed timestamp and not `NIL'. (is (equal (ratify:datetime-p (cdaddr data)) nil)))) ; time value. (fiveam:test all-device-status-data :description "Validates the status data produced when all status data for all devices is requested in the same API call." (let ((data (ritherdon-rest:parse-request "/status/latest"))) (is (= 6 (length data))) (is-true (consp data)) (is (equal ':|DEVICE 1| (car (first data)))) (is (equal ':|DEVICE 2| (car (second data)))) (is (equal ':|DEVICE 3| (car (third data)))) (is (equal ':|DEVICE 4| (car (fourth data)))) (is (equal ':|DEVICE 5| (car (fifth data)))) ;; if 6 not last a big change has occurred. (is (equal ':|DEVICE 6| (caar (last data))))))