Home

app.js (link)

const Sequelize = require('sequelize')

const UserFactory = require('./lib/UserFactory')

const TransactionFactory = require('./lib/TransactionFactory')

 

const sequelize = new Sequelize('digithera_test', 'root', 'supersecure', {

const Sequelize = require('sequelize')

const UserFactory = require('./lib/UserFactory')

const Transaction = require('./lib/Transaction')

const TransactionFactory = require('./lib/TransactionFactory')

 

const sequelize = new Sequelize('digithera_test', 'root', 'supersecure', {

const transactionFactory = new TransactionFactory(sequelize)

try {

const user = await userFactory.find('lucas')

console.log(user)

const transactions = await transactionFactory.getStatementForUser(user)

console.log(transactions)

const users = await userFactory.getUsersWithBalanceOver(483000)

console.log(users)

} catch (err) {

console.error(err)

}

const transactionFactory = new TransactionFactory(sequelize)

try {

const user = await userFactory.find('lucas')

console.log('Found user: ', user)

 

const transactions = await transactionFactory.getStatementForUser(user)

console.log(

`Transactions for user ${user.username} (balance: ${Transaction.getBalanceFormatted(transactions)})`,

transactions

)

 

const balance = argv[2] || 0

const usersWithBalanceGreaterThan = await userFactory.findUsersWithBalanceGreaterThan(balance)

console.log(`Users with balance greater than ${balance}: `, usersWithBalanceGreaterThan)

} catch (err) {

console.error(err)

}

db.sql (link)

 

CREATE TABLE purchases (

id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,

user_id INT NOT NULL,

product_id INT NOT NULL,

transaction_id INT NOT NULL,

discount_id INT DEFAULT NULL,

 

CREATE TABLE purchases (

id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,

user_id INT NOT NULL COMMENT ('Can be derived from transaction but having dealt with highly denormalized databases it can be useful to have references like this when building reports.'),

product_id INT NOT NULL,

transaction_id INT NOT NULL,

discount_id INT DEFAULT NULL,

/dev/null (link)

lib/Discount.js (link)

"use strict"

 

module.exports = class Discount {

/**

* @param {number} id

* @param {number} percentage

* @param {string} createdAt

* @param {string} updatedAt

*/

constructor (id, percentage, createdAt, updatedAt) {

this.id = id

this.percentage = percentage

this.createdAt = createdAt

this.updatedAt = updatedAt

}

 

/**

* @returns {number}

*/

get percentageFormatted () {

return this.percentage ? this.percentage / 100 : 0

}

 

/**

* @returns {module.Purchase}

* @constructor

*/

static NullDiscount () {

return new this(null, null, null, null)

}

}

/dev/null (link)

lib/Product.js (link)

"use strict"

 

module.exports = class Product {

/**

* @param {number} id

* @param {string} name

* @param {number} price

* @param {string} createdAt

* @param {string} updatedAt

*/

constructor (id, name, price, createdAt, updatedAt) {

this.id = id

this.name = name

this.price = price

this.createdAt = createdAt

this.updatedAt = updatedAt

}

 

/**

* @returns {number}

*/

get priceFormatted () {

return this.price ? this.price / 100 : 0

}

 

/**

* @returns {module.Product}

* @constructor

*/

static NullProduct () {

return new this(null, null, null, null, null)

}

}

/dev/null (link)

lib/Purchase.js (link)

"use strict"

 

const Discount = require('./Discount')

const Product = require('./Product')

 

module.exports = class Purchase {

/**

* @param {number} id

* @param {number} amount

* @param {number} discountApplied

* @param {string} createdAt

* @param {string} updatedAt

* @param {module.Product} product

* @param {module.Discount} discount

*/

constructor (id, amount, discountApplied, createdAt, updatedAt, product, discount) {

this.id = id

this.amount = amount

this.discountApplied = discountApplied

this.createdAt = createdAt

this.updatedAt = updatedAt

this.product = product

this.discount = discount

}

 

/**

* @returns {number}

*/

get amountFormatted () {

return this.amount ? this.amount / 100 : 0

}

 

/**

* @returns {number}

*/

get discountAppliedFormatted () {

return this.discountApplied ? this.discountApplied / 100 : 0

}

 

/**

* @returns {module.Purchase}

* @constructor

*/

static NullPurchase () {

return new this(null, null, null, null, null, null, Product.NullProduct(), Discount.NullDiscount())

}

}

lib/Transaction.js (link)

"use strict"

 

module.exports = class Transaction {

constructor (id, user_id, amount, created_at) {

this.id = id

this.user_id = user_id

this.amount = amount

this.created_at = created_at

}

 

get amountFormatted () {

return this.amount / 100

}

}

"use strict"

 

module.exports = class Transaction {

/**

* @param {number} id

* @param {number} amount

* @param {string} createdAt

* @param {module.User} user

* @param {module.Purchase} purchase

*/

constructor (id, amount, createdAt, user, purchase) {

this.id = id

this.amount = amount

this.createdAt = createdAt

this.user = user

this.purchase = purchase

}

 

/**

* @returns {number}

*/

get amountFormatted () {

return this.amount / 100

}

 

/**

* @param {module.Transaction[]} transactions

* @return {number}

*/

static getBalance (transactions) {

return transactions

.map(t => t.amount)

.reduce((l, r) => l + r, 0)

}

 

/**

* @param {module.Transaction[]} transactions

* @return {number}

*/

static getBalanceFormatted (transactions) {

const balance = this.getBalance(transactions)

return balance === 0 ? 0 : balance / 100

}

}

lib/TransactionFactory.js (link)

"use strict"

 

const Transaction = require('./Transaction')

const User = require('./User')

 

"use strict"

 

const Discount = require('./Discount')

const Product = require('./Product')

const Purchase = require('./Purchase')

const Transaction = require('./Transaction')

const User = require('./User')

 

 

/**

* @param {User} user

*/

async getStatementForUser (user) {

const transactions = await this.databaseConnection

.query(

'SELECT * FROM transactions WHERE user_id = ?',

{ replacements: [user.id], type: this.databaseConnection.QueryTypes.SELECT }

)

return transactions.map(t => new Transaction(t.id, t.user_id, t.amount, t.created_at))

}

}

 

/**

* @param {User} user

* @returns {Promise<Transaction[]>}

*/

async getStatementForUser (user) {

// This should probably be masked behind an ORM!

const transactions = await this.databaseConnection

.query(

`

SELECT

transactions.id AS transaction_id,

transactions.user_id,

amount,

transactions.created_at AS transaction_created_at,

purchases.id AS purchase_id,

purchases.discount_percentage AS purchase_discount,

purchases.created_at AS purchase_created_at,

purchases.updated_at AS purchase_updated_at,

products.id AS product_id,

products.name AS product_name,

products.price AS product_price,

products.created_at AS product_created_at,

products.updated_at AS product_updated_at,

discounts.id AS discount_id,

discounts.percentage AS discount_percentage,

discounts.created_at AS discount_created_at,

discounts.updated_at AS discount_updated_at

FROM transactions

LEFT JOIN purchases on transactions.id = purchases.transaction_id

LEFT JOIN products ON purchases.product_id = products.id

LEFT JOIN discounts ON purchases.discount_id = discounts.id

WHERE transactions.user_id = ?

`,

{ replacements: [ user.id ], type: this.databaseConnection.QueryTypes.SELECT }

)

 

return transactions.map(t => {

const product = t.product_id

? new Product(t.product_id, t.product_name, t.product_price, t.product_created_at, t.product_updated_at)

: Product.NullProduct()

 

const discount = t.discount_id

? new Discount(t.discount_id, t.discount_percentage, t.discount_created_at, t.discount_updated_at)

: Discount.NullDiscount()

 

const purchase = t.purchase_id

? new Purchase(

t.purchase_id,

Math.abs(t.amount),

t.purchase_discount,

t.purchase_created_at,

t.purchase_updated_at,

product,

discount

)

: Purchase.NullPurchase()

 

return new Transaction(t.transaction_id, t.amount, t.transaction_created_at, user, purchase)

})

}

}

lib/User.js (link)

"use strict"

 

module.exports = class User {

constructor (id, username, email, created_at, updated_at, balance = null) {

this.id = id

this.username = username

this.email = email

this.created_at = created_at

this.updated_at = updated_at

this.balance = balance

}

 

get balanceFormatted () {

return this.balance ? this.balance / 100 : 0

}

}

"use strict"

 

module.exports = class User {

/**

* @param {number} id

* @param {string} username

* @param {string} email

* @param {string} createdAt

* @param {string} updatedAt

* @param {number|null} balance

*/

constructor (id, username, email, createdAt, updatedAt, balance = null) {

this.id = id

this.username = username

this.email = email

this.createdAt = createdAt

this.updatedAt = updatedAt

this.balance = balance

}

 

/**

* @returns {number}

*/

get balanceFormatted () {

return this.balance ? this.balance / 100 : 0

}

 

/**

* @returns {module.User}

* @constructor

*/

static NullUser() {

return new this(null, null, null, null, null, null)

}

}

lib/UserFactory.js (link)

`,

{ replacements: [username], type: this.databaseConnection.QueryTypes.SELECT }

)

return users.length ? this.convertQueryToObject(users)[0] : null

}

 

async getUsersWithBalanceOver (amount) {

const users = await this.databaseConnection

.query(

`

`,

{ replacements: [username], type: this.databaseConnection.QueryTypes.SELECT }

)

return users.length ? this.convertResultToDomain(users)[0] : null

}

 

/**

* @param {number} amount

* @returns {Promise<User[]>}

*/

async findUsersWithBalanceGreaterThan (amount) {

const users = await this.databaseConnection

.query(

`

`,

{ replacements: [amount], type: this.databaseConnection.QueryTypes.SELECT }

)

return this.convertQueryToObject(users)

}

 

convertQueryToObject(results) {

return results.map(user =>

new User(user.id, user.username, user.email, user.created_at, user.updated_at, user.balance || null)

)

`,

{ replacements: [amount], type: this.databaseConnection.QueryTypes.SELECT }

)

return this.convertResultToDomain(users)

}

 

/**

* @param {object[]} results

* @returns {User[]}

*/

convertResultToDomain (results) {

return results.map(user =>

new User(user.id, user.username, user.email, user.created_at, user.updated_at, user.balance || null)

)

/dev/null (link)

spec/TransactionSpec.js (link)

"use strict"

 

const Purchase = require('../lib/Purchase')

const Transaction = require("../lib/Transaction")

const User = require('../lib/User')

 

describe("The Transaction class", () => {

 

it("should correctly format the amount when requesting the formatted amount", () => {

const transaction = new Transaction(1, 100000, '', User.NullUser(), Purchase.NullPurchase())

 

expect(transaction.amountFormatted).toBe(1000.00)

})

 

it("should sum up the balance of a list of transaction correctly", () => {

const transactions = [

new Transaction(1, 100000, '', User.NullUser(), Purchase.NullPurchase()),

new Transaction(2, -10000, '', User.NullUser(), Purchase.NullPurchase()),

new Transaction(3, 9999, '', User.NullUser(), Purchase.NullPurchase())

]

 

expect(Transaction.getBalance(transactions)).toBe(99999)

})

 

it("should sum the balance to 0 for an empty transaction list", () => {

expect(Transaction.getBalance([])).toBe(0)

})

})

spec/UserFactorySpec.js (link)

}

})

 

const users = await userFactory.getUsersWithBalanceOver(100)

expect(users.length).toBe(2)

})

})

}

})

 

const users = await userFactory.findUsersWithBalanceGreaterThan(100)

expect(users.length).toBe(2)

})

})

spec/UserSpec.js (link)

 

it("should return the correctly formatted balance", () => {

const user = new User(1, 'matt', 'm@mhn.me', '2018-08-20 12:38:00Z', '2018-08-20 12:38:00Z', 10000)

expect(user.balanceFormatted).toBeCloseTo(100.00)

})

})

 

it("should return the correctly formatted balance", () => {

const user = new User(1, 'matt', 'm@mhn.me', '2018-08-20 12:38:00Z', '2018-08-20 12:38:00Z', 10000)

expect(user.balanceFormatted).toBeCloseTo(100.00, 2)

})

})

Stats

Raw diff

diff --git a/app.js b/app.js
index 0d71b89..7a41e4f 100644
--- a/app.js
+++ b/app.js
@@ -1,5 +1,6 @@
 const Sequelize = require('sequelize')
 const UserFactory = require('./lib/UserFactory')
+const Transaction = require('./lib/Transaction')
 const TransactionFactory = require('./lib/TransactionFactory')
 
 const sequelize = new Sequelize('digithera_test', 'root', 'supersecure', {
@@ -25,11 +26,17 @@ exports.main = async (argv) => {
   const transactionFactory = new TransactionFactory(sequelize)
   try {
     const user = await userFactory.find('lucas')
-    console.log(user)
+    console.log('Found user: ', user)
+
     const transactions = await transactionFactory.getStatementForUser(user)
-    console.log(transactions)
-    const users = await userFactory.getUsersWithBalanceOver(483000)
-    console.log(users)
+    console.log(
+      `Transactions for user ${user.username} (balance: ${Transaction.getBalanceFormatted(transactions)})`,
+      transactions
+    )
+
+    const balance = argv[2] || 0
+    const usersWithBalanceGreaterThan = await userFactory.findUsersWithBalanceGreaterThan(balance)
+    console.log(`Users with balance greater than ${balance}: `, usersWithBalanceGreaterThan)
   } catch (err) {
     console.error(err)
   }
diff --git a/db.sql b/db.sql
index 3247d4d..430febc 100644
--- a/db.sql
+++ b/db.sql
@@ -43,7 +43,7 @@ CREATE TABLE discounts (
 
 CREATE TABLE purchases (
   id                   INT          NOT NULL AUTO_INCREMENT PRIMARY KEY,
-  user_id              INT          NOT NULL,
+  user_id              INT          NOT NULL COMMENT ('Can be derived from transaction but having dealt with highly denormalized databases it can be useful to have references like this when building reports.'),
   product_id           INT          NOT NULL,
   transaction_id       INT          NOT NULL,
   discount_id          INT          DEFAULT NULL,
diff --git a/lib/Discount.js b/lib/Discount.js
new file mode 100644
index 0000000..9fef241
--- /dev/null
+++ b/lib/Discount.js
@@ -0,0 +1,31 @@
+"use strict"
+
+module.exports = class Discount {
+  /**
+   * @param {number} id
+   * @param {number} percentage
+   * @param {string} createdAt
+   * @param {string} updatedAt
+   */
+  constructor (id, percentage, createdAt, updatedAt) {
+    this.id = id
+    this.percentage = percentage
+    this.createdAt = createdAt
+    this.updatedAt = updatedAt
+  }
+
+  /**
+   * @returns {number}
+   */
+  get percentageFormatted () {
+    return this.percentage ? this.percentage / 100 : 0
+  }
+
+  /**
+   * @returns {module.Purchase}
+   * @constructor
+   */
+  static NullDiscount () {
+    return new this(null, null, null, null)
+  }
+}
diff --git a/lib/Product.js b/lib/Product.js
new file mode 100644
index 0000000..576082a
--- /dev/null
+++ b/lib/Product.js
@@ -0,0 +1,33 @@
+"use strict"
+
+module.exports = class Product {
+  /**
+   * @param {number} id
+   * @param {string} name
+   * @param {number} price
+   * @param {string} createdAt
+   * @param {string} updatedAt
+   */
+  constructor (id, name, price, createdAt, updatedAt) {
+    this.id = id
+    this.name = name
+    this.price = price
+    this.createdAt = createdAt
+    this.updatedAt = updatedAt
+  }
+
+  /**
+   * @returns {number}
+   */
+  get priceFormatted () {
+    return this.price ? this.price / 100 : 0
+  }
+
+  /**
+   * @returns {module.Product}
+   * @constructor
+   */
+  static NullProduct () {
+    return new this(null, null, null, null, null)
+  }
+}
diff --git a/lib/Purchase.js b/lib/Purchase.js
new file mode 100644
index 0000000..be07a1f
--- /dev/null
+++ b/lib/Purchase.js
@@ -0,0 +1,47 @@
+"use strict"
+
+const Discount = require('./Discount')
+const Product = require('./Product')
+
+module.exports = class Purchase {
+  /**
+   * @param {number} id
+   * @param {number} amount
+   * @param {number} discountApplied
+   * @param {string} createdAt
+   * @param {string} updatedAt
+   * @param {module.Product} product
+   * @param {module.Discount} discount
+   */
+  constructor (id, amount, discountApplied, createdAt, updatedAt, product, discount) {
+    this.id = id
+    this.amount = amount
+    this.discountApplied = discountApplied
+    this.createdAt = createdAt
+    this.updatedAt = updatedAt
+    this.product = product
+    this.discount = discount
+  }
+
+  /**
+   * @returns {number}
+   */
+  get amountFormatted () {
+    return this.amount ? this.amount / 100 : 0
+  }
+
+  /**
+   * @returns {number}
+   */
+  get discountAppliedFormatted () {
+    return this.discountApplied ? this.discountApplied / 100 : 0
+  }
+
+  /**
+   * @returns {module.Purchase}
+   * @constructor
+   */
+  static NullPurchase () {
+    return new this(null, null, null, null, null, null, Product.NullProduct(), Discount.NullDiscount())
+  }
+}
diff --git a/lib/Transaction.js b/lib/Transaction.js
index f8f4ffd..229cb7d 100644
--- a/lib/Transaction.js
+++ b/lib/Transaction.js
@@ -1,14 +1,44 @@
 "use strict"
 
 module.exports = class Transaction {
-  constructor (id, user_id, amount, created_at) {
+  /**
+   * @param {number} id
+   * @param {number} amount
+   * @param {string} createdAt
+   * @param {module.User} user
+   * @param {module.Purchase} purchase
+   */
+  constructor (id, amount, createdAt, user, purchase) {
     this.id = id
-    this.user_id = user_id
     this.amount = amount
-    this.created_at = created_at
+    this.createdAt = createdAt
+    this.user = user
+    this.purchase = purchase
   }
 
+  /**
+   * @returns {number}
+   */
   get amountFormatted () {
     return this.amount / 100
   }
+
+  /**
+   * @param {module.Transaction[]} transactions
+   * @return {number}
+   */
+  static getBalance (transactions) {
+    return transactions
+      .map(t => t.amount)
+      .reduce((l, r) => l + r, 0)
+  }
+
+  /**
+   * @param {module.Transaction[]} transactions
+   * @return {number}
+   */
+  static getBalanceFormatted (transactions) {
+    const balance = this.getBalance(transactions)
+    return balance === 0 ? 0 : balance / 100
+  }
 }
diff --git a/lib/TransactionFactory.js b/lib/TransactionFactory.js
index 1fd35ee..0edb735 100644
--- a/lib/TransactionFactory.js
+++ b/lib/TransactionFactory.js
@@ -1,5 +1,8 @@
 "use strict"
 
+const Discount = require('./Discount')
+const Product = require('./Product')
+const Purchase = require('./Purchase')
 const Transaction = require('./Transaction')
 const User = require('./User')
 
@@ -13,13 +16,62 @@ module.exports = class TransactionFactory {
 
   /**
    * @param {User} user
+   * @returns {Promise<Transaction[]>}
    */
   async getStatementForUser (user) {
+    // This should probably be masked behind an ORM!
     const transactions = await this.databaseConnection
       .query(
-        'SELECT * FROM transactions WHERE user_id = ?',
-        { replacements: [user.id], type: this.databaseConnection.QueryTypes.SELECT }
+        `
+          SELECT
+            transactions.id AS transaction_id,
+            transactions.user_id,
+            amount,
+            transactions.created_at AS transaction_created_at,
+            purchases.id AS purchase_id,
+            purchases.discount_percentage AS purchase_discount,
+            purchases.created_at AS purchase_created_at,
+            purchases.updated_at AS purchase_updated_at,
+            products.id AS product_id,
+            products.name AS product_name,
+            products.price AS product_price,
+            products.created_at AS product_created_at,
+            products.updated_at AS product_updated_at,
+            discounts.id AS discount_id,
+            discounts.percentage AS discount_percentage,
+            discounts.created_at AS discount_created_at,
+            discounts.updated_at AS discount_updated_at
+          FROM transactions
+          LEFT JOIN purchases on transactions.id = purchases.transaction_id
+          LEFT JOIN products ON purchases.product_id = products.id
+          LEFT JOIN discounts ON purchases.discount_id = discounts.id
+          WHERE transactions.user_id = ?
+        `,
+        { replacements: [ user.id ], type: this.databaseConnection.QueryTypes.SELECT }
       )
-    return transactions.map(t => new Transaction(t.id, t.user_id, t.amount, t.created_at))
+
+    return transactions.map(t => {
+      const product = t.product_id
+        ? new Product(t.product_id, t.product_name, t.product_price, t.product_created_at, t.product_updated_at)
+        : Product.NullProduct()
+
+      const discount = t.discount_id
+        ? new Discount(t.discount_id, t.discount_percentage, t.discount_created_at, t.discount_updated_at)
+        : Discount.NullDiscount()
+
+      const purchase = t.purchase_id
+        ? new Purchase(
+          t.purchase_id,
+          Math.abs(t.amount),
+          t.purchase_discount,
+          t.purchase_created_at,
+          t.purchase_updated_at,
+          product,
+          discount
+        )
+        : Purchase.NullPurchase()
+
+      return new Transaction(t.transaction_id, t.amount, t.transaction_created_at, user, purchase)
+    })
   }
 }
diff --git a/lib/User.js b/lib/User.js
index b1b16ce..b38a51e 100644
--- a/lib/User.js
+++ b/lib/User.js
@@ -1,16 +1,35 @@
 "use strict"
 
 module.exports = class User {
-  constructor (id, username, email, created_at, updated_at, balance = null) {
+  /**
+   * @param {number} id
+   * @param {string} username
+   * @param {string} email
+   * @param {string} createdAt
+   * @param {string} updatedAt
+   * @param {number|null} balance
+   */
+  constructor (id, username, email, createdAt, updatedAt, balance = null) {
     this.id = id
     this.username = username
     this.email = email
-    this.created_at = created_at
-    this.updated_at = updated_at
+    this.createdAt = createdAt
+    this.updatedAt = updatedAt
     this.balance = balance
   }
 
+  /**
+   * @returns {number}
+   */
   get balanceFormatted () {
     return this.balance ? this.balance / 100 : 0
   }
+
+  /**
+   * @returns {module.User}
+   * @constructor
+   */
+  static NullUser() {
+    return new this(null, null, null, null, null, null)
+  }
 }
diff --git a/lib/UserFactory.js b/lib/UserFactory.js
index 43aa89b..4f2169e 100644
--- a/lib/UserFactory.js
+++ b/lib/UserFactory.js
@@ -26,10 +26,14 @@ module.exports = class UserFactory {
         `,
         { replacements: [username], type: this.databaseConnection.QueryTypes.SELECT }
       )
-    return users.length ? this.convertQueryToObject(users)[0] : null
+    return users.length ? this.convertResultToDomain(users)[0] : null
   }
 
-  async getUsersWithBalanceOver (amount) {
+  /**
+   * @param {number} amount
+   * @returns {Promise<User[]>}
+   */
+  async findUsersWithBalanceGreaterThan (amount) {
     const users = await this.databaseConnection
       .query(
         `
@@ -41,10 +45,14 @@ module.exports = class UserFactory {
         `,
         { replacements: [amount], type: this.databaseConnection.QueryTypes.SELECT }
       )
-    return this.convertQueryToObject(users)
+    return this.convertResultToDomain(users)
   }
 
-  convertQueryToObject(results) {
+  /**
+   * @param {object[]} results
+   * @returns {User[]}
+   */
+  convertResultToDomain (results) {
     return results.map(user =>
       new User(user.id, user.username, user.email, user.created_at, user.updated_at, user.balance || null)
     )
diff --git a/spec/TransactionSpec.js b/spec/TransactionSpec.js
new file mode 100644
index 0000000..3079117
--- /dev/null
+++ b/spec/TransactionSpec.js
@@ -0,0 +1,28 @@
+"use strict"
+
+const Purchase = require('../lib/Purchase')
+const Transaction = require("../lib/Transaction")
+const User = require('../lib/User')
+
+describe("The Transaction class", () => {
+
+  it("should correctly format the amount when requesting the formatted amount", () => {
+    const transaction = new Transaction(1, 100000, '', User.NullUser(), Purchase.NullPurchase())
+
+    expect(transaction.amountFormatted).toBe(1000.00)
+  })
+
+  it("should sum up the balance of a list of transaction correctly", () => {
+    const transactions = [
+      new Transaction(1, 100000, '', User.NullUser(), Purchase.NullPurchase()),
+      new Transaction(2, -10000, '', User.NullUser(), Purchase.NullPurchase()),
+      new Transaction(3,   9999, '', User.NullUser(), Purchase.NullPurchase())
+    ]
+
+    expect(Transaction.getBalance(transactions)).toBe(99999)
+  })
+
+  it("should sum the balance to 0 for an empty transaction list", () => {
+    expect(Transaction.getBalance([])).toBe(0)
+  })
+})
diff --git a/spec/UserFactorySpec.js b/spec/UserFactorySpec.js
index 5c01d59..5229224 100644
--- a/spec/UserFactorySpec.js
+++ b/spec/UserFactorySpec.js
@@ -68,7 +68,7 @@ describe("The UserFactory class", () => {
       }
     })
 
-    const users = await userFactory.getUsersWithBalanceOver(100)
+    const users = await userFactory.findUsersWithBalanceGreaterThan(100)
     expect(users.length).toBe(2)
   })
 })
diff --git a/spec/UserSpec.js b/spec/UserSpec.js
index c3d5822..708b9e3 100644
--- a/spec/UserSpec.js
+++ b/spec/UserSpec.js
@@ -6,6 +6,6 @@ describe("The User class", () => {
 
   it("should return the correctly formatted balance", () => {
     const user = new User(1, 'matt', 'm@mhn.me', '2018-08-20 12:38:00Z', '2018-08-20 12:38:00Z', 10000)
-    expect(user.balanceFormatted).toBeCloseTo(100.00)
+    expect(user.balanceFormatted).toBeCloseTo(100.00, 2)
   })
 })