Merge pull request #8 from starpit/add_since_to_activations_list

add support for a since query to activations.list
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..52ad81f
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+  - "6"
+  - "4"
diff --git a/lib/actions.js b/lib/actions.js
index 181802c..f66ea0e 100644
--- a/lib/actions.js
+++ b/lib/actions.js
@@ -61,7 +61,7 @@
 
   action (method, options) {
     const action = options.actionName
-    const namespace = this.namespace(options)
+    const namespace = encodeURIComponent(this.namespace(options))
     const params = this.params(method, `namespaces/${namespace}/actions/${action}`)
 
     if (!action) {
diff --git a/lib/messages.js b/lib/messages.js
index 6f88d69..ffd2759 100644
--- a/lib/messages.js
+++ b/lib/messages.js
@@ -13,7 +13,7 @@
   MISSING_PACKAGE_BODY_ERROR: 'Missing mandatory package parameter from options.',
   MISSING_NAMESPACE_ERROR: 'Missing namespace from options, please set a default namespace or pass one in the options.',
   INVOKE_TIMEOUT_ERROR: 'Action invocation timed out before completing.',
-  CREATE_CONFLICT_ERROR: 'Resource already exists with this name but overwrite flag was false.',
+  CREATE_CONFLICT_ERROR: 'Conflict detected updating resource.',
   INVALID_AUTH_ERROR: 'OpenWhisk authentication failed, check API key?',
   MISSING_URL_ERROR: 'Invalid URL for API called, OpenWhisk returned HTTP 404 response.',
   API_SYSTEM_ERROR: 'API call failed, response contained error code.'
diff --git a/package.json b/package.json
index 1e7c81d..da246bf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "openwhisk",
-  "version": "2.1.3",
+  "version": "2.1.5",
   "description": "JavaScript client library for the OpenWhisk platform",
   "main": "lib/main.js",
   "engines": {
diff --git a/test/integration/README.md b/test/integration/README.md
index 82578f6..e853908 100644
--- a/test/integration/README.md
+++ b/test/integration/README.md
@@ -4,7 +4,7 @@
 Running the integration tests requires the following environment variables to be defined.
 
     export OW_API_KEY=<your api key>
-    export OW_API_HOST=<openwhisk host>
+    export OW_API_URL=<openwhisk API url>
     export OW_NAMESPACE=<namespace to test against>
 
 You can retrieve these settings using the `wsk` CLI:
@@ -17,17 +17,21 @@
 ---
 * Name: hello 
 
+```
 function main() {
    return {payload: 'Hello world'};
 }
+```
 
 Action
 ---
 * Name: tests 
 
+```
 function main() {
    return {payload: 'Hello world'};
 }
+```
 
 Trigger
 ---
diff --git a/test/integration/rules.test.js b/test/integration/rules.test.js
index 3869c21..9473e2d 100644
--- a/test/integration/rules.test.js
+++ b/test/integration/rules.test.js
@@ -51,7 +51,8 @@
   })
 })
 
-test('create, get and delete a rule', t => {
+// Running update tests conconcurrently leads to resource conflict errors.
+test.serial('create, get and delete a rule', t => {
   const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
 
   const errors = err => {
@@ -69,12 +70,12 @@
       t.is(rule_result.name, result.name)
       t.is(rule_result.namespace, NAMESPACE)
       t.pass()
-      return rules.delete({ruleName: 'random_rule_test'}).catch(errors)
-    }).catch(errors)
+      return rules.disable({ruleName: 'random_rule_test'}).then(() => rules.delete({ruleName: 'random_rule_test'}))
+    })
   }).catch(errors)
 })
 
-test('create and update a rule', t => {
+test.serial('create and update a rule', t => {
   const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
 
   const errors = err => {
@@ -88,10 +89,12 @@
     t.is(result.namespace, NAMESPACE)
     t.is(result.action, 'hello')
     t.is(result.trigger, 'sample')
-    return rules.update({ruleName: 'random_update_test', action: 'tests', trigger: 'sample'}).then(update_result => {
-      t.is(update_result.action, 'tests')
-      t.pass()
-      return rules.delete({ruleName: 'random_update_test'}).catch(errors)
+    return rules.disable({ruleName: 'random_update_test'}).then(() => {
+      return rules.update({ruleName: 'random_update_test', action: 'tests', trigger: 'sample'}).then(update_result => {
+        t.is(update_result.action, 'tests')
+        t.pass()
+        return rules.delete({ruleName: 'random_update_test'}).catch(errors)
+      })
     }).catch(errors)
   }).catch(errors)
 })
diff --git a/test/unit/actions.test.js b/test/unit/actions.test.js
index 2ae0f87..aac323c 100644
--- a/test/unit/actions.test.js
+++ b/test/unit/actions.test.js
@@ -372,3 +372,41 @@
   const actions = new Actions(params)
   return actions.invoke({actionName: action_name, blocking: true})
 })
+
+const Packages = proxyquire('../../lib/packages.js', {'./base_operation': ctor})
+test('create a new package, then create a new action in that package', t => {
+  const params = {api: 'https://openwhisk.ng.bluemix.net/api/v1/', api_key: 'user_authorisation_key', namespace: 'default'}
+  const package_name = 'package_name'
+  const packageBody = {version: '1.0.0', publish: true, annotations: [], parameters: [], binding: {}}
+
+  stub.request = req => {
+    t.is(req.url, `${params.api}namespaces/${params.namespace}/packages/${package_name}`)
+    t.is(req.headers.Authorization, `Basic ${new Buffer(params.api_key).toString('base64')}`)
+    t.is(req.method, 'PUT')
+    t.deepEqual(req.body, packageBody)
+    t.deepEqual(req.qs, {})
+    return Promise.resolve()
+  }
+
+  const packages = new Packages(params)
+  return packages.create({packageName: package_name, package: packageBody})
+	.then(() => {
+	    const params2 = {api: 'https://openwhisk.ng.bluemix.net/api/v1/', api_key: 'user_authorisation_key', namespace: `default/${package_name}`}
+	    const action_name = 'action_name'
+	    const action = 'function main() { // main function body};'
+
+	    stub.request = req => {
+		t.is(req.url, `${params2.api}namespaces/${encodeURIComponent(params2.namespace)}/actions/${action_name}`)
+		t.is(req.headers.Authorization, `Basic ${new Buffer(params.api_key).toString('base64')}`)
+		t.is(req.method, 'PUT')
+		t.deepEqual(req.body, {exec: {kind: 'nodejs', code: action}})
+		t.deepEqual(req.qs, {})
+		return Promise.resolve()
+	    }
+
+	    t.plan(10)
+
+	    const actions = new Actions(params2)
+	    return actions.create({actionName: action_name, action: action})
+	})
+})
diff --git a/test/unit/base_operation.test.js b/test/unit/base_operation.test.js
index 0ff8e09..39124a8 100644
--- a/test/unit/base_operation.test.js
+++ b/test/unit/base_operation.test.js
@@ -9,7 +9,7 @@
   t.throws(() => base_operation.handle_errors({statusCode: 403}), /authentication failed/)
   t.throws(() => base_operation.handle_errors({statusCode: 404}), /HTTP 404/)
   t.throws(() => base_operation.handle_errors({statusCode: 408}), /timed out/)
-  t.throws(() => base_operation.handle_errors({statusCode: 409}), /action already exists/i)
+  t.throws(() => base_operation.handle_errors({statusCode: 409}), /Conflict/)
   t.throws(() => base_operation.handle_errors({statusCode: 500, error: {}}), /API call failed/)
   t.throws(() => base_operation.handle_errors({statusCode: 500, error: {response: {result: {error: 'custom'}}}}), /custom/)
 })