Skip to content
This repository has been archived by the owner on Nov 8, 2024. It is now read-only.

Commit

Permalink
Merge pull request #616 from apiaryio/honzajavorek/use-just-1st-req-res
Browse files Browse the repository at this point in the history
Solution for #615 and #558 (breaking)
  • Loading branch information
honzajavorek authored Sep 9, 2016
2 parents 23efffd + 82a077d commit 8c909a8
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 24 deletions.
79 changes: 60 additions & 19 deletions docs/how-to-guides.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ To have an idea where we can hook our arbitrary code, we should first ask Dredd
```
$ dredd api-description.yml http://localhost:3000 --names
info: /categories > POST
info: /category/{id} > DELETE
info: /category/{id}/items > POST
info: /categories > POST > 200 > application/json
info: /category/{id} > DELETE > 200 > application/json
info: /category/{id}/items > POST > 200 > application/json
```
Now we can create a `hooks.js` file. The file will contain setup and teardown of the database fixture:
Expand Down Expand Up @@ -165,12 +165,10 @@ To test various scenarios, you will want to write each of them into a separate A

For workflows to work properly, you'll also need to keep **shared context** between individual HTTP transactions. You can use [hooks](hooks.md) in order to achieve that. See tips on how to [pass data between transactions](hooks.md#sharing-data-between-steps-in-request-stash).

### Example
### API Blueprint Example

Imagine we have a simple workflow described:

#### API Blueprint

```markdown
FORMAT: 1A
Expand Down Expand Up @@ -209,7 +207,52 @@ FORMAT: 1A
```

#### Swagger
### Writing Hooks

To have an idea where we can hook our arbitrary code, we should first ask Dredd to list all available transaction names:

```
$ dredd api-description.apib http://localhost:3000 --names
info: /login > POST
info: /cars > GET
info: /cars/{id} > PATCH
```

Now we can create a `hooks.js` file. The code of the file will use global `stash` variable to share data between requests:

```javascript
hooks = require('hooks');
db = require('./src/db');
stash = {}
// Stash the token we've got
after('/login > POST', function (transaction) {
stash.token = JSON.parse(transaction.real.body).token;
});
// Add the token to all HTTP transactions
beforeEach(function (transaction) {
if (stash.token) {
transaction.headers['X-Api-Key'] = stash.token
};
});
// Stash the car ID we've got
after('/cars > GET', function (transaction) {
stash.carId = JSON.parse(transaction.real.body).id;
});
// Replace car ID in request with the one we've stashed
before('/cars/{id} > PATCH', function (transaction) {
transaction.fullPath = transaction.fullPath.replace('42', stash.carId)
transaction.request.uri = transaction.fullPath
})
```

### Swagger Example

Imagine we have a simple workflow described:

```yaml
swagger: "2.0"
Expand Down Expand Up @@ -291,15 +334,15 @@ paths:
type: string
```

#### Writing Hooks
### Writing Hooks

To have an idea where we can hook our arbitrary code, we should first ask Dredd to list all available transaction names:

```
$ dredd api-description.apib http://localhost:3000 --names # for Swagger, use api-description.yml
info: /login > POST
info: /cars > GET
info: /cars/{id} > PATCH
$ dredd api-description.yml http://localhost:3000 --names
info: /login > POST > 200 > application/json
info: /cars > GET > 200 > application/json
info: /cars/{id} > PATCH > 200 > application/json
```

Now we can create a `hooks.js` file. The code of the file will use global `stash` variable to share data between requests:
Expand All @@ -311,7 +354,7 @@ db = require('./src/db');
stash = {}
// Stash the token we've got
after('/login > POST', function (transaction) {
after('/login > POST > 200 > application/json', function (transaction) {
stash.token = JSON.parse(transaction.real.body).token;
});
Expand All @@ -323,12 +366,12 @@ beforeEach(function (transaction) {
});
// Stash the car ID we've got
after('/cars > GET', function (transaction) {
after('/cars > GET > 200 > application/json', function (transaction) {
stash.carId = JSON.parse(transaction.real.body).id;
});
// Replace car ID in request with the one we've stashed
before('/cars/{id} > PATCH', function (transaction) {
before('/cars/{id} > PATCH > 200 > application/json', function (transaction) {
transaction.fullPath = transaction.fullPath.replace('42', stash.carId)
transaction.request.uri = transaction.fullPath
})
Expand Down Expand Up @@ -480,10 +523,8 @@ When using [Swagger][] format, by default Dredd tests only responses with `2xx`
```javascript
var hooks = require('hooks');

hooks.before('/resource > GET', function (transaction, done) {
if (transaction.expected.statusCode[0] == '5') {
transaction.skip = false;
}
hooks.before('/resource > GET > 500 > application/json', function (transaction, done) {
transaction.skip = false;
done();
});
```
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"clone": "^1.0.2",
"coffee-script": "^1.10.0",
"colors": "^1.1.2",
"dredd-transactions": "^1.6.0",
"dredd-transactions": "^3.0.0",
"file": "^0.2.2",
"gavel": "^0.5.3",
"glob": "^7.0.5",
Expand Down
22 changes: 22 additions & 0 deletions test/fixtures/regression-615.apib
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FORMAT: 1A

# Beehive API

## Honey [/honey]

### Retrieve [GET]

+ Request (application/json)
+ Request (application/xml)
+ Response 200
+ Response 500

### Remove [DELETE]

+ Request (application/json)
+ Response 200
+ Response 403
+ Response 500

+ Request (text/plain)
+ Response 200
6 changes: 2 additions & 4 deletions test/fixtures/swagger-multiple-responses.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
var hooks = require('hooks');


hooks.before('/honey > GET', function (transaction, done) {
if (transaction.expected.statusCode[0] == '5') {
transaction.skip = false;
}
hooks.before('/honey > GET > 500 > application/json', function (transaction, done) {
transaction.skip = false;
done();
});
13 changes: 13 additions & 0 deletions test/fixtures/swagger-transaction-names.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

var hooks = require('hooks');


function logTransactionName(transaction, done) {
hooks.log(transaction.name);
done();
}


hooks.before('/honey > GET > 400 > application/json', logTransactionName);
hooks.before('/honey > GET > 500 > application/json', logTransactionName);
hooks.before('/honey > GET > 200 > application/json', logTransactionName);
25 changes: 25 additions & 0 deletions test/integration/dredd-test.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -608,3 +608,28 @@ describe 'Dredd class Integration', ->
assert.notEqual(matches[2][1], 'skip')
)
)

describe('when using Swagger document with hooks', ->
reTransactionName = /hook: (.+)/g
matches = undefined

beforeEach((done) ->
execCommand(
options:
path: './test/fixtures/multiple-responses.yaml'
hookfiles: './test/fixtures/swagger-transaction-names.js'
, (err) ->
matches = []
matches.push(groups[1]) while groups = reTransactionName.exec(stdout)
done(err)
)
)

it('transaction names contain status code and content type', ->
assert.deepEqual(matches, [
'/honey > GET > 200 > application/json'
'/honey > GET > 400 > application/json'
'/honey > GET > 500 > application/json'
])
)
)
1 change: 1 addition & 0 deletions test/regressions/regression-319-354-test.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ parseJSON = (body) ->
body


# This can be removed once https://github.com/apiaryio/dredd/issues/341 is done
parseOutput = (output) ->
# Parse individual entries (deals also with multi-line entries)
entries = []
Expand Down
93 changes: 93 additions & 0 deletions test/regressions/regression-615-test.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{assert} = require 'chai'
{exec} = require 'child_process'
express = require 'express'
clone = require 'clone'


PORT = 3333
DREDD_BIN = require.resolve '../../bin/dredd'


runDredd = (descriptionFile, cb) ->
result = {}
cmd = "#{DREDD_BIN} #{descriptionFile} http://localhost:#{PORT} -ed --no-color"

cli = exec cmd, (err, stdout, stderr) ->
result.exitStatus = err?.code or null
result.stdout = '' + stdout
result.stderr = '' + stderr

cli.on 'close', (code) ->
result.exitStatus ?= code if code
cb null, result


parseJSON = (body) ->
return undefined unless body
try
JSON.parse body
catch
body


# This can be removed once https://github.com/apiaryio/dredd/issues/341 is done
parseOutput = (output) ->
# Parse individual entries (deals also with multi-line entries)
entries = []
entry = undefined
for line in output.split /\r?\n/
match = line.match /^(\w+): (.+)?$/
if match
if entry
entry.body = entry.body.trim()
entries.push entry
entry = {label: match[1], body: match[2] or ''}
else
entry.body += "\n#{line.trim()}"

# Correction of following situation:
#
# fail: POST /customers duration: 13ms
# fail: body: At '/name' Invalid type: null (expected string)
# body: At '/shoeSize' Invalid type: string (expected number)
entries = entries.filter (entry, i) ->
previousEntry = entries[i - 1]
if entry.label is 'body' and previousEntry.label is 'fail'
previousEntry.body += '\n' + entry.body
return false
return true

# Re-arrange data from entries
results = {summary: '', failures: []}
for entry in entries
switch entry.label
when 'complete' then results.summary = entry.body
when 'fail' then results.failures.push entry.body
return results


describe 'Regression: Issue #615', ->
requests = []
results = undefined

beforeEach (done) ->
app = express()

# Attaching endpoint for each testing scenario
app.all '/honey', (req, res) ->
res.status(200).send ''

# Spinning up the Express server, running Dredd, and saving results
server = app.listen PORT, ->
runDredd './test/fixtures/regression-615.apib', (err, result) ->
results = parseOutput result.stdout
server.close done

it 'outputs no failures', ->
# Intentionally not testing just '.length' as this approach will output the difference
assert.deepEqual results.failures, []
it 'results in exactly three tests', ->
assert.include results.summary, '3 total'
it 'results in three passing tests', ->
# Ensures just the 200 responses were selected, because the server returns only 200s
assert.include results.summary, '3 passing'

0 comments on commit 8c909a8

Please sign in to comment.