Code Small FTW: Wednesday

Yesterday’s installment including The Design and Testing the App can be found here.

Pomodoro Break

Getting the App side of the project into reasonable shape took 14 pomodoros (6 hours of work time). This seems like a good point to reflect a little.

I started with the App side, because I know it is a weak point for me. Working in Pomodoros, I was highly productive the first day with the exception of the end of the day. That hour-and-a-half of unstructured time, working in frustration at being stuck with a view component not behaving the way I wanted it to, was the least productive time of the day.

During most of the the brief reflection periods at the end of each Pomodoro, I remember catching myself going too far at one point or other. It’s the catching myself, though, that made all the difference. Being truly focused on the simplest piece of functionality that would work yielded working results quickly.

As I continued to work through the App side of things, some usability issues emerged, which led to decisions to expand the codebase. For instance, it became clear that I should be able to save the token so that I didn’t have to re-enter it every time I launched the App. This was in line with the goal of the App being as easy to use as pen and paper. Most of the time, I made notes of these revelations, to be implemented later on. The initial goal of getting the app in working order was still top priority. So, on to the server side!

The Server Side

Now I was back in more comfortable territory. I knew that I wanted the server side implemented as simple RESTful API calls. The team I work with are the stewards of a bdd testing framework called peanut for use with node (originally authored by Robert Malko). Peanut uses the standard gherkin syntax for writing test scenarios. This is where I started.

I made the decision to NOT implement any auth functionality until the end. Since I am familiar with express, I knew that I would be able to implement the auth code as middleware and easily add it in to existing route code as a simple parameter. This enabled me to focus on the core functionality I needed.

Based on the first design pomodoro, I knew that I needed the following API endpoints:

  • transaction
  • history
  • process

I was able to complete the entire API in 3 Pomodoros. Not having any authentication layer in place made it easier to test the API functionality. The transaction endpoint takes in transaction data and saves it to the mongo database. The history endpoint returns all history from the mongo database. Since we process our expenditures on a near daily basis, there should not ever be too much data to return from the history call, so I didn’t worry about paging. The process endpoint takes in a bunch of transaction ids, removes them from the database and returns the updated list of remaining transactions.

Since I am publishing this series of articles and since the network traffic is ultimately going over the public Internet, it was clear that some sort of authentication layer was important. As with the rest of the design and implementation for this App, I wanted the authentication to be as simple as possible. As usual, the best place to start was the tests. Here is what the transaction feature looked like before any authentication functionality was built:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Feature: Transaction
  In order to record individual finances
  As a user
  I want to add credit and debit transactions
 
  Background:
    Given I have cleaned up
    And I have the following transaction data
      | date     | amount | description        | category           |
      | 2/1/2012 | 12.45  | some cool purchase | some cool categroy |
 
  Scenario: Success: Submit credit transaction
    When I submit the transaction
    Then I should get a successful response

And here is what it looked like after adding the auth lines to it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Feature: Transaction
  In order to record individual finances
  As a user
  I want to add credit and debit transactions
 
  Background:
    Given I have cleaned up
    And I have logged out
    And a user exists with auth token "1234567890"
    And I have the following transaction data
      | date     | amount | description        | category           |
      | 2/1/2012 | 12.45  | some cool purchase | some cool categroy |
 
  Scenario: Success: Submit credit transaction
    When I authenticate with "1234567890"
    And I submit the transaction
    Then I should get a successful response
 
  Scenario: Failure: auth problem
    When I authenticate with "bad auth token"
    Then I should get a failure response

Lines 8 and 9 introduce the concept of logging out and having a user present in the database. Line 15 introduces the concept of authenticating. The second scenario ensures that a bad login results in a failure.

After these lines were added to the test, the first run of peanut simply returned the missing signatures for the new functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Given(/^I have logged out$/, function(step) {
  step.pending();
});
 
Given(/^a user exists with auth token "([^"]*?)"$/, function(step, arg) {
  step.pending();
});
 
When(/^I authenticate with "([^"]*?)"$/, function(step, arg) {
  step.pending();
});
 
Then(/^I should get a failure response$/, function(step) {
  step.pending();
});

The implementation of the “And I have logged out” step, was to use the request library to make a GET call to the logout endpoint. I could then assert that the response came back with a 200 status, indicating that the call was processed normally. That code looks something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Given(/^I have logged out$/, function(step) {
  var self = this;
  var endpoint = "http://localhost:3000/api/v1/logout";
 
  request({
    method: 'GET',
    uri: endpoint
  }, function(err, res, body) {
    body = JSON.parse(body);
    if (err) throw err;
    assert.equal(res.statusCode, 200);
    assert.equal(body.status, 'SUCCESS');
    step.done();
  })
});

Line 3 indicates the endpoint to be hit. Lines 11 and 12 assert that we get the proper response status code and that the response message is what we expect it to be.

Of course, the first time I ran the test, it failed because what came back from the server was a status code of 404, since the endpoint did not yet exist. Now that I had a failing test that asserted my expectations, I could implement the actual endpoint and underlying code. BDD goodness in action.

Here’s a look at the implementation code (simplified here for brevity):

1
2
3
4
app.get('/api/v1/logout', function(req, res) {
  req.logout();
  res.send({status: "SUCCESS", result: "logged out"});
})

Line 2 destroys the session and line 3 responds. The test now passes. Boom!

For the ‘And a user exists with auth token “1234567890”’ implementation, I wrote a test that made use of a (non-existent) user model to insert a user document (think “record” for those unfamiliar with no-sql databases) in mongo. The test failed as expected which prompted me to write the user model implementation. I did this using the native mongo driver for node.js as a more serious object mapping library (like mongoose) seemed like it would be overkill for this little project.

The ‘When I authenticate with “1234567890”’ was a little more complex in the end, but starting with tests always makes it smoother. As usual, I wrote the test for the interface I wanted. Just like with logout, I wrote a test that hit an endpoint, got back a response and asserted expectations on what that response should contain. This time, I made a POST call and passed in the auth token parameter to the endpoint (1234567890 in this case). And the test failed right on cue.

Crap! On a plane – just wrote for about a half hour without being in a Pomodoro. It is still not ingrained in me, even though I know the benefit of its use. Taking a break now and I will fire up the Pomodoro when I come back.

I used the express form strategy and middleware mechanism to implement authentication on the server side. A more in-depth discussion is outside the scope of this post. The end result was an authenticate endpoint that had my test passing. All that was left to do was to require that a user had authenticated in order to make use of the other endpoints. Express makes this easy by allowing you to hook up middleware. All I had to do was change code like this:

1
2
3
...
app.post('/api/v1/transaction', function(req, res) {
...

into code like this:

1
2
3
4
5
...
var isAuthenticated = require('../middleware/authentication').isAuthenticated;
...
app.post('/api/v1/transaction', isAuthenticated, function(req, res) {
...

The job of the middleware is to exercise some logic and then allow the original call to proceed or not. If the user had already been authenticated, then a call to next() allows the processing to continue. Otherwise, an error response is sent back to the caller and the original call is not completed.

I changed all of the method signatures of the existing endpoints to call the isAuthenticated middleware. Now, all of my original tests were broken, since none of those tests had any authentication. The fix for the tests was to add in the auth steps, just like I did for the transaction endpoint. So, the history feature changed from:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Feature: History
  In order manage transactions
  As a user
  I want to get and update history
 
  Background:
    Given I have cleaned up
    And I have the following transaction data
      ...
    And the transaction data has been submitted
 
  Scenario: Get history
    When I get the history
    Then I should get back the history with ids

to

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Feature: History
  In order manage transactions
  As a user
  I want to get and update history
 
  Background:
    Given I have cleaned up
    And I have logged out
    And a user exists with auth token "1234567890"
    And I have the following transaction data
      ...
    And the transaction data has been submitted
 
  Scenario: Get history
    When I authenticate with "1234567890"
    And I get the history
    Then I should get back the history with ids

Notice the lines 8, 9 and 15. Since I had already implemented these steps, altering the the rest of the scenarios in this way made all the tests pass again, thereby proving that authentication had been properly integrated.

I’ve spent a bunch of time in this section going through my development process to show that deferring the authentication implementation paid dividends for me in terms of speed. My initial focus was on the critical business logic for entering transactions, retrieving history and processing historical transactions. Integrating authentication and authorization later on was quite simple whereas solving that problem first, in this case, might have proved a hinderance since the details of the services (core business logic) hadn’t been worked out yet.

The entire server side took 6 pomodoros (2 1/2 hours work time). I had gone through a total of 20 pomodoros since I started, (8 1/3 hours work time) over the course of 5 days to get to this point.

Tomorrow’s installment: Putting it all together



Leave a Reply