Reading a bash acceptance test

From Alistair Mann / csi18n
Jump to: navigation, search

Here's an example storyboard expressed in a Bash acceptance test. The example uploads a new translation in JSON format, then retrieves it in serialized PHP format, displaying what the server stores to the user.

  1.  #!/bin/bash
  2.  source acceptance_tests/predefined_codes.sh
  3.  #
  4.  # POST#1. OP wants to upload a suggested translation
  5.  # Upload it, then extract the "Location: " header where
  6.  # the upload ended up.
  7.  echo "POSTING";
  8.  curl -i -X POST -H 'Connection: close' -H 'X-Track-Security: true' -H 'X-APIKey: '$SH_APIKEY_MPSVR_GUI -H 'Content-Type: application/json;v=1.0' -u 'test05:test' --data '{"csi18n_xlate_resource":{"language":"en-CA","translation":"Hello wurld, eh","visibility":"personal"}}' $SH_SERVER'/newmarks/me/Help_translate:' | grep Location | sed 's|Location: ||' | sed 's|.$||' >/tmp/curl.out
  9.  LOC1=`cat /tmp/curl.out`
  10.  echo ""
  11.  #
  12.  # GET#3. Obtain back the translation just uploaded
  13.  echo "GETTING";
  14.  curl -i -X GET -H 'Accept: text/plain' -H 'Connection: close' -H 'X-Track-Security: true' -H 'X-APIKey: '$SH_APIKEY_MPSVR_GUI -u 'test05:test' $LOC1
  15.  echo ""

Let's review the parts in turn:

#!/bin/bash

All bash scripts start with this, or something like it, informing the OS how to handle the script.

source acceptance_tests/predefined_codes.sh

All my acceptance tests begin with this: it informs the script that it should include the data in the given file. Were we to look at that file we'd see:

SH_SERVER='https://rest.mpsvr.com:443'
SH_APIKEY_MPSVR_GUI='3cac23b8442e3971f1d10660cc779b9f'

Saying to create two variables denoting where and how to find the csi18n server, the second the APIKey to use. Back to the main script:

#
# POST#1. OP wants to upload a suggested translation
# Upload it, then extract the "Location: " header where
# the upload ended up.

All lines (other than the first) beginning with the hash symbol ("#") are comments, and are ignored.

echo "POSTING";

'Echo' sends whatever follows back to user.

curl -i -X POST -H 'Connection: close' -H 'X-Track-Security: true' -H 'X-APIKey: '$SH_APIKEY_MPSVR_GUI -H 'Content-Type: application/json;v=1.0' -u 'test05:test' --data '{"csi18n_xlate_resource":{"language":"en-CA","translation":"Hello wurld, eh","visibility":"personal"}}' $SH_SERVER'/newmarks/me/Help_translate:' | grep Location | sed 's|Location: ||' | sed 's|.$||' >/tmp/curl.out

Notice the use of the pipe ("|") before grep and each sed. In a bash script the pipe says to feed the output of the previous statement as input to the next statement. With that in mind, let's break the above down further:

curl -i -X POST -H 'Connection: close' -H 'X-Track-Security: true' -H 'X-APIKey: '$SH_APIKEY_MPSVR_GUI -H 'Content-Type: application/json;v=1.0' -u 'test05:test' --data '{"csi18n_xlate_resource":{"language":"en-CA","translation":"Hello wurld, eh","visibility":"personal"}}' $SH_SERVER'/newmarks/me/Help_translate:'

Here cURL is use to connect to the server, POST a resource, and retrieve the result back. It has a number of parameters:

-i retrieve the HTTP headers sent from the server, as well as the body
-X POST once connected to the server, we'll make use of the POST method in HTTP
-H 'Connection: close' close the connection as soon as it's completed - the server might keep the connection open for a few seconds otherwise just in case we sent more
-H 'X-Track-Security: true' instructs the service to keep notes as the request passes through the security subsystem. That log is not normally available, so using this header yourself will serve only to slow your request down.
-H 'X-APIKey: '$SH_APIKEY_MPSVR_GUI send the APIKey included as a variable with source above.
-H 'Content-Type: application/json;v=1.0' a header to describe the data we send below informing the server that the data is JSON formatted, and its version number is 1.0
-u 'test05:test' send the username then the password over. The two are separated by a colon.
--data '{"csi18n_xlate_resource":{"language":"en-CA","translation":"Hello wurld, eh","visibility":"personal"}}' send the following data to the server. You may recognise this data as a JSON representation, however we could have sent anything including binary (when suitably encoded.)
$SH_SERVER'/newmarks/me/Help_translate:' using the location of the server described in the variable from source above, also add in the path and name of the resource to be used and send the lot to the server

cURL will make the connection, retrieve the response headers and body, and pass them through the first pipe to grep

grep Location

Grep's purpose here is to check for the presence of text, and if present, return the whole line containing it. Thus, this line takes the retrieved headers and body from the server, discards all the lines except the line with "Location" in it, and passes the whole of that line through the next pipe to sed

sed 's|Location: ||'

Sed's purpose here is to switch the text "Location: " for an empty string. That is, remove the field name, colon and first space from the data, then pass that data through the next pipe to the second sed

sed 's|.$||' >/tmp/curl.out

Sed's purpose this time is to remove the last character - a new line - from the text, then drop what's left into an emptied temporary file.

So that whole curl line is to POST a new resource, obtain its location on the server, and leave that location in a file. Carrying on:

LOC1=`cat /tmp/curl.out`
echo ""

The first line here sets a new variable to contain the location curl just received, the second will print a newline.

#
# GET#3. Obtain back the translation just uploaded
echo "GETTING";
curl -i -X GET -H 'Accept: text/plain' -H 'Connection: close' -H 'X-Track-Security: true' -H 'X-APIKey: '$SH_APIKEY_MPSVR_GUI -u 'test05:test' $LOC1
echo ""

This second exchange you may be able to read already:

  • Tell the user we're about do the GET
  • Connect to the server and retrieve the resource available at the location given us above
  • Because we didn't pipe the response anywhere, it's printed to the user
  • Add a blank line.

And the script is complete.

If we actually ran the above script, it would show us:

$ acceptance_tests/bash_test_Example.sh
POSTING
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   169  100    32  100   137    111    479 --:--:-- --:--:-- --:--:--   805

GETTING
HTTP/1.1 200 Ok. Done. Sorted.
Date: Wed, 20 Aug 2014 12:20:12 GMT
Server: Apache
X-Testing-Dupe: HTTP/1.1 200 Ok. Done. Sorted.
X-CDMI-Date: 2014-08-20T13:20:12.928337Z
X-Server: mpsvr/2014Aug20-105534
ETag: "24188e241186b69476342602690b56ad"
Content-Length: 926
X-CDMI-Last-Modified: 2014-08-19T21:50:47.111401Z
Last-Modified: Tue, 19 Aug 2014 21:50:47 BST
Connection: close
Content-Type: text/vnd.php.serialized

a:8:{s:21:"csi18n_xlate_resource";a:7:{s:8:"language";s:5:"en-CA";s:11:"translation";s:15:"Hello wurld, eh";s:10:"visibility";s:8:"personal";s:20:"datetime_last_change";s:27:"2014-08-19T21:50:47.111401Z";s:4:"crid";i:287;s:3:"sid";i:1;s:7:"ip_addr";s:15:"109.158.156.239";}s:16:"previous_uploads";s:61:"/xlates/1/search?crid=287&page=-1&visibility=personal";s:13:"later_uploads";s:60:"/xlates/1/search?crid=287&page=0&visibility=personal";s:22:"previous_uploads_by_ip";s:82:"/xlates/1/search?crid=287&ipv4=109.158.156.239&page=-1&visibility=personal";s:19:"later_uploads_by_ip";s:81:"/xlates/1/search?crid=287&ipv4=109.158.156.239&page=0&visibility=personal";s:11:"search_form";s:24:"/xlates/1/search";s:22:"csi18n_xlate_first_url";s:53:"/xlates/1/Hello,-World/en-CA/personal/287/287";s:21:"csi18n_xlate_last_url";s:53:"/xlates/1/Hello,-World/en-CA/personal/287/287";}

$