From 1475f605aa8fb0efa2aed3f6a8e828939e6fbffd Mon Sep 17 00:00:00 2001 From: Tobias Reisinger Date: Thu, 28 May 2020 02:12:39 +0200 Subject: [PATCH] add: foreign key support in database add: more tests fix: bad tag handling when finding 0 in column --- helpers/bind_server.c | 52 ------- helpers/get_port.c | 24 ---- helpers/open_discovery_socket.c | 57 -------- include/helpers.h | 18 --- main.c | 2 + models/junction_tag.c | 29 +++- models/relay.c | 2 +- sql/migration_0.sql | 15 +- .../controller_relays_basic.tavern.yaml | 129 +++--------------- tests/tavern_tests/tags.tavern.yaml | 53 +++++++ .../validate_controller.cpython-38.pyc | Bin 0 -> 1907 bytes .../__pycache__/validate_relay.cpython-38.pyc | Bin 0 -> 2767 bytes tests/tavern_utils/validate_controller.py | 41 ++++++ tests/tavern_utils/validate_relay.py | 68 ++++++++- 14 files changed, 214 insertions(+), 276 deletions(-) delete mode 100644 helpers/bind_server.c delete mode 100644 helpers/get_port.c delete mode 100644 helpers/open_discovery_socket.c create mode 100644 tests/tavern_utils/__pycache__/validate_controller.cpython-38.pyc create mode 100644 tests/tavern_utils/__pycache__/validate_relay.cpython-38.pyc create mode 100644 tests/tavern_utils/validate_controller.py diff --git a/helpers/bind_server.c b/helpers/bind_server.c deleted file mode 100644 index b796d5b..0000000 --- a/helpers/bind_server.c +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -int -helper_bind_tcp_server(char* addr, uint16_t port, int max_client_backlog) -{ - char port_str[6]; - sprintf(port_str, "%d", port); - - struct addrinfo hints, *res; - int fd; - int status; - - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; - - if ((status = getaddrinfo(addr, port_str, &hints, &res)) != 0) - { - LOG_ERROR("getaddrinfo: %s\n", gai_strerror(status)); - return -1; - } - - fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - - if ((status = bind(fd, res->ai_addr, res->ai_addrlen)) == -1) - { - LOG_ERROR("error binding socket: %s\n", strerror(errno)); - freeaddrinfo(res); - return -1; - } - - if ((status = listen(fd, max_client_backlog)) == -1) - { - LOG_ERROR("error setting up listener: %s\n", strerror(errno)); - freeaddrinfo(res); - return -1; - } - - freeaddrinfo(res); - - return fd; -} diff --git a/helpers/get_port.c b/helpers/get_port.c deleted file mode 100644 index 58c9ce8..0000000 --- a/helpers/get_port.c +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include - -uint16_t -helper_get_port(int sock) -{ - struct sockaddr_in sin; - socklen_t len = sizeof(sin); - if (getsockname(sock, (struct sockaddr *)&sin, &len) == -1) - { - LOG_ERROR("could not get socket name for port: %s\n", strerror(errno)); - return 0; - } - else - { - return ntohs(sin.sin_port); - } -} diff --git a/helpers/open_discovery_socket.c b/helpers/open_discovery_socket.c deleted file mode 100644 index 855bc0a..0000000 --- a/helpers/open_discovery_socket.c +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include - -int -helper_open_discovery_socket(uint16_t discovery_port) -{ - struct addrinfo hints, *res; - int fd, status; - - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_INET; // use ipv4 - hints.ai_socktype = SOCK_DGRAM; //set socket flag - hints.ai_flags = AI_PASSIVE; // get my IP - - char discovery_port_str[6]; - sprintf(discovery_port_str, "%u", discovery_port); - - //get connection info for our computer - if ((status = getaddrinfo(NULL, discovery_port_str, &hints, &res)) != 0) - { - LOG_FATAL("getaddrinfo: %s\n", gai_strerror(status)); - freeaddrinfo(res); - exit(EXIT_FAILURE); - } - - //creating socket - fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - int yes = 1; - - // lose the pesky "Address already in use" error message - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) - { - LOG_FATAL("setsockopt: %s\n", strerror(errno)); - freeaddrinfo(res); - exit(EXIT_FAILURE); - } - - if (bind(fd, res->ai_addr, res->ai_addrlen) == -1) - { - LOG_FATAL("bind: %s\n", strerror(errno)); - freeaddrinfo(res); - exit(EXIT_FAILURE); - } - - freeaddrinfo(res); - - LOG_INFO("opened discovery socket on port %u\n", discovery_port); - - return fd; -} diff --git a/include/helpers.h b/include/helpers.h index f382679..45af161 100644 --- a/include/helpers.h +++ b/include/helpers.h @@ -8,24 +8,6 @@ int helper_connect_tcp_server(char* host, uint16_t port); -int -helper_bind_tcp_server(char* addr, uint16_t port, int max_client_backlog); - -uint16_t -helper_get_port(int sock); - -/** - * @brief Open socket for discovery - * - * Will exit program when unable to open socket. - * - * @param discovery_port Port number to listen on for discovery broadcasts - * - * @return Open socket to accept discovery broadcasts on - */ -int -helper_open_discovery_socket(uint16_t discovery_port); - void helper_parse_cli(int argc, const char **argv, config_t *config); diff --git a/main.c b/main.c index 24b7cab..c5b1a12 100644 --- a/main.c +++ b/main.c @@ -84,6 +84,8 @@ main(int argc, const char** argv) terminate(1); } + sqlite3_exec(global_database, "PRAGMA foreign_keys = ON", 0, 0, 0); + /******************** INIT ROUTER ********************/ diff --git a/models/junction_tag.c b/models/junction_tag.c index 9beddc1..c4c6a33 100644 --- a/models/junction_tag.c +++ b/models/junction_tag.c @@ -15,8 +15,24 @@ junction_tag_insert(int tag_id, int relay_id, int schedule_id) sqlite3_prepare_v2(global_database, "INSERT INTO junction_tag(tag_id, schedule_id, relay_id) values (?1, ?2, ?3);", -1, &stmt, NULL); sqlite3_bind_int(stmt, 1, tag_id); - sqlite3_bind_int(stmt, 2, schedule_id); - sqlite3_bind_int(stmt, 3, relay_id); + + if(schedule_id) + { + sqlite3_bind_int(stmt, 2, schedule_id); + } + else + { + sqlite3_bind_null(stmt, 2); + } + + if(relay_id) + { + sqlite3_bind_int(stmt, 3, relay_id); + } + else + { + sqlite3_bind_null(stmt, 3); + } rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) @@ -46,10 +62,13 @@ get_ids(sqlite3_stmt *stmt) if (s == SQLITE_ROW) { new_id = sqlite3_column_int(stmt, 0); - row++; + if(new_id != 0) // found row for other target (relay <> schedule) + { + row++; - ids = (int*)realloc(ids, sizeof(int) * (row + 1)); - ids[row - 1] = new_id; + ids = (int*)realloc(ids, sizeof(int) * (row + 1)); + ids[row - 1] = new_id; + } } else { diff --git a/models/relay.c b/models/relay.c index 3e364dd..75632e6 100644 --- a/models/relay.c +++ b/models/relay.c @@ -236,7 +236,7 @@ relay_to_json(relay_t *relay) controller_t *controller = controller_get_by_id(relay->controller_id); if(!controller) { - LOG_DEBUG("failed to get controller\n"); + LOG_WARN("failed to get controller\n"); cJSON_Delete(json); return NULL; } diff --git a/sql/migration_0.sql b/sql/migration_0.sql index cd17b3d..36246d1 100644 --- a/sql/migration_0.sql +++ b/sql/migration_0.sql @@ -31,6 +31,7 @@ create table relays controller_id INTEGER NOT NULL REFERENCES controllers (id) + ON DELETE CASCADE ); create table schedules @@ -59,11 +60,14 @@ create table junction_tag ( tag_id INTEGER NOT NULL - REFERENCES tags (id), + REFERENCES tags (id) + ON DELETE CASCADE, relay_id INTEGER - REFERENCES relays (id), + REFERENCES relays (id) + ON DELETE CASCADE, schedule_id INTEGER REFERENCES schedules (id) + ON DELETE CASCADE ); create table junction_relay_schedule @@ -71,11 +75,12 @@ create table junction_relay_schedule weekday SMALLINT NOT NULL, relay_id INTEGER - NOT NULL - REFERENCES relays (id), + REFERENCES relays (id) + ON DELETE CASCADE, schedule_id INTEGER - NOT NULL + DEFAULT 1 REFERENCES schedules (id) + ON DELETE SET DEFAULT ); INSERT INTO schedules (uid, name, periods) VALUES (x'6f666600000000000000000000000000', 'off', x'00'); diff --git a/tests/tavern_tests/controller_relays_basic.tavern.yaml b/tests/tavern_tests/controller_relays_basic.tavern.yaml index ee59c11..df2bed1 100644 --- a/tests/tavern_tests/controller_relays_basic.tavern.yaml +++ b/tests/tavern_tests/controller_relays_basic.tavern.yaml @@ -7,18 +7,12 @@ stages: url: "http://localhost:5000/api/v1/controllers/discover/" response: status_code: 200 - json: - - id: !anystr - name: !anystr - relay_count: !anyint - relays: !anystr - active: !anybool - port: !anyint - ip: !anystr - relays: !anylist + verify_response_with: + function: validate_controller:multiple save: json: returned_id: "[0].id" + returned_relay_count: "[0].relay_count" - name: "[controller_relays_basic] get controller relays, check length" request: @@ -26,71 +20,11 @@ stages: url: "http://localhost:5000/api/v1/controllers/{returned_id}/relays" response: status_code: 200 - json: - - name: !anystr - number: 0 - controller_id: "{returned_id}" - active_schedule: - id: !anystr - name: !anystr - periods: !anylist - tags: !anylist - schedules: !anylist - tags: !anylist - - name: !anystr - number: 1 - controller_id: "{returned_id}" - active_schedule: !anydict - schedules: !anylist - tags: !anylist - - name: !anystr - number: 2 - controller_id: "{returned_id}" - active_schedule: !anydict - schedules: !anylist - tags: !anylist - - name: !anystr - number: 3 - controller_id: "{returned_id}" - active_schedule: !anydict - schedules: !anylist - tags: !anylist - - name: !anystr - number: 4 - controller_id: "{returned_id}" - active_schedule: !anydict - schedules: !anylist - tags: !anylist - - name: !anystr - number: 5 - controller_id: "{returned_id}" - active_schedule: !anydict - schedules: !anylist - tags: !anylist - - name: !anystr - number: 6 - controller_id: "{returned_id}" - active_schedule: !anydict - schedules: !anylist - tags: !anylist - - name: !anystr - number: 7 - controller_id: "{returned_id}" - active_schedule: !anydict - schedules: !anylist - tags: !anylist - - name: !anystr - number: 8 - controller_id: "{returned_id}" - active_schedule: !anydict - schedules: !anylist - tags: !anylist - - name: !anystr - number: 9 - controller_id: "{returned_id}" - active_schedule: !anydict - schedules: !anylist - tags: !anylist + verify_response_with: + function: validate_relay:multiple + function: validate_relay:relay_count + extra_kwargs: + relay_count: !int "{returned_relay_count:d}" - name: "[controller_relays_basic] get controller relays, check length" request: @@ -98,42 +32,11 @@ stages: url: "http://localhost:5000/api/v1/controllers/{returned_id}/relays/5" response: status_code: 200 - json: - name: !anystr - number: 5 - controller_id: "{returned_id}" - active_schedule: - id: !anystr - name: !anystr - periods: !anylist - tags: !anylist - schedules: - - id: !anystr - name: !anystr - periods: !anylist - tags: !anylist - - id: !anystr - name: !anystr - periods: !anylist - tags: !anylist - - id: !anystr - name: !anystr - periods: !anylist - tags: !anylist - - id: !anystr - name: !anystr - periods: !anylist - tags: !anylist - - id: !anystr - name: !anystr - periods: !anylist - tags: !anylist - - id: !anystr - name: !anystr - periods: !anylist - tags: !anylist - - id: !anystr - name: !anystr - periods: !anylist - tags: !anylist - tags: !anylist + verify_response_with: + function: validate_relay:single + function: validate_relay:check_controller_id + extra_kwargs: + name: "{returned_id}" + function: validate_relay:check_number + extra_kwargs: + number: 5 diff --git a/tests/tavern_tests/tags.tavern.yaml b/tests/tavern_tests/tags.tavern.yaml index f3576aa..57c18a1 100644 --- a/tests/tavern_tests/tags.tavern.yaml +++ b/tests/tavern_tests/tags.tavern.yaml @@ -44,3 +44,56 @@ stages: id: "{returned_id}" name: "{returned_name}" periods: "{returned_periods}" + +- name: "[tags] discover controllers" + request: + method: POST + url: "http://localhost:5000/api/v1/controllers/discover/" + response: + status_code: 200 + verify_response_with: + function: validate_controller:multiple + save: + json: + returned_id: "[0].id" + +- name: "[tags] set relay tag" + request: + method: PUT + url: "http://localhost:5000/api/v1/controllers/{returned_id}/relays/3" + json: + tags: + - "test_tag_1" + response: + status_code: 200 + verify_response_with: + function: validate_relay:single + function: validate_relay:check_controller_id + extra_kwargs: + name: "{returned_id}" + function: validate_relay:check_number + extra_kwargs: + number: 3 + function: validate_relay:check_tag + extra_kwargs: + tag: "{tavern.request_vars.json.tags[0]}" + save: + json: + returned_name: "name" + returned_number: "number" + returned_tag: "tags[0]" + +- name: "[tags] get relay, check name and number" + request: + method: GET + url: "http://localhost:5000/api/v1/relays/tag/{returned_tag}" + response: + status_code: 200 + verify_response_with: + function: validate_relay:multiple + function: validate_relay:find + extra_kwargs: + name: "{returned_name}" + number: !int "{returned_number:d}" + controller_id: "{returned_id}" + tag: "{returned_tag}" diff --git a/tests/tavern_utils/__pycache__/validate_controller.cpython-38.pyc b/tests/tavern_utils/__pycache__/validate_controller.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..419f175c2dd8841e067cda593d862bcc66da9797 GIT binary patch literal 1907 zcmbtU&yO256drqKlG$vM(gMp;pv;k6NL2}m145ue#R264NL3Jo>DZficQTW;ovk)X zCDiSyf5qn5|AZ5Nz+5?T;|eFf=gE)BmKz@VdHlWi?9cXl?}tvO<)Quk*Y7|7*zmkR zXi{%JCSRbNM-ajjEc1rd$c9XC;lJ>PTm&M-=!=GEVhlti4lstIB@Qt*L|3#$=LH)! z1^dqH-TD)2d4rVEux=bCgLEAOXIpV6+&HsOB_earbr*9S|m zW6*%-;i~cgf>mBx|2aEN8D^K4-to`$Ce^N|k-xg0)V)`>(oXXUizMPJe`O~DI78*)v09jmz z&PQ{rGSlDgxLf$+*`@Aa$$;Yc4ur>!>Ep*NW?goNwV2cI1wSQ%kNyzdya7=K`O3RO zc)1{AgC2K&WpHYWyvNE&OEW8STnwF{669Jos`Jvnp{(+leiL$UL&CQk-p6FKq06-+ z#5l5|%7K-K3}_<*f+aBKo!@hW_+W9gVX%8udXrwL>G1kCi31W%5_Bmz16_&klDI)T zn$9z;W|<_i2BiqZk64@OxA2#`gAj(cNB$lFq^h@C7mFpO-(AbAV&SOz15GZAySrDS z3`{37J&R8gl}Yi+ujo<*a&$LL$?-L&&JzK94CUsEkMRG0S<;Iv-mkHAC7{kzzYUWO zH!{D;tyOU&$M>M#uzLspDcNrtz0kJY=9pA>h^PMknZIPhJM;AelstCLsRYx^`kR>x zgJ{W})#D|1ICoZ$*H)NSR#p58-<;Z@D%9>x!s<;laeB0WW>m!!lsA;f+|?w+6NS<+ zGB}%z1_{H@WrPXq(*T6|@$2conV9UA~02!fSjPt;6el z1#Oi#_$peLukm%XOMHuO@XhC}UE}OKqj~2Q_Apujqh|bAXe%gRp~yNUnK3kO7-zRE zGp|j#n_1kvW4v^e3R_?b1Jr{bv zK(D6smXsFoYsQz)O!+G$b&{0$*T#?DNbyoT{0aHWWCyi&|MxNL#(4FAptCwz11Q$m z9+}WG)u~=XzDDx?Yi__|>#f2`2InWC$RCO@_OCoW@*?RaY3BKf7bRKP4Mkxm{&|@1 zjkVC9=y);{QPM3|f;7oQ8pomNL_B{_tImOobK0wZkVThaM+Uu+58|+#wE$1&@hHgJ zOQpKZALv~sz*IAiWcI3xYtNhWI$qDe3_ahA!=#(_yg?H72Y&2jsrPCA$-Juch`t{s zP%pRep7o-jH{FLA6?W!#WxlItQ-3Na9{K)^WV$327gy7^Y}SfeB%?%TeiDR*)eW=4 z!c7#7FQp7c7NyBILZqUwpkLUE!uq8jN8Hb*#w;40%TPq8R~;3sbuEGMHmxkDa$^QHHpqHM92U&U&`SRdGq))>jlLz5>*B?Cd4}w%c7)n4Hj46`N zAd6yoFrlE<-oLmK4Xj2vK<=S3*e<>1E?Yy{V-2>!>gY9?(whrh3StUg6}W5YXji{A zt`Stqd0DGzs(FR|RHjLj6|M;7MVi1ubO>GDL9cMM0k+YXCMtu8UDR_M5P*l$PzNCl&m8&t07~-LY{-yYb~97cFNxlp5bgPJ);vS?WHPT$9RvS~p#P=aCi9JC7ZHZCn<(?)spNZedkftm$+|&%`R`_6sDqZIs zRXXM*m_Ei}_OcN?8zXaQ(xr=rj^=)Yn{poxU{R1M2e4a$T>f#sLwS-uN2hQ+4rkQ| z`bZnr(($ti)uLW{bq7-x53!^V0m+BZmE<)B5|E2mc<2Z8pHMy|o%}cahQTzTnEHcI zIpC*JWoUBa%o2wJhT*!{|#sz1Gg3ag4-iU zN$|%3L#>Uf!|Kq6p)R>Ia{8A;N51H9X`^vl-XJekCnj@s%n0RWbdish3BRBC=ByFv zW6-pV$_2jO*-tpHJ(Ql)H_`c$$J_enrOBFO#J6{_ho926V% c1#mw*Pw5BabNa4G!g24WyWv`H1x3Hk|HPq!P5=M^ literal 0 HcmV?d00001 diff --git a/tests/tavern_utils/validate_controller.py b/tests/tavern_utils/validate_controller.py new file mode 100644 index 0000000..7ad0e9c --- /dev/null +++ b/tests/tavern_utils/validate_controller.py @@ -0,0 +1,41 @@ +import json +import validate_relay + +def _verify_single(controller): + assert isinstance(controller.get("id"), str), "controller id is not a string" + assert isinstance(controller.get("name"), str), "controller name is not a string" + assert isinstance(controller.get("relay_count"), int), "controller relay_count is not an integer" + + assert isinstance(controller.get("relays"), list), "controller relays is not a list" + assert len(controller.get("relays")) == controller.get("relay_count"), "controller relay have a length unequal to relay_count" + for relay in controller.get("relays"): + assert isinstance(relay, dict), "controller relays contain a relay which is not a dict" + validate_relay._verify_single(relay) + assert relay.get("controller_id") == controller.get("id") + +def single(response): + _verify_single(response.json()) + +def multiple(response): + assert isinstance(response.json(), list), "response is not a list" + for controller in response.json(): + _verify_single(controller) + +def check_id(response, id): + assert response.json().get("id") == id, "controller id check failed" + +def check_name(response, name): + assert response.json().get("name") == name, "controller name check failed" + +def find(response, id=None, name=None): + for controller in response.json(): + if id != None and id != schedule.get("id"): + print(schedule.get("id")) + continue + + if name != None and name != schedule.get("name"): + print(schedule.get("name")) + continue + return + assert False, "controller not found in list" + diff --git a/tests/tavern_utils/validate_relay.py b/tests/tavern_utils/validate_relay.py index 0ae475a..39f3811 100644 --- a/tests/tavern_utils/validate_relay.py +++ b/tests/tavern_utils/validate_relay.py @@ -1,2 +1,68 @@ +import json +import validate_schedule + +def _verify_single(relay): + assert isinstance(relay.get("number"), int), "relay number is not an integer" + assert isinstance(relay.get("name"), str), "relay name is not a string" + assert isinstance(relay.get("controller_id"), str), "relay controller_id is not a string" + + assert isinstance(relay.get("active_schedule"), dict), "relay active_schedule is not a dict" + validate_schedule._verify_single(relay.get("active_schedule")) + + assert isinstance(relay.get("schedules"), list), "relay schedules is not a list" + assert len(relay.get("schedules")) == 7, "relay schedule have a length unequal to 7" + for schedule in relay.get("schedules"): + assert isinstance(relay, dict), "relay schedules contain a schedule which is not a dict" + validate_schedule._verify_single(schedule) + + assert isinstance(relay.get("tags"), list), "relay tags is not a list" + for tag in relay.get("tags"): + assert isinstance(tag, str), "relay tags contain a tag which is not a string" + def single(response): - assert response.json().get("number") >= 0 + _verify_single(response.json()) + +def multiple(response): + assert isinstance(response.json(), list), "response is not a list" + for relay in response.json(): + _verify_single(relay) + +def relay_count(response, relay_count): + assert len(response.json()) == relay_count, "response has invalid length" + +def check_number(response, number): + assert response.json().get("number") == number, "relay number check failed" + +def check_name(response, name): + assert response.json().get("name") == name, "relay name check failed" + +def check_controller_id(response, controller_id): + assert response.json().get("controller_id") == controller_id, "relay controller_id check failed" + +def check_tag(response, tag): + for response_tag in response.json().get("tags"): + if response_tag == tag: + return + assert False, "tag not found in relay," + +def find(response, name=None, number=None, controller_id=None, tag=None): + print(response.json()) + for relay in response.json(): + if number != None and number != relay.get("number"): + continue + + if name != None and name != relay.get("name"): + continue + + if controller_id != None and controller_id != relay.get("controller_id"): + continue + + if tag != None: + found_in_response = False + for response_tag in relay.get("tags"): + if response_tag == tag: + found_in_response = True + if not found_in_response: + continue + return + assert False, "relay not found in list"