Skip to content

Commit

Permalink
Create ngx_mock module and avoid ad-hoc mocking of _G.ngx in specs
Browse files Browse the repository at this point in the history
  • Loading branch information
jirutka committed Dec 2, 2015
1 parent 23a3835 commit d6a928a
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 52 deletions.
3 changes: 2 additions & 1 deletion .busted
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

return {
_all = {
helper = 'spec/spec_helper'
helper = 'spec/spec_helper',
lpath = './src/?.lua;./spec/support/?.lua;'
}
}
14 changes: 5 additions & 9 deletions spec/ngx-oauth/Cookies_spec.moon
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,13 @@ describe '__call', ->
setup ->
_G.pairs = spec_helper.sorted_pairs
export crypto_stub = mock {
encrypt: (bits, key, value) -> "<ENC[#{value}]>"
decrypt: (bits, key, value) -> "<DEC[#{value}]>"
encrypt: (bits, key, value) -> "~~ENC-#{value}~~"
decrypt: (bits, key, value) -> "~~DEC-#{value}~~"
}

before_each ->
export cookies = Cookies(conf, crypto_stub)
_G.ngx =
var: {}
header: {}
escape_uri: (value) -> value\gsub(' ', '+')
unescape_uri: spec_helper.unescape_uri
_G.ngx = spec_helper.ngx_mock!


describe 'add_token', ->
Expand Down Expand Up @@ -69,7 +65,7 @@ describe '__call', ->
tkn = { access_token: 'acc-token', expires_in: conf.max_age / 2, refresh_token: 'ref-token' }
expected = {
access_token_cookie(tkn),
"#{prefix}refresh_token=<ENC[#{tkn.refresh_token}]>;max-age=#{conf.max_age};#{cookie_attrs}"
"#{prefix}refresh_token=~~ENC-#{tkn.refresh_token}~~;max-age=#{conf.max_age};#{cookie_attrs}"
}

it 'writes cookie with access token and encrypted refresh token', ->
Expand Down Expand Up @@ -114,7 +110,7 @@ describe '__call', ->
it 'returns decrypted value of the cookie', ->
set_cookie "#{prefix}refresh_token", 'token-123'

assert.same '<DEC[token-123]>', cookies.get_refresh_token()
assert.same '~~DEC-token-123~~', cookies.get_refresh_token()
assert.stub(crypto_stub.decrypt).called_with(conf.aes_bits, conf.client_secret, 'token-123')

context 'refresh_token cookie does not exist', ->
Expand Down
3 changes: 1 addition & 2 deletions spec/ngx-oauth/config_spec.moon
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ describe 'load', ->
}

before_each ->
_G.ngx =
var: { scheme: 'https', server_name: 'example.org' }
_G.ngx.var = { scheme: 'https', server_name: 'example.org' }


context 'when all variables are set', ->
Expand Down
7 changes: 0 additions & 7 deletions spec/ngx-oauth/crypto_spec.moon
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ encrypted = 'I6u7/Lp5vS+APJJtBjVRGdteDmq0Fxt45xzrEb7Q9ag='


describe 'encrypt/decrypt', ->
setup ->
_G.ngx = mock
encode_base64: (value) -> basexx.to_base64(value)
decode_base64: (value) -> basexx.from_base64(value)

it 'encrypts and decryptes string', ->
encrypted = crypto.encrypt(128, key, plain)
Expand All @@ -20,8 +16,6 @@ describe 'encrypt/decrypt', ->


describe 'decrypt', ->
before_each ->
_G.ngx = decode_base64: (value) -> basexx.from_base64(value)

context 'given correct key and encrypted value', ->
it 'returns decrypted value', ->
Expand All @@ -47,5 +41,4 @@ describe 'decrypt', ->

context 'given invalid base64 value', ->
it 'returns nil', ->
_G.ngx.decode_base64 = -> nil
assert.is_nil crypto.decrypt(128, key, '%invalid%')
6 changes: 2 additions & 4 deletions spec/ngx-oauth/http_client_spec.moon
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,10 @@ describe 'get_for_json', ->
describe 'post_form_for_json', ->

headers = { Cookie: 'foo=42;path=/' }
body = {foo: 42}
body = {colour: '#004b7e'}
post_form_for_json = -> client.post_form_for_json(headers, url, body)

setup ->
_G.ngx = mock
encode_args: (tab) -> '<encoded>'
spy.on(client, 'request')

teardown ->
Expand All @@ -188,6 +186,6 @@ describe 'post_form_for_json', ->
it 'calls request() with body encoded using ngx.encode_args', ->
post_form_for_json!
assert.stub(_G.ngx.encode_args).called_with body
assert.spy(client.request).called_with(_, _, _, '<encoded>')
assert.spy(client.request).called_with(_, _, _, 'colour=%23004b7e')

contexts_json_response -> post_form_for_json!
15 changes: 4 additions & 11 deletions spec/ngx-oauth/nginx_spec.moon
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ nginx = require 'ngx-oauth.nginx'

describe 'add_response_cookies', ->
before_each ->
_G.ngx = { header: {} }
_G.ngx = spec_helper.ngx_mock!

set_resp_cookie = (value) ->
_G.ngx.header['Set-Cookie'] = value
Expand Down Expand Up @@ -37,8 +37,8 @@ describe 'add_response_cookies', ->


describe 'fail', ->
setup ->
_G.ngx = mock { ERR: 4, WARN: 5, HTTP_OK: 200, header: {}, log: ->, say: ->, exit: -> }
before_each ->
_G.ngx = spec_helper.ngx_mock!

context 'given valid status', ->
before_each -> nginx.fail(503, 'Ay, %s!', 'caramba')
Expand Down Expand Up @@ -80,8 +80,6 @@ describe 'fail', ->
describe 'format_cookie', ->
setup ->
_G.pairs = spec_helper.sorted_pairs
_G.ngx = mock
escape_uri: (value) -> string.gsub(value, ' ', '+')

it 'returns correctly formated cookie with attributes', ->
actual = nginx.format_cookie('foo', 'meh', version: 1, path: '/')
Expand All @@ -100,10 +98,7 @@ describe 'format_cookie', ->

describe 'get_cookie', ->
setup ->
_G.ngx = mock {
var: { cookie_plain: 'meh.', cookie_encod: '%2Fping%3F' }
unescape_uri: spec_helper.unescape_uri
}
_G.ngx.var = { cookie_plain: 'meh.', cookie_encod: '%2Fping%3F' }

context 'existing cookie', ->
it 'returns cookie value from ngx.var', ->
Expand All @@ -119,8 +114,6 @@ describe 'get_cookie', ->


describe 'log', ->
before_each ->
_G.ngx = mock { ERR: 4, WARN: 5, INFO: 7, log: -> }

it 'calls ngx.log with given level and message prefixed by [ngx-oauth]', ->
nginx.log(ngx.WARN, 'allons-y!')
Expand Down
4 changes: 2 additions & 2 deletions spec/ngx-oauth/oauth2_spec.moon
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ http_client_stub = { post_form_for_json: -> }


setup ->
_G.ngx = { encode_args: spec_helper.encode_args, encode_base64: to_base64 }
_G.pairs = spec_helper.sorted_pairs
-- Trick Lua to require our fake http_client instead of ngx-oauth.http_client module.
package.loaded['ngx-oauth.http_client'] = http_client_stub
export oauth = require 'ngx-oauth.oauth2'
Expand All @@ -28,7 +28,7 @@ teardown ->

describe 'authorization_url', ->
expected = 'https://oaas.org/authorize?client_id=client123&redirect_uri='..
'http%3A%2F%2Fexample%2Eorg&response_type=code&scope=email&state=xyz'
'http%3A%2F%2Fexample.org&response_type=code&scope=email&state=xyz'

it 'returns encoded authorization url with correct query parameters', ->
actual = oauth.authorization_url(conf, 'xyz')
Expand Down
21 changes: 5 additions & 16 deletions spec/spec_helper.moon
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
luassert = require 'luassert'
say = require 'say'
ngx_mock = require 'ngx_mock'
say = require 'say'

_G.ngx = ngx_mock!

-- Assertions

Expand Down Expand Up @@ -34,21 +37,7 @@ sorted_pairs = (tab) ->
key, tab[key] if key


encode_args = (tab) ->
encode_param = (str) ->
tostring(str)\gsub('\n', '\r\n')\gsub('([^%w_])', (c) ->
string.format('%%%02X', string.byte(c)))

table.concat([ encode_param(k)..'='..encode_param(v) for k, v in sorted_pairs(tab) ], '&')


unescape_uri = (str) ->
tostring(str)\gsub('+', ' ')\gsub('\r\n', '\n')\gsub('%%(%x%x)', (h) ->
string.char(tonumber(h, 16)))


export spec_helper = {
:encode_args
:sorted_pairs
:unescape_uri
:ngx_mock
}
65 changes: 65 additions & 0 deletions spec/support/ngx_mock.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---------
-- Mock for global ngx table. This implementation is not complete, it contains
-- just subset of variables and functions we need.

local basexx = require 'basexx'
local spy = require 'luassert.spy'
local stub = require 'luassert.stub'


local function escape_uri (str)
return tostring(str):gsub('\n', '\r\n'):gsub('([^%w%-_.~ ])', function(ch)
return string.format('%%%02X', string.byte(ch))
end):gsub(' ', '+')
end

local function encode_args (tab)
local list = {}
for k, v in pairs(tab) do
table.insert(list, escape_uri(k)..'='..escape_uri(v))
end
return table.concat(list, '&')
end

local function unescape_uri (str)
return tostring(str):gsub('+', ' '):gsub('\r\n', '\n'):gsub('%%(%x%x)', function(b)
return string.char(tonumber(b, 16))
end)
end

return function()
return {
-- Log constants
STDERR = 0,
EMERG = 1,
ALERT = 2,
CRIT = 3,
ERR = 4,
WARN = 5,
NOTICE = 6,
INFO = 7,
DEBUG = 8,

-- Tables
var = {},
header = {},

-- Spied functions

-- nginx's implementation returns nil when input can't be decoded.
decode_base64 = spy(function(...)
local ok, ret = pcall(basexx.from_base64, ...)
if ok then return ret end
end),

encode_args = spy(encode_args),
encode_base64 = spy(basexx.to_base64),
escape_uri = spy(escape_uri),
unescape_uri = spy(unescape_uri),

-- Stubs for functions without any return value
log = stub(),
say = stub(),
exit = stub()
}
end

0 comments on commit d6a928a

Please sign in to comment.