test_httpserver.nim
1 # nim-graphql 2 # Copyright (c) 2021 Status Research & Development GmbH 3 # Licensed under either of 4 # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 # at your option. 7 # This file may not be copied, modified, or distributed except according to 8 # those terms. 9 10 # curl -X POST -H Content-Type:application/graphql http://localhost:8547/graphql -d @data.json 11 12 import 13 std/[os, json], 14 pkg/[toml_serialization, unittest2, chronos], 15 ../graphql, ../graphql/[httpserver, httpclient], 16 ./test_utils 17 18 when defined(tls): 19 import 20 ./keys/keys 21 22 type 23 Unit = object 24 name: string 25 skip: bool 26 error: string 27 opName: string 28 code: string 29 result: string 30 31 TestCase = object 32 units: seq[Unit] 33 34 Counter = ref object 35 skip: int 36 fail: int 37 ok: int 38 39 const 40 caseFolder = "tests" / "serverclient" 41 serverAddress = initTAddress("127.0.0.1:8547") 42 43 proc createServer(serverAddress: TransportAddress, authHooks: seq[AuthHook] = @[]): GraphqlHttpServerRef = 44 let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr} 45 var ctx = GraphqlRef.new() 46 ctx.initMockApi() 47 48 const schema = """ 49 type Query { 50 name: String 51 color: Int 52 tree: Tree 53 } 54 55 type Tree { 56 name: String 57 age: Int 58 number: [Int!] 59 } 60 """ 61 let r = ctx.parseSchema(schema) 62 if r.isErr: 63 debugEcho r.error 64 return 65 66 when defined(tls): 67 let res = GraphqlHttpServerRef.new( 68 graphql = ctx, 69 address = serverAddress, 70 tlsPrivateKey = TLSPrivateKey.init(SecureKey), 71 tlsCertificate = TLSCertificate.init(SecureCrt), 72 socketFlags = socketFlags, 73 authHooks = authHooks 74 ) 75 else: 76 let res = GraphqlHttpServerRef.new( 77 graphql = ctx, 78 address = serverAddress, 79 socketFlags = socketFlags, 80 authHooks = authHooks 81 ) 82 83 if res.isErr(): 84 debugEcho res.error 85 return 86 87 res.get() 88 89 proc setupClient(address: TransportAddress): GraphqlHttpClientRef = 90 const 91 secure = defined(tls) 92 93 GraphqlHttpClientRef.new(address, secure = secure).get() 94 95 proc runExecutor(client: GraphqlHttpClientRef, unit: Unit, testStatusIMPL: var TestStatus) = 96 client.operationName(unit.opName) 97 let res = waitFor client.sendRequest(unit.code) 98 check res.isOk 99 if res.isErr: 100 debugEcho res.error 101 return 102 103 let clientResp = res.get() 104 check (clientResp.status == 200) == (unit.error.len == 0) 105 106 let resp = decodeResponse(clientResp.response) 107 if not resp.errors.isNil: 108 if unit.error.len == 0: 109 debugEcho $resp.errors 110 check unit.error.len != 0 111 let node = parseJson(unit.error) 112 check $node == $resp.errors 113 114 if not resp.data.isNil: 115 check unit.result.len != 0 116 let node = parseJson(unit.result) 117 check $node == $resp.data 118 119 proc runSuite(client: GraphqlHttpClientRef, fileName: string, counter: Counter) = 120 let parts = splitFile(fileName) 121 let cases = Toml.loadFile(fileName, TestCase) 122 suite parts.name: 123 for x in cases.units: 124 let unit = x # prevent nim >= 1.6 cannot capture lent 125 test unit.name: 126 if unit.skip: 127 skip() 128 inc counter.skip 129 else: 130 client.runExecutor(unit, testStatusIMPL) 131 if testStatusIMPL == OK: 132 inc counter.ok 133 else: 134 inc counter.fail 135 136 proc executeCases() = 137 var counter = Counter() 138 let server = createServer(serverAddress) 139 server.start() 140 var client = setupClient(serverAddress) 141 142 for fileName in walkDirRec(caseFolder): 143 client.runSuite(fileName, counter) 144 145 waitFor server.closeWait() 146 debugEcho counter[] 147 148 proc testHooks() = 149 proc mockAuth(req: HttpRequestRef): Future[HttpResponseRef] {. 150 gcsafe, async: (raises: [CatchableError]).} = 151 if req.headers.getString("Auth-Token") == "Good Token": 152 return HttpResponseRef(nil) 153 154 return await req.respond(Http401, "Unauthorized access") 155 156 let server = createServer(serverAddress, @[AuthHook(mockAuth)]) 157 server.start() 158 159 const 160 query = """{ __type(name: "ID") { kind }}""" 161 headers = [ 162 163 ("Auth-Token", "Good Token") 164 ] 165 166 suite "Test Hooks": 167 test "no auth token": 168 let client = setupClient(serverAddress) 169 170 let res = waitFor client.sendRequest(query) 171 check res.isOk 172 if res.isErr: 173 debugEcho res.error 174 return 175 176 let resp = res.get() 177 check resp.status == 401 178 check resp.reason == "Unauthorized" 179 check resp.response == "Unauthorized access" 180 181 test "good auth token": 182 let client = setupClient(serverAddress) 183 let res = waitFor client.sendRequest(query, @headers) 184 check res.isOk 185 if res.isErr: 186 debugEcho res.error 187 return 188 189 let resp = res.get() 190 check resp.status == 200 191 check resp.reason == "OK" 192 check resp.response == """{"data":{"__type":{"kind":"SCALAR"}}}""" 193 194 waitFor server.closeWait() 195 196 template mainModule() {.used.} = 197 proc main() = 198 let conf = getConfiguration() 199 if conf.testFile.len == 0: 200 executeCases() 201 testHooks() 202 return 203 204 # disable unittest param handler 205 var counter = Counter() 206 let fileName = caseFolder / conf.testFile 207 var client = setupClient(serverAddress) 208 if conf.unit.len == 0: 209 let server = createServer(serverAddress) 210 server.start() 211 client.runSuite(fileName, counter) 212 waitFor server.closeWait() 213 echo counter[] 214 return 215 216 let cases = Toml.loadFile(fileName, TestCase) 217 let server = createServer(serverAddress) 218 server.start() 219 for x in cases.units: 220 let unit = x # prevent nim >= 1.6 cannot capture lent 221 if unit.name != conf.unit: 222 continue 223 test unit.name: 224 client.runExecutor(unit, testStatusIMPL) 225 waitFor server.closeWait() 226 227 var message: string 228 ## Processing command line arguments 229 if processArguments(message) != ConfigStatus.Success: 230 echo message 231 quit(QuitFailure) 232 else: 233 if len(message) > 0: 234 echo message 235 quit(QuitSuccess) 236 main() 237 238 const 239 crashCondition = defined(windows) and defined(i386) and defined(release) 240 241 when isMainModule: 242 when not crashCondition: 243 import 244 ../graphql/test_config 245 mainModule() 246 else: 247 when not crashCondition: 248 executeCases() 249 testHooks()