Added more functional tests, fix bugs discovered.

* Fixed the formatting of command line logging of strawboss workers.
* Fixed a bug in the (de)serialization of log levels in the strawboss service
  config file.
* Pulled `parseBuildStatus` logic out of `loadBuildStatus` so that we could
  parse a JSON that didn't come from a file.
* Added `parseRun` for Run objects.
* Moved `/ping` to `/service/debug/ping` for symmetry with
  `/service/debug/stop`
* Added functional tests of full builds.
This commit is contained in:
Jonathan Bernard
2017-11-25 18:25:03 -06:00
parent 58fbbc048c
commit 4edae250ba
8 changed files with 119 additions and 48 deletions

View File

@ -1,10 +1,11 @@
import cliutils, httpclient, json, os, osproc, sequtils, strutils, tempfile,
times, unittest, untar
times, unittest, untar, uuids
from langutils import sameContents
import ../testutil
import ../../../main/nim/strawbosspkg/configuration
import ../../../main/nim/strawbosspkg/core
let apiBase = "http://localhost:8180/api"
let cfgFilePath = "src/test/json/strawboss.config.json"
@ -39,7 +40,7 @@ suite "strawboss server":
newCfg.buildDataDir = tempBuildDataDir
# update the repo string for the extracted test project
var testProjDef = newCfg.findProject(testProjName)
var testProjDef = newCfg.getProject(testProjName)
testProjDef.repo = testProjTempDir
newCfg.setProject(testProjName, testProjDef)
@ -90,45 +91,64 @@ suite "strawboss server":
test "run a successful build with artifacts":
let http = newAuthenticatedHttpClient(apibase, "bob@builder.com", "password")
let resp = http.post(apiBase & "/project/" & testProjName & "/step/build/run/0.1.0")
let resp = http.post(apiBase & "/project/" & testProjName & "/step/build/run/0.2.1")
check resp.status.startsWith("200")
# give the filesystem time to create stuff
sleep(100)
# Check that the run was queued
let queuedRun = parseRun(parseJson(resp.body))
check queuedRun.status.state == BuildState.queued
# check that the run request has been saved
# Wait for the build to complete
let completedRun = http.waitForBuild(apiBase, testProjname, $queuedRun.id)
# check that the run directory, run request, status, and output logs exist
let runsDir = tempBuildDataDir & "/" & testProjName & "/runs"
let runId = $completedRun.id
check existsDir(runsDir)
for suffix in [".request.json", ".status.json", ".stdout.log", ".stderr.log"]:
check existsFile(runsDir & "/" & runId & suffix)
let reqFile = runsDir
# check that the project directory has been created in the artifacts repo
let runArtifactsDir = tempBuildDataDir & "/" & testProjName & "/artifacts/build/0.1.0"
let runArtifactsDir = tempBuildDataDir & "/" & testProjName & "/artifacts/build/0.2.1"
check existsDir(runArtifactsDir)
# check that the run status file has been created in the artifacts repo
let statusFile = tempBuildDataDir & "/" & testProjName & "/status/0.1.0.json"
# check that the build step status file has been created
let statusFile = tempBuildDataDir & "/" & testProjName & "/status/build/0.2.1.json"
check fileExists(statusFile)
# check that the run status is not failed
# check that the status is complete
var status = loadBuildStatus(statusFile)
check status.state != "failed"
# wait for the build to complete
let expTime = getTime() + TIMEOUT
while getTime() < expTime and not contains(["complete", "failed"], status.state):
sleep(1000)
status = loadBuildStatus(statusFile)
# check that the status is "complete"
check status.state == "complete"
check status.state == BuildState.complete
# check that the artifacts we expect are present
let binFile = runArtifactsDir & "/test_project"
check existsFile(binFile)
# TODO
test "run a time-consuming build and check the status via the API":
check false
test "run a multi-step build":
let http = newAuthenticatedHttpClient(apibase, "bob@builder.com", "password")
# Run the "test" step (depends on "build")
var resp = http.post(apiBase & "/project/" & testProjname & "/step/test/run/0.2.1")
check resp.status.startsWith("200")
let queuedRun = parseRun(parseJson(resp.body))
let completedRun = http.waitForBuild(apiBase, testProjName, $queuedRun.id)
# there should be successful status files for both the build and test steps
for stepName in ["build", "test"]:
let statusFile = tempBuildDataDir & "/" & testProjName & "/status/" & stepName & "/0.2.1.json"
check fileExists(statusFile)
let status = loadBuildStatus(statusFile)
check status.state == BuildState.complete
#test "already completed steps should not be rebuilt":
# let http = newAuthenticatedHttpClient(apibase, "bob@builder.com", "password")
# let runArtifactsDir = tempBuildDataDir & "/" & testProjName & "/artifacts/build/0.2.1"
# let exeModTime = getLastModificationTime(runArtifactsDir & "/test_project")
# Run the "build" step
# Kick off a build that depends on "build" (which was run in the last test)
# TODO
#test "kick off multiple runs and check the list of active runs via the API":

View File

@ -1,4 +1,7 @@
import httpclient, json, strutils
import httpclient, json, os, strutils, times
import ../../main/nim/strawbosspkg/core
import ../../main/nim/strawbosspkg/configuration
proc newAuthenticatedHttpClient*(apiBase, uname, pwd: string): HttpClient =
result = newHttpClient()
@ -6,4 +9,38 @@ proc newAuthenticatedHttpClient*(apiBase, uname, pwd: string): HttpClient =
assert authResp.status.startsWith("200")
result.headers = newHttpHeaders({"Authorization": "Bearer " & parseJson(authResp.body).getStr})
proc waitForBuild*(client: HttpClient, apiBase, projectName, runId: string,
expectedState = BuildState.complete,
failedState = BuildState.failed,
timeout = 10): Run =
let startTime = epochTime()
var run: Run
#echo "Waiting for '" & $expectedState & "' from run:\n\t" &
# apiBase & "/project/" & projectName & "/run/" & runId
while true:
var curElapsed = epochTime() - startTime
#echo "Checking (" & $curElapsed & " has passed)."
if curElapsed > toFloat(timeout):
raise newException(SystemError, "Timeout exceeded waiting for build.")
let resp = client.get(apiBase & "/project/" & projectName & "/run/" & runId)
#echo "Received resp:\n\n" & $resp.status & "\n\n" & $resp.body
if not resp.status.startsWith("200"):
raise newException(IOError, "Unable to retrieve status. Received response: " & resp.body)
run = parseRun(parseJson(resp.body))
if run.status.state == failedState:
raise newException(IOError, "Run transitioned to failed state '" & $failedState & "'")
if run.status.state == expectedState:
return run
sleep(200)

View File

@ -41,7 +41,7 @@ suite "strawboss server":
check fromJWT(cfg, tok) == session
test "ping":
let resp = http.get(apiBase & "/ping")
let resp = http.get(apiBase & "/service/debug/ping")
check:
resp.status.startsWith("200")
resp.body == "\"pong\""