# Build a project This scenario tests the controller API to simulate a build. SCENARIO build a project Set up the controller. GIVEN an RSA key pair for token signing AND controller config uses statedir at the state directory AND controller config uses https://blobs.example.com as artifact store AND controller config uses https://auth.example.com as authentication AND controller config uses https://notify.example.com as notify AND an access token for user with scopes ... uapi_pipelines_post ... uapi_projects_post ... uapi_projects_id_status_put ... uapi_projects_id_status_get ... uapi_projects_id_builds_get ... uapi_workers_id_get ... uapi_builds_get ... uapi_builds_id_get ... uapi_logs_id_get AND a running ick controller Add up a project with some named pipelines. WHEN user makes request POST /pipelines with a valid token and body ... { ... "pipeline": "construct", ... "actions": [ ... { "where": "host", "shell": "day 1" }, ... { "where": "host", "shell": "day 2" } ... ] ... } THEN result has status code 201 WHEN user makes request POST /projects with a valid token and body ... { ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "pipelines": ["construct"] ... } THEN result has status code 201 Add a second project so we know each project gets its own work steps. WHEN user makes request POST /projects with a valid token and body ... { ... "project": "constantinople", ... "parameters": { ... "hey": "there" ... }, ... "pipelines": ["construct"] ... } THEN result has status code 201 There are no builds yet. WHEN user makes request GET /builds THEN result has status code 200 AND body matches { "builds": [] } Register a worker. GIVEN an access token for obelix with scopes ... uapi_workers_post ... uapi_work_post ... uapi_work_get WHEN obelix makes request POST /workers with a valid token and body ... { ... } THEN result has status code 201 Trigger build. WHEN user makes request GET /projects/rome/+trigger THEN result has status code 200 Worker wants work and gets the first step to run. If the worker asks again, it gets the same answer. **FIXME: should the name of the worker be in the path or can we get it in the access token?** Note that the controller has inserted a special additional step to get the worker to construct a new workspace for the build. WHEN obelix makes request GET /work THEN result has status code 200 AND body matches ... { ... "build_id": "rome/1", ... "build_number": 1, ... "log": "/logs/rome/1", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "action_id": "1", ... "step": { ... "action": "create_workspace", ... "where": "host" ... } ... } WHEN obelix makes request GET /work THEN result has status code 200 AND body matches ... { ... "build_id": "rome/1", ... "build_number": 1, ... "log": "/logs/rome/1", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "action_id": "1", ... "step": { ... "action": "create_workspace", ... "where": "host" ... } ... } User can now see pipeline is running and which worker is building it. WHEN user makes request GET /workers/obelix THEN result has status code 200 AND body matches ... { ... "worker": "obelix", ... "doing": { ... "build_id": "rome/1", ... "build_number": 1, ... "log": "/logs/rome/1", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "action_id": "1", ... "step": { ... "action": "create_workspace", ... "where": "host" ... } ... } ... } WHEN user makes request GET /builds THEN result has status code 200 AND body matches ... { ... "builds": [ ... { ... "build_id": "rome/1", ... "build_number": 1, ... "log": "/logs/rome/1", ... "worker": "obelix", ... "project": "rome", ... "graph": { ... "1": { ... "status": "building", ... "depends": [], ... "action": {"where": "host", "action": "create_workspace" } ... }, ... "2": { ... "status": "blocked", ... "depends": ["1"], ... "action": {"where": "host", "shell": "day 1" } ... }, ... "3": { ... "status": "blocked", ... "depends": ["2"], ... "action": {"where": "host", "shell": "day 2" } ... } ... }, ... "parameters": { ... "foo": "bar" ... }, ... "status": "building", ... "exit_code": null, ... "log": "/logs/rome/1" ... } ... ] ... } WHEN user makes request GET /logs/rome/1 THEN result has status code 200 AND result has header Content-Type: text/plain Worker reports workspace creation is done. Note the zero exit code. WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "rome/1", ... "action_id": "1", ... "worker": "obelix", ... "project": "rome", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 Worker requests more work, and gets the first actual build step. WHEN obelix makes request GET /work THEN result has status code 200 AND body matches ... { ... "build_id": "rome/1", ... "build_number": 1, ... "log": "/logs/rome/1", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "action_id": "2", ... "step": { ... "shell": "day 1", ... "where": "host" ... } ... } Worker reports some build output. Note the null exit code. The step hasn't finished yet. WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "rome/1", ... "action_id": "2", ... "worker": "obelix", ... "project": "rome", ... "exit_code": null, ... "stdout": "hey ho", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 Worker-manager still gets the same step, since the build step didn't finish. WHEN obelix makes request GET /work THEN result has status code 200 AND body matches ... { ... "build_id": "rome/1", ... "build_number": 1, ... "log": "/logs/rome/1", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "action_id": "2", ... "step": { ... "shell": "day 1", ... "where": "host" ... } ... } The build log is immediately accessible. WHEN user makes request GET /logs/rome/1 THEN result has status code 200 AND result has header Content-Type: text/plain AND body text contains "hey ho" Report the step is done, and successfully. WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "rome/1", ... "action_id": "2", ... "worker": "obelix", ... "project": "rome", ... "exit_code": 0, ... "stdout": ", hey ho\n", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN user makes request GET /logs/rome/1 THEN result has status code 200 AND result has header Content-Type: text/plain AND body text contains "hey ho, hey ho\n" The build status now shows the next step as the active one. WHEN user makes request GET /builds THEN result has status code 200 AND body matches ... { ... "builds": [ ... { ... "build_id": "rome/1", ... "build_number": 1, ... "log": "/logs/rome/1", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "graph": { ... "1": { ... "status": "done", ... "depends": [], ... "action": {"where": "host", "action": "create_workspace" } ... }, ... "2": { ... "status": "done", ... "depends": ["1"], ... "action": {"where": "host", "shell": "day 1" } ... }, ... "3": { ... "status": "ready", ... "depends": ["2"], ... "action": {"where": "host", "shell": "day 2" } ... } ... }, ... "status": "building", ... "exit_code": 0, ... "log": "/logs/rome/1" ... } ... ] ... } Now there's another step to do. WHEN obelix makes request GET /work THEN result has status code 200 AND body matches ... { ... "build_id": "rome/1", ... "build_number": 1, ... "log": "/logs/rome/1", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "action_id": "3", ... "step": { ... "shell": "day 2", ... "where": "host" ... } ... } User sees changed status. WHEN user makes request GET /workers/obelix THEN result has status code 200 AND body matches ... { ... "worker": "obelix", ... "doing": { ... "build_id": "rome/1", ... "build_number": 1, ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "action_id": "3", ... "step": { ... "shell": "day 2", ... "where": "host" ... }, ... "log": "/logs/rome/1" ... } ... } Report it done. WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "rome/1", ... "action_id": "3", ... "worker": "obelix", ... "project": "rome", ... "exit_code": 0, ... "stdout": "to the gold mine we go!\n", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 Worker now gets told to notify about the build. WHEN obelix makes request GET /work THEN result has status code 200 AND body matches ... { ... "build_id": "rome/1", ... "build_number": 1, ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "action_id": "4", ... "step": { ... "action": "notify" ... }, ... "log": "/logs/rome/1" ... } Report it's done. WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "rome/1", ... "action_id": "4", ... "worker": "obelix", ... "project": "rome", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 Now there's no more work to do. WHEN obelix makes request GET /work THEN result has status code 200 AND body matches {} There's a build with a log. Also, the build status shows there's no current action. WHEN user makes request GET /builds THEN result has status code 200 AND body matches ... { ... "builds": [ ... { ... "build_id": "rome/1", ... "build_number": 1, ... "log": "/logs/rome/1", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "graph": { ... "1": { ... "status": "done", ... "depends": [], ... "action": {"where": "host", "action": "create_workspace" } ... }, ... "2": { ... "status": "done", ... "depends": ["1"], ... "action": {"where": "host", "shell": "day 1" } ... }, ... "3": { ... "status": "done", ... "depends": ["2"], ... "action": {"where": "host", "shell": "day 2" } ... }, ... "4": { ... "status": "done", ... "depends": [], ... "action": {"action": "notify" } ... } ... }, ... "status": "done", ... "exit_code": 0 ... } ... ] ... } WHEN user makes request GET /builds/rome/1 THEN result has status code 200 AND body matches ... { ... "build_id": "rome/1", ... "build_number": 1, ... "log": "/logs/rome/1", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "graph": { ... "1": { ... "status": "done", ... "depends": [], ... "action": {"where": "host", "action": "create_workspace" } ... }, ... "2": { ... "status": "done", ... "depends": ["1"], ... "action": {"where": "host", "shell": "day 1" } ... }, ... "3": { ... "status": "done", ... "depends": ["2"], ... "action": {"where": "host", "shell": "day 2" } ... }, ... "4": { ... "status": "done", ... "depends": [], ... "action": {"action": "notify" } ... } ... }, ... "status": "done", ... "exit_code": 0 ... } WHEN user makes request GET /logs/rome/1 THEN result has status code 200 AND result has header Content-Type: text/plain AND body text contains "hey ho, hey ho\n" AND body text contains "to the gold mine we go!\n" Start build again. This should become build number 2. WHEN user makes request GET /projects/rome/+trigger THEN result has status code 200 WHEN obelix makes request GET /work THEN result has status code 200 AND body matches ... { ... "build_id": "rome/2", ... "build_number": 2, ... "log": "/logs/rome/2", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "action_id": "1", ... "step": { ... "action": "create_workspace", ... "where": "host" ... } ... } WHEN user makes request GET /builds THEN result has status code 200 AND body matches ... { ... "builds": [ ... { ... "build_id": "rome/1", ... "build_number": 1, ... "log": "/logs/rome/1", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "graph": { ... "1": { ... "status": "done", ... "depends": [], ... "action": {"where": "host", "action": "create_workspace" } ... }, ... "2": { ... "status": "done", ... "depends": ["1"], ... "action": {"where": "host", "shell": "day 1" } ... }, ... "3": { ... "status": "done", ... "depends": ["2"], ... "action": {"where": "host", "shell": "day 2" } ... }, ... "4": { ... "status": "done", ... "depends": [], ... "action": {"action": "notify" } ... } ... }, ... "status": "done", ... "exit_code": 0 ... }, ... { ... "build_id": "rome/2", ... "build_number": 2, ... "log": "/logs/rome/2", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "graph": { ... "1": { ... "status": "building", ... "depends": [], ... "action": {"where": "host", "action": "create_workspace" } ... }, ... "2": { ... "status": "blocked", ... "depends": ["1"], ... "action": {"where": "host", "shell": "day 1" } ... }, ... "3": { ... "status": "blocked", ... "depends": ["2"], ... "action": {"where": "host", "shell": "day 2" } ... } ... }, ... "status": "building", ... "exit_code": null ... } ... ] ... } WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "rome/2", ... "action_id": "1", ... "worker": "obelix", ... "project": "rome", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN obelix makes request GET /work THEN result has status code 200 AND body matches ... { ... "build_id": "rome/2", ... "build_number": 2, ... "log": "/logs/rome/2", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "action_id": "2", ... "step": { ... "shell": "day 1", ... "where": "host" ... } ... } WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "rome/2", ... "action_id": "2", ... "worker": "obelix", ... "project": "rome", ... "exit_code": 0, ... "stdout": "hey ho", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN obelix makes request GET /work THEN result has status code 200 WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "rome/2", ... "action_id": "3", ... "worker": "obelix", ... "project": "rome", ... "exit_code": 0, ... "stdout": "hey ho", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN obelix makes request GET /work THEN result has status code 200 AND body matches ... { ... "build_id": "rome/2", ... "build_number": 2, ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "action_id": "4", ... "step": { ... "action": "notify" ... }, ... "log": "/logs/rome/2" ... } WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "rome/2", ... "action_id": "4", ... "worker": "obelix", ... "project": "rome", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN obelix makes request GET /work THEN result has status code 200 AND body matches {} WHEN user makes request GET /builds THEN result has status code 200 AND body matches ... { ... "builds": [ ... { ... "build_id": "rome/1", ... "build_number": 1, ... "log": "/logs/rome/1", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "graph": { ... "1": { ... "status": "done", ... "depends": [], ... "action": {"where": "host", "action": "create_workspace" } ... }, ... "2": { ... "status": "done", ... "depends": ["1"], ... "action": {"where": "host", "shell": "day 1" } ... }, ... "3": { ... "status": "done", ... "depends": ["2"], ... "action": {"where": "host", "shell": "day 2" } ... }, ... "4": { ... "status": "done", ... "depends": [], ... "action": {"action": "notify" } ... } ... }, ... "status": "done", ... "exit_code": 0 ... }, ... { ... "build_id": "rome/2", ... "build_number": 2, ... "log": "/logs/rome/2", ... "worker": "obelix", ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, ... "graph": { ... "1": { ... "status": "done", ... "depends": [], ... "action": {"where": "host", "action": "create_workspace" } ... }, ... "2": { ... "status": "done", ... "depends": ["1"], ... "action": {"where": "host", "shell": "day 1" } ... }, ... "3": { ... "status": "done", ... "depends": ["2"], ... "action": {"where": "host", "shell": "day 2" } ... }, ... "4": { ... "status": "done", ... "depends": [], ... "action": {"action": "notify" } ... } ... }, ... "status": "done", ... "exit_code": 0 ... } ... ] ... } FINALLY stop ick controller # Build two projects sequentially This scenario tests the controller API to simulate a build. SCENARIO build two projects sequentially Set up the controller. GIVEN an RSA key pair for token signing AND controller config uses statedir at the state directory AND controller config uses https://blobs.example.com as artifact store AND controller config uses https://auth.example.com as authentication AND controller config uses https://notify.example.com as notify AND an access token for user with scopes ... uapi_pipelines_post ... uapi_projects_post ... uapi_projects_id_status_put ... uapi_projects_id_status_get ... uapi_projects_id_builds_get ... uapi_workers_id_get ... uapi_builds_get ... uapi_builds_id_get ... uapi_logs_id_get AND a running ick controller Add a couple of projects. WHEN user makes request POST /pipelines with a valid token and body ... { ... "pipeline": "do_something", ... "actions": [ ... { "where": "host", "shell": "something" } ... ] ... } THEN result has status code 201 WHEN user makes request POST /projects with a valid token and body ... { ... "project": "first", ... "pipelines": ["do_something"] ... } THEN result has status code 201 WHEN user makes request POST /projects with a valid token and body ... { ... "project": "second", ... "pipelines": ["do_something"] ... } THEN result has status code 201 Register a worker. GIVEN an access token for obelix with scopes ... uapi_workers_post ... uapi_work_post ... uapi_work_get WHEN obelix makes request POST /workers with a valid token and body ... { ... } THEN result has status code 201 Build the first project. WHEN user makes request GET /projects/first/+trigger THEN result has status code 200 WHEN obelix makes request GET /work THEN result is step ... { ... "action": "create_workspace", ... "where": "host" ... } WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "first/1", ... "action_id": "1", ... "build_number": 1, ... "worker": "obelix", ... "project": "first", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN obelix makes request GET /work THEN result is step ... { ... "shell": "something", ... "where": "host" ... } WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "first/1", ... "action_id": "2", ... "build_number": 1, ... "worker": "obelix", ... "project": "first", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN obelix makes request GET /work THEN result is step ... { ... "action": "notify" ... } WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "first/1", ... "action_id": "3", ... "build_number": 1, ... "worker": "obelix", ... "project": "first", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN obelix makes request GET /work THEN body matches {} WHEN user requests list of builds THEN the list of builds is ["first/1"] Build second project. WHEN user makes request GET /projects/second/+trigger THEN result has status code 200 WHEN obelix makes request GET /work THEN result is step ... { ... "action": "create_workspace", ... "where": "host" ... } WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "second/1", ... "action_id": "1", ... "worker": "obelix", ... "project": "second", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN obelix makes request GET /work THEN result is step ... { ... "shell": "something", ... "where": "host" ... } WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "second/1", ... "action_id": "2", ... "worker": "obelix", ... "project": "second", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN obelix makes request GET /work THEN result is step ... { ... "action": "notify" ... } WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "second/1", ... "action_id": "3", ... "build_number": 1, ... "worker": "obelix", ... "project": "second", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN obelix makes request GET /work THEN body matches {} WHEN user requests list of builds THEN the list of builds is ["first/1", "second/1"] Finish up. FINALLY stop ick controller # Build two projects concurrently This scenario tests the controller API to simulate a build. SCENARIO build two projects concurrently Set up the controller. GIVEN an RSA key pair for token signing AND controller config uses statedir at the state directory AND controller config uses https://blobs.example.com as artifact store AND controller config uses https://auth.example.com as authentication AND controller config uses https://notify.example.com as notify AND an access token for user with scopes ... uapi_pipelines_post ... uapi_projects_post ... uapi_projects_id_status_put ... uapi_projects_id_status_get ... uapi_projects_id_builds_get ... uapi_workers_id_get ... uapi_builds_get ... uapi_builds_id_get ... uapi_logs_id_get AND a running ick controller Add a couple of projects. WHEN user makes request POST /pipelines with a valid token and body ... { ... "pipeline": "do_something", ... "actions": [ ... { "where": "host", "shell": "something" } ... ] ... } THEN result has status code 201 WHEN user makes request POST /projects with a valid token and body ... { ... "project": "first", ... "pipelines": ["do_something"] ... } THEN result has status code 201 WHEN user makes request POST /projects with a valid token and body ... { ... "project": "second", ... "pipelines": ["do_something"] ... } THEN result has status code 201 Register a couple of workers. GIVEN an access token for asterix with scopes ... uapi_workers_post ... uapi_work_post ... uapi_work_get WHEN asterix makes request POST /workers with a valid token and body ... { ... } THEN result has status code 201 GIVEN an access token for obelix with scopes ... uapi_workers_post ... uapi_work_post ... uapi_work_get WHEN obelix makes request POST /workers with a valid token and body ... { ... } THEN result has status code 201 Trigger both projects. WHEN user makes request GET /projects/first/+trigger THEN result has status code 200 WHEN user requests list of builds THEN the list of builds is ["first/1"] WHEN asterix makes request GET /work THEN result is step ... { ... "action": "create_workspace", ... "where": "host" ... } WHEN user requests list of builds THEN the list of builds is ["first/1"] WHEN user makes request GET /projects/second/+trigger THEN result has status code 200 WHEN obelix makes request GET /work THEN result is step ... { ... "action": "create_workspace", ... "where": "host" ... } WHEN user requests list of builds THEN the list of builds is ["first/1", "second/1"] WHEN asterix makes request POST /work with a valid token and body ... { ... "build_id": "first/1", ... "action_id": "1", ... "build_number": 1, ... "worker": "asterix", ... "project": "first", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN asterix makes request GET /work THEN result is step ... { ... "shell": "something", ... "where": "host" ... } WHEN obelix makes request GET /work THEN result is step ... { ... "action": "create_workspace", ... "where": "host" ... } WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "second/1", ... "action_id": "1", ... "build_number": 1, ... "worker": "obelix", ... "project": "second", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN obelix makes request GET /work THEN result is step ... { ... "shell": "something", ... "where": "host" ... } WHEN asterix makes request POST /work with a valid token and body ... { ... "build_id": "first/1", ... "action_id": "2", ... "worker": "asterix", ... "project": "first", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN asterix makes request GET /work THEN result is step ... { ... "action": "notify" ... } WHEN asterix makes request POST /work with a valid token and body ... { ... "build_id": "first/1", ... "action_id": "3", ... "worker": "asterix", ... "project": "first", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN asterix makes request GET /work THEN body matches {} WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "second/1", ... "action_id": "2", ... "worker": "obelix", ... "project": "second", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN obelix makes request GET /work THEN result is step ... { ... "action": "notify" ... } WHEN obelix makes request POST /work with a valid token and body ... { ... "build_id": "second/1", ... "action_id": "3", ... "worker": "obelix", ... "project": "second", ... "exit_code": 0, ... "stdout": "", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 WHEN obelix makes request GET /work THEN body matches {} WHEN user requests list of builds THEN the list of builds is ["first/1", "second/1"] Finish up. FINALLY stop ick controller