1 /// Test for [tagion.services.inputvalidator] 2 module tagion.testbench.services.inputvalidator; 3 // Default import list for bdd 4 import core.time; 5 import nngd; 6 import std.format; 7 import std.stdio; 8 import std.typecons : Tuple; 9 import std.typecons; 10 import tagion.actor; 11 import tagion.actor.exceptions; 12 import tagion.basic.Types; 13 import tagion.behaviour; 14 import tagion.communication.HiRPC; 15 import tagion.hibon.Document; 16 import tagion.hibon.HiBON; 17 import tagion.hibon.HiBONBase; 18 import tagion.hibon.HiBONJSON; 19 import tagion.logger.LogRecords : LogInfo; 20 import tagion.logger.Logger; 21 import tagion.services.inputvalidator; 22 import tagion.services.messages; 23 import tagion.testbench.actor.util; 24 import tagion.testbench.tools.Environment; 25 import tagion.tools.Basic; 26 import concurrency = tagion.utils.pretend_safe_concurrency; 27 import tagion.utils.pretend_safe_concurrency; 28 29 enum feature = Feature( 30 "Inputvalidator service", 31 [ 32 "This feature should verify that the inputvalidator accepts valid and rejects invalid LEB128 input over a socket" 33 ]); 34 35 alias FeatureContext = Tuple!( 36 SendADocumentToTheSocket, "SendADocumentToTheSocket", 37 SendNoneHiRPC, "SendNoneHiRPC", 38 SendPartialHiBON, "SendPartialHiBON", 39 SendBigContract, "SendBigContract", 40 FeatureGroup*, "result" 41 ); 42 43 @safe @Scenario("send a HiRPC document to the socket", []) 44 class SendADocumentToTheSocket { 45 NNGSocket sock; 46 const string sock_path; 47 this(string _sock_path) @trusted { 48 sock = NNGSocket(nng_socket_type.NNG_SOCKET_REQ); 49 sock_path = _sock_path; 50 } 51 52 Document doc; 53 54 @Given("a inputvalidator") 55 Document aInputvalidator() { 56 waitforChildren(Ctrl.ALIVE); 57 return result_ok; 58 } 59 60 @When("we send a HiRPC `Document`") 61 Document aSocket() @trusted { 62 sock.sendtimeout = msecs(1000); 63 sock.sendbuf = 4096; 64 sock.recvbuf = 4096; 65 int rc = sock.dial(sock_path /* nonblock : true */ ); 66 check(rc == 0, format("Failed to dial %s", nng_errstr(rc))); 67 HiRPC hirpc; 68 auto hibon = new HiBON(); 69 hibon["$test"] = 5; 70 const sender = hirpc.act(hibon); 71 doc = sender.toDoc; 72 rc = sock.send(doc.serialize); 73 check(rc == 0, format("Failed to send %s", nng_errstr(rc))); 74 Document received = sock.receive!Buffer; 75 check(sock.m_errno == 0, format("Failed to receive %s", nng_errstr(sock.m_errno))); 76 check(received.length != 0, "Received empty doc"); 77 auto receiver = hirpc.receive(received); 78 check(receiver.isResponse, "Expected an error"); 79 80 return result_ok; 81 } 82 83 @Then("we receive back the Document in our mailbox") 84 Document ourMailbox() @trusted { 85 auto res = concurrency.receiveOnly!(Tuple!(inputDoc, Document)); 86 writeln("Receive back: ", res[1].toPretty); 87 check(res[1] == doc, "The value was not the same as we sent"); 88 return result_ok; 89 } 90 } 91 92 @safe @Scenario("send none hirpc document", []) 93 class SendNoneHiRPC { 94 95 NNGSocket sock; 96 const string sock_path; 97 this(string _sock_path) @trusted { 98 sock = NNGSocket(nng_socket_type.NNG_SOCKET_REQ); 99 sock_path = _sock_path; 100 } 101 102 @Given("a inputvalidator") 103 Document inputvalidator() { 104 waitforChildren(Ctrl.ALIVE); 105 106 register("inputvalidator_tester", thisTid); 107 108 log.registerSubscriptionTask("inputvalidator_tester"); 109 submask.subscribe(InputValidatorService.rejected); 110 return result_ok; 111 } 112 113 @When("we send a document which is not a HiRPC on a socket") 114 Document socket() @trusted { 115 sock.sendtimeout = msecs(1000); 116 sock.sendbuf = 4096; 117 sock.recvbuf = 4096; 118 sock.recvtimeout = msecs(1000); 119 int rc = sock.dial(sock_path); 120 check(rc == 0, format("Failed to dial %s", rc)); 121 122 auto hibon = new HiBON(); 123 hibon["$test"] = 5; 124 writefln("Buf length %s %s", hibon.serialize.length, Document(hibon.serialize).valid); 125 126 rc = sock.send(hibon.serialize); 127 check(rc == 0, format("Failed to send %s", rc)); 128 Document received = sock.receive!Buffer; 129 check(sock.m_errno == 0, format("Failed to receive %s", nng_errstr(sock.m_errno))); 130 check(received.length != 0, "Received empty buffer"); 131 check(received !is Document.init, "Received empty document"); 132 HiRPC hirpc = HiRPC(null); 133 auto receiver = hirpc.receive(received); 134 check(receiver.isError, "Expected an error"); 135 136 return result_ok; 137 } 138 139 @Then("the inputvalidator rejects") 140 Document rejects() { 141 import tagion.testbench.actor.util; 142 143 check(!concurrency.receiveTimeout(100.msecs, (inputDoc _, Document __) {}), 144 "should not have received a doc"); 145 receiveOnlyTimeout!(LogInfo, const(Document)); 146 147 return result_ok; 148 } 149 150 } 151 152 @safe @Scenario("send partial HiBON", []) 153 class SendPartialHiBON { 154 155 NNGSocket sock; 156 const string sock_path; 157 this(string _sock_path) @trusted { 158 sock = NNGSocket(nng_socket_type.NNG_SOCKET_REQ); 159 sock_path = _sock_path; 160 sock.sendtimeout = msecs(1000); 161 sock.sendbuf = 4096; 162 } 163 164 @Given("a inputvalidator") 165 Document inputvalidator() { 166 check(waitforChildren(Ctrl.ALIVE), "waitforChildren"); 167 168 register("inputvalidator_tester", thisTid); 169 log.registerSubscriptionTask("inputvalidator_tester"); 170 submask.subscribe(InputValidatorService.rejected); 171 return result_ok; 172 } 173 174 @When("we send a `partial_hibon` on a socket") 175 Document socket() @trusted { 176 int rc = sock.dial(sock_path); 177 check(rc == 0, format("Failed to dial %s", nng_errstr(rc))); 178 HiRPC hirpc; 179 auto hibon = new HiBON(); 180 hibon["$test"] = 5; 181 const sender = hirpc.act(hibon); 182 Document doc = sender.toDoc; 183 immutable partial_buf = doc.serialize[0 .. 26].dup; 184 writefln("Buf length %s %s", partial_buf.length, Document(partial_buf).valid); 185 rc = sock.send(partial_buf); 186 check(rc == 0, format("Failed to send %s", nng_errstr(rc))); 187 Document received = sock.receive!Buffer; 188 check(sock.m_errno == 0, format("Failed to receive %s", nng_errstr(sock.m_errno))); 189 check(received.length != 0, "Received empty doc"); 190 auto receiver = hirpc.receive(received); 191 check(receiver.isError, "Expected an error"); 192 193 return result_ok; 194 } 195 196 @Then("the inputvalidator rejects") 197 Document rejects() { 198 check(!concurrency.receiveTimeout(100.msecs, (inputDoc _, Document __) {}), "should not have received a doc"); 199 receiveOnlyTimeout!(LogInfo, const(Document)); // Subscribed rejected data 200 return result_ok; 201 } 202 203 } 204 205 @safe @Scenario("send Big Contract", 206 []) 207 class SendBigContract { 208 209 NNGSocket sock; 210 const string sock_path; 211 this(string _sock_path) @trusted { 212 sock = NNGSocket(nng_socket_type.NNG_SOCKET_REQ); 213 sock_path = _sock_path; 214 sock.sendtimeout = msecs(1000); 215 sock.sendbuf = 0x4000; 216 } 217 218 219 @Given("a inputvalidator") 220 Document inputvalidator() { 221 check(waitforChildren(Ctrl.ALIVE), "waitforChildren"); 222 223 register("inputvalidator_tester", thisTid); 224 log.registerSubscriptionTask("inputvalidator_tester"); 225 submask.subscribe(InputValidatorService.rejected); 226 return result_ok; 227 } 228 229 @When("we send a `huge contract` on a socket") 230 Document socket() @trusted { 231 import std.range; 232 import std.algorithm; 233 234 HiRPC hirpc; 235 int rc = sock.dial(sock_path); 236 check(rc == 0, format("Failed to dial %s", nng_errstr(rc))); 237 auto hibon = new HiBON(); 238 239 string long_string = iota(0,10_000).map!(i => format("%d", i)).join; 240 // writefln(long_string); 241 hibon["$test"] = long_string; 242 const sender = hirpc.act(hibon); 243 auto to_send = sender.toDoc.serialize; 244 writefln("long_hibon length=%skb", to_send.length/1000, Document(to_send).valid); 245 rc = sock.send(to_send); 246 check(rc == 0, format("Failed to send %s", nng_errstr(rc))); 247 Document received = sock.receive!Buffer; 248 check(sock.m_errno == 0, format("Failed to receive %s", nng_errstr(sock.m_errno))); 249 check(received.length != 0, "Received empty doc"); 250 auto receiver = hirpc.receive(received); 251 writefln(receiver.toPretty); 252 check(!receiver.isError, "Did not expect an error"); 253 254 return result_ok; 255 } 256 257 @Then("we should receive response ok") 258 Document ok() { 259 check(concurrency.receiveTimeout(200.msecs, (inputDoc _, Document __) {}), "should have received a doc"); 260 return result_ok; 261 } 262 263 }