nx_moonraker_client_test.nx source
↩ module page · 132 lines · 5135 B
1// nx_moonraker_client_test.nx -- verify URL building + JSON body +
2// HTTP request assembly + status parsing against known-byte expected
3// outputs.
4//
5// Closed-form invariants (every byte hand-verified):
6// (a) Filename validation: "test.gcode" safe; quotes/backslash refused.
7// (b) URL build: base "http://10.0.0.5:7125" + path "/printer/info"
8// -> "http://10.0.0.5:7125/printer/info" (33 bytes).
9// (c) JSON print-start body for "voron.gcode" ->
10//
11// (28 bytes including the {} and quotes).
12// (d) POST request bytes start with "POST /printer/print/start HTTP/1.1\r\n"
13//
14// contain "Host: 10.0.0.5:7125\r\n",
15//
16// contain "Content-Type: application/json\r\n",
17//
18// contain "Content-Length: 28\r\n\r\n", and end with the JSON body.
19// (e) GET request bytes start with "GET /printer/info HTTP/1.1\r\n".
20// (f) GET request with API key contains "X-Api-Key: secret\r\n".
21// (g) Status-line parser: "HTTP/1.1 200 OK\r\n" -> 200.
22// (h) Status-line parser: "HTTP/1.1 503 Service" -> 503.
23// (i) Status-line parser malformed: "HTTPx/1.1 200 OK" -> -1.
24//
25// expect_exit: 0
26// license_tier: ORIGINAL
27
28import "nx_syscalls.nx"
29import "nx_moonraker_client.nx"
30
31func smoke_buf_eq(buf: *u8, expected: *u8, n: i64) -> i64 {
32 var i: i64 = 0
33 while i < n {
34 if (buf[i] as i64) != (expected[i] as i64) { return 0 }
35 i = i + 1
36 }
37 return 1
38}
39
40func smoke_contains(buf: *u8, len: i64, needle: *u8) -> i64 {
41 var nlen: i64 = 0
42 while needle[nlen] != 0 { nlen = nlen + 1 }
43 if len < nlen { return 0 }
44 var i: i64 = 0
45 while i <= len - nlen {
46 var j: i64 = 0
47 var matched: i64 = 1
48 while j < nlen {
49 if buf[i + j] != needle[j] { matched = 0; j = nlen }
50 j = j + 1
51 }
52 if matched == 1 { return 1 }
53 i = i + 1
54 }
55 return 0
56}
57
58func cstr_len(s: *u8) -> i64 {
59 var i: i64 = 0
60 while s[i] != 0 { i = i + 1 }
61 return i
62}
63
64func main() -> i64 {
65 let buf: *u8 = sys_mmap(4096)
66
67 // --- (a) Filename validation ---
68 let safe_fn: *u8 = "test.gcode"
69 if nx_mr_filename_is_safe(safe_fn, 10) != 1 { return 10 }
70 let quote_fn: *u8 = "bad\"name.gcode"
71 if nx_mr_filename_is_safe(quote_fn, 14) != 0 { return 11 }
72 let back_fn: *u8 = "bad\\name"
73 if nx_mr_filename_is_safe(back_fn, 8) != 0 { return 12 }
74
75 // --- (b) URL build ---
76 let base: *u8 = "http://10.0.0.5:7125"
77 let path: *u8 = "/printer/info"
78 let n_url: i64 = nx_mr_build_url(base, 20, path, 13, buf, 4096)
79 if n_url != 33 { return 20 }
80 if smoke_buf_eq(buf, "http://10.0.0.5:7125/printer/info", 33) != 1 { return 21 }
81
82 // --- (c) JSON print-start body ---
83 let fn: *u8 = "voron.gcode"
84 let n_json: i64 = nx_mr_build_print_start_body(fn, 11, buf, 4096)
85 // {"filename":"voron.gcode"} -> 13 + 11 + 2 = 26 bytes (no, let me count)
86 // { 1
87 // "filename" 10
88 // : 1
89 // "voron.gcode" 13
90 // } 1
91 // = 26 bytes
92 if n_json != 26 { return 30 }
93 if smoke_buf_eq(buf, "{\"filename\":\"voron.gcode\"}", 26) != 1 { return 31 }
94
95 // --- (d) POST request ---
96 let host: *u8 = "10.0.0.5:7125"
97 let start_path: *u8 = "/printer/print/start"
98 let body: *u8 = "{\"filename\":\"voron.gcode\"}"
99 let n_post: i64 = nx_mr_build_post_request(host, 13,
100 start_path, 20,
101 body, 26,
102 0 as *u8, 0,
103 buf, 4096)
104 if n_post <= 0 { return 40 }
105 if smoke_contains(buf, n_post, "POST /printer/print/start HTTP/1.1\r\n") != 1 { return 41 }
106 if smoke_contains(buf, n_post, "Host: 10.0.0.5:7125\r\n") != 1 { return 42 }
107 if smoke_contains(buf, n_post, "Content-Type: application/json\r\n") != 1 { return 43 }
108 if smoke_contains(buf, n_post, "Content-Length: 26\r\n") != 1 { return 44 }
109 // Body included after \r\n\r\n
110 if smoke_contains(buf, n_post, "{\"filename\":\"voron.gcode\"}") != 1 { return 45 }
111
112 // --- (e) GET request ---
113 let n_get: i64 = nx_mr_build_get_request(host, 13, path, 13,
114 0 as *u8, 0, buf, 4096)
115 if n_get <= 0 { return 50 }
116 if smoke_contains(buf, n_get, "GET /printer/info HTTP/1.1\r\n") != 1 { return 51 }
117 if smoke_contains(buf, n_get, "Host: 10.0.0.5:7125\r\n") != 1 { return 52 }
118
119 // --- (f) GET with API key ---
120 let api_key: *u8 = "secret"
121 let n_get_auth: i64 = nx_mr_build_get_request(host, 13, path, 13,
122 api_key, 6, buf, 4096)
123 if n_get_auth <= 0 { return 60 }
124 if smoke_contains(buf, n_get_auth, "X-Api-Key: secret\r\n") != 1 { return 61 }
125
126 // --- (g)(h)(i) Status parser ---
127 if nx_mr_parse_status("HTTP/1.1 200 OK\r\n", 17) != 200 { return 70 }
128 if nx_mr_parse_status("HTTP/1.1 503 Service", 20) != 503 { return 71 }
129 if nx_mr_parse_status("HTTPx/1.1 200 OK\r\n", 18) != -1 { return 72 }
130
131 return 0
132}