import asyncdispatch, httpclient, json, os, osproc, sequtils, strutils, tempfile, times, unittest from langutils import sameContents import ../../main/nim/strawbosspkg/configuration import ../../main/nim/strawbosspkg/server import ../../main/nim/strawbosspkg/private/util # test helpers proc newAuthenticatedHttpClient(apiBase, uname, pwd: string): HttpClient = result = newHttpClient() let authResp = result.post(apiBase & "/auth-token", $(%*{"username": uname, "password": pwd})) assert authResp.status.startsWith("200") result.headers = newHttpHeaders({"Authorization": "Bearer " & parseJson(authResp.body).getStr}) let apiBase = "http://localhost:8180/api" let cfgFilePath = "src/test/json/strawboss.config.json" let cfg = loadStrawBossConfig(cfgFilePath) let testuser = UserRef( # note: needs to correspond to an actual user name: "bob@builder.com", hashedPwd: "$2a$11$lVZ9U4optQMhzPh0E9A7Yu6XndXblUF3gCa.zmEvJy4F.4C4718b.") suite "strawboss server": # suite setup code let serverProcess = startProcess("./strawboss", ".", @["serve", "-c", cfgFilePath], loadEnv(), {poUsePath}) let http = newHttpClient() # give the server time to spin up sleep(100) ## UNIT TESTS test "validate hashed pwd": check validatePwd(testuser, "password") test "detect invalid pwds": check(not validatePwd(testuser, "Password")) test "make and extract a JWT token from a session": let session = newSession(testuser) let tok = toJWT(cfg, session) check fromJWT(cfg, tok) == session test "ping": let resp = http.get(apiBase & "/ping") check: resp.status.startsWith("200") resp.body == "\"pong\"" test "fail auth": let resp = http.post(apiBase & "/auth-token", $(%*{"username": "bob@builder.com", "password": "notpassword"})) check resp.status.startsWith("401") test "auth": let resp = http.post(apiBase & "/auth-token", $(%*{"username": "bob@builder.com", "password": "password"})) check resp.status.startsWith("200") test "verify valid auth token": let authHttp = newAuthenticatedHttpClient(apiBase, "bob@builder.com", "password") let resp = authHttp.get(apiBase & "/verify-auth") check resp.status.startsWith("200") test "verify fails when no auth token is given": let resp = http.get(apiBase & "/verify-auth") check resp.status.startsWith("401") test "verify fails when invalid auth token is given": let http1 = newHttpClient() http1.headers = newHttpHeaders({"Authorization": "Bearer nope"}) let resp = http1.get(apiBase & "/verify-auth") check resp.status.startsWith("401") test "fail to get projects when not authenticated": let resp = http.get(apiBase & "/projects") check resp.status.startsWith("401") test "get projects": let authHttp = newAuthenticatedHttpClient(apiBase, "bob@builder.com", "password") let resp = authHttp.get(apiBase & "/projects") check resp.status.startsWith("200") let projects: seq[ProjectDef] = parseJson(resp.body).getElems.mapIt(parseProjectDef(it)) check sameContents(projects, cfg.projects) # suite tear-down # give the server time to spin down but kill it after that discard newAsyncHttpClient().post(apiBase & "/service/debug/stop") sleep(100) if serverProcess.running: kill(serverProcess) suite "strawboss server continued": setup: let tmpArtifactsDir = mkdtemp() let (_, tmpCfgPath) = mkstemp() var newCfg = cfg newCfg.artifactsRepo = tmpArtifactsDir writeFile(tmpCfgPath, $newCfg) let serverProcess = startProcess("./strawboss", ".", @["serve", "-c", tmpCfgPath], loadEnv(), {poUsePath}) # give the server time to spin up sleep(100) teardown: discard newAsyncHttpClient().post(apiBase & "/service/debug/stop") removeDir(tmpArtifactsDir) removeFile(tmpCfgPath) # give the server time to spin down but kill it after that sleep(100) if serverProcess.running: kill(serverProcess) test "handle missing project configuration": let http = newAuthenticatedHttpClient(apibase, "bob@builder.com", "password") let resp = http.get(apiBase & "/projects/" & cfg.projects[0].name) check resp.status.startsWith("404") test "gives 404 when no versions built": let http = newAuthenticatedHttpClient(apibase, "bob@builder.com", "password") let resp = http.get(apiBase & "/projects/" & cfg.projects[0].name & "/versions") check resp.status.startsWith("404") test "GET /api/project/@projectName/versions": let projArtifactsDir = tmpArtifactsDir & "/" & cfg.projects[0].name let expectedVersions = @["alpha", "beta", "1.0.0", "1.0.1"] # Touch configuration files createDir(projArtifactsDir) for v in expectedVersions: var f: File check open(f, projArtifactsDir & "/configuration." & v & ".json", fmWrite) close(f) let http = newAuthenticatedHttpClient(apibase, "bob@builder.com", "password") let resp = http.get(apiBase & "/project/" & cfg.projects[0].name & "/versions") let returnedVersions = parseJson(resp.body).getElems.mapIt(it.getStr) check sameContents(expectedVersions, returnedVersions) # Last-chance catch to kill the server in case some test err'ed and didn't # reach it's teardown handler discard newAsyncHttpClient().post(apiBase & "/service/debug/stop")