
commit e38bed8be3f876da5c840710f82a2f5e6b67ecc5 Author: Serene Han <keroserene+git@gmail.com> Date: Thu Feb 4 14:13:03 2016 -0800 convert all coffeescript tests to jasmine --- .gitignore | 1 + .travis.yml | 2 +- proxy/Cakefile | 23 ++-- proxy/README.md | 9 +- proxy/snowflake.coffee | 4 +- proxy/snowflake_test.coffee | 323 -------------------------------------------- proxy/spec.coffee | 180 ++++++++++++++++++++++++ proxy/websocket.coffee | 1 + 8 files changed, 205 insertions(+), 338 deletions(-) diff --git a/.gitignore b/.gitignore index 516f3ff..b1db25c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ snowflake.log proxy/test proxy/build proxy/node_modules +proxy/spec ignore/ diff --git a/.travis.yml b/.travis.yml index c860b37..4a7aedd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ go: - 1.5 before_script: - npm install -g coffee-script coffeelint + npm install -g coffee-script coffeelint jasmine script: - make check diff --git a/proxy/Cakefile b/proxy/Cakefile index fc92934..c569110 100644 --- a/proxy/Cakefile +++ b/proxy/Cakefile @@ -11,10 +11,10 @@ FILES = [ 'ui.coffee' 'snowflake.coffee' ] -FILES_TEST = [ - 'snowflake_test.coffee' +FILES_SPEC = [ + 'spec.coffee' ] -FILES_ALL = FILES.concat FILES_TEST +FILES_ALL = FILES.concat FILES_SPEC OUTFILE = 'build/snowflake.coffee' STATIC = 'static' @@ -23,16 +23,18 @@ concatCoffeeFiles = -> exec 'cat ' + FILES.join(' ') + ' | cat > ' + OUTFILE copyStaticFiles = -> exec 'cp ' + STATIC + '/* build/' compileCoffee = -> - exec 'coffee -o build -c build/snowflake.coffee', (err, stdout, stderr) -> + exec 'coffee -o build -b -c build/snowflake.coffee', (err, stdout, stderr) -> throw err if err task 'test', 'snowflake unit tests', -> - exec 'mkdir -p test' - testFile = 'test/snowflake.bundle.coffee' - exec 'cat ' + FILES.join(' ') + ' snowflake_test.coffee | cat > ' + testFile - exec 'coffee ' + testFile + ' -v', (err, stdout, stderr) -> - throw err if err - console.log stdout + stderr + exec 'jasmine init >&-' + jasmineFiles = FILES.concat FILES_SPEC + outFile = 'spec/snowflake.bundle.coffee' + exec 'cat ' + jasmineFiles.join(' ') + ' | cat > ' + outFile + exec 'coffee -o spec -cb ' + outFile + spawn 'jasmine', ['spec/snowflake.bundle.js'], { + stdio: 'inherit' + } task 'build', 'build the snowflake proxy', -> exec 'mkdir -p build' @@ -49,4 +51,5 @@ task 'lint', 'ensure idiomatic coffeescript', -> task 'clean', 'remove all built files', -> exec 'rm -r build' + exec 'rm -r spec' exec 'rm -r test/snowflake.bundle.coffee' diff --git a/proxy/README.md b/proxy/README.md index 1affaca..364ac0e 100644 --- a/proxy/README.md +++ b/proxy/README.md @@ -2,12 +2,16 @@ This is the browser proxy component of Snowflake. ### Testing: -Unit tests are available with: +Unit testing with Jasmine are available with: ``` cake test ``` +Requires jasmine. (`npm install -g jasmine`) -To run locally, start a webserver and navigate to `snowflake.html`. +To run locally, either: +- Navigate to `proxy/build/embed.html` +- For a more fully featured "debug" version, + start a webserver and navigate to `snowflake.html`. ### Parameters @@ -16,7 +20,6 @@ snowflake uses the default relay `192.81.135.242:9901` and uses automatic signaling with the default broker at `https://snowflake-reg.appspot.com/`. - Here are optional parameters to include in the query string. ``` manual - enables copy-paste signalling mode. diff --git a/proxy/snowflake.coffee b/proxy/snowflake.coffee index 74f6a6e..eea1518 100644 --- a/proxy/snowflake.coffee +++ b/proxy/snowflake.coffee @@ -15,10 +15,12 @@ COPY_PASTE_ENABLED = false DEBUG = false query = null -if window && window.location +if 'undefined' != typeof window && window.location query = Query.parse(window.location.search.substr(1)) DEBUG = Params.getBool(query, 'debug', false) COPY_PASTE_ENABLED = Params.getBool(query, 'manual', false) +else + window = {} # HEADLESS is true if we are running not in a browser with a DOM. HEADLESS = 'undefined' == typeof(document) diff --git a/proxy/snowflake_test.coffee b/proxy/snowflake_test.coffee deleted file mode 100644 index 624257a..0000000 --- a/proxy/snowflake_test.coffee +++ /dev/null @@ -1,323 +0,0 @@ -window = {} -ui = {} - -VERBOSE = false -VERBOSE = true if process.argv.indexOf('-v') >= 0 - -numTests = 0 -numFailed = 0 - -announce = (testName) -> - console.log '\n --- ' + testName + ' ---' if VERBOSE - -pass = (test) -> - numTests++ - console.log 'PASS ' + test if VERBOSE - -fail = (test, expected, actual) -> - numTests++ - numFailed++ - console.log 'FAIL ' + test + - ' expected: ' + JSON.stringify(expected) + - ' actual: ' + JSON.stringify(actual) - -# Stubs for browser functionality. -class WebSocket - OPEN: 1 - CLOSED: 0 - -testBuildUrl = -> - TESTS = [{ - args: ['http', 'example.com'] - expected: 'http://example.com' - },{ - args: ['http', 'example.com', 80] - expected: 'http://example.com' - },{ - args: ['http', 'example.com', 81], - expected: 'http://example.com:81' - },{ - args: ['https', 'example.com', 443] - expected: 'https://example.com' - },{ - args: ['https', 'example.com', 444] - expected: 'https://example.com:444' - },{ - args: ['http', 'example.com', 80, '/'] - expected: 'http://example.com/' - },{ - args: ['http', 'example.com', 80, '/test?k=%#v'] - expected: 'http://example.com/test%3Fk%3D%25%23v' - },{ - args: ['http', 'example.com', 80, '/test', []] - expected: 'http://example.com/test?' - },{ - args: ['http', 'example.com', 80, '/test', [['k', '%#v']]] - expected: 'http://example.com/test?k=%25%23v' - },{ - args: ['http', 'example.com', 80, '/test', [['a', 'b'], ['c', 'd']]] - expected: 'http://example.com/test?a=b&c=d' - },{ - args: ['http', '1.2.3.4'] - expected: 'http://1.2.3.4' - },{ - args: ['http', '1:2::3:4'] - expected: 'http://[1:2::3:4]' - },{ - args: ['http', 'bog][us'] - expected: 'http://bog%5D%5Bus' - },{ - args: ['http', 'bog:u]s'] - expected: 'http://bog%3Au%5Ds' - }] - announce 'testBuildUrl' - for test in TESTS - actual = buildUrl.apply undefined, test.args - if actual == test.expected - pass test.args - else - fail test.args, test.expected, actual - -### -This test only checks that things work for strings formatted like -document.cookie. Browsers maintain several properties about this string, for -example cookie names are unique with no trailing whitespace. See -http://www.ietf.org/rfc/rfc2965.txt for the grammar. -### -testParseCookieString = -> - TESTS = [{ - cs: '' - expected: {} - },{ - cs: 'a=b' - expected: { a: 'b' } - },{ - cs: 'a=b=c' - expected: { a: 'b=c' } - },{ - cs: 'a=b; c=d' - expected: { a: 'b', c: 'd' } - },{ - cs: 'a=b ; c=d' - expected: { a: 'b', c: 'd' } - },{ - cs: 'a= b', - expected: { a: 'b' } - },{ - cs: 'a=' - expected: { a: '' } - }, { - cs: 'key', - expected: null - }, { - cs: 'key=%26%20' - expected: { key: '& ' } - }, { - cs: 'a=\'\'' - expected: { a: '\'\'' } - }] - announce 'testParseCookieString' - for test in TESTS - actual = Parse.cookie test.cs - if JSON.stringify(actual) == JSON.stringify(test.expected) - pass test.cs - else - fail test.cs, test.expected, actual - -testParseQueryString = -> - TESTS = [{ - qs: '' - expected: {} - },{ - qs: 'a=b' - expected: { a: 'b' } - },{ - qs: 'a=b=c' - expected: { a: 'b=c' } - },{ - qs: 'a=b&c=d' - expected: { a: 'b', c: 'd' } - },{ - qs: 'client=&relay=1.2.3.4%3A9001' - expected: { client: '', relay: '1.2.3.4:9001' } - },{ - qs: 'a=b%26c=d' - expected: { a: 'b&c=d' } - },{ - qs: 'a%3db=d' - expected: { 'a=b': 'd' } - },{ - qs: 'a=b+c%20d' - expected: { 'a': 'b c d' } - },{ - qs: 'a=b+c%2bd' - expected: { 'a': 'b c+d' } - },{ - qs: 'a+b=c' - expected: { 'a b': 'c' } - },{ - qs: 'a=b+c+d' - expected: { a: 'b c d' } - # First appearance wins. - },{ - qs: 'a=b&c=d&a=e' - expected: { a: 'b', c: 'd' } - },{ - qs: 'a' - expected: { a: '' } - },{ - qs: '=b', - expected: { '': 'b' } - },{ - qs: '&a=b' - expected: { '': '', a: 'b' } - },{ - qs: 'a=b&' - expected: { a: 'b', '':'' } - },{ - qs: 'a=b&&c=d' - expected: { a: 'b', '':'', c: 'd' } - }] - announce 'testParseQueryString' - for test in TESTS - actual = Query.parse test.qs - if JSON.stringify(actual) == JSON.stringify(test.expected) - pass test.qs - else - fail test.qs, test.expected, actual - -testGetParamBoolean = -> - TESTS = [{ - qs: 'param=true' - expected: true - },{ - qs: 'param', - expected: true - },{ - qs: 'param=' - expected: true - },{ - qs: 'param=1' - expected: true - },{ - qs: 'param=0' - expected: false - },{ - qs: 'param=false' - expected: false - },{ - qs: 'param=unexpected' - expected: null - },{ - qs: 'pram=true' - expected: false - }] - announce 'testGetParamBoolean' - for test in TESTS - query = Query.parse test.qs - actual = Params.getBool(query, 'param', false) - if actual == test.expected - pass test.qs - else - fail test.qs, test.expected, actual - -testParseAddress = -> - TESTS = [{ - spec: '' - expected: null - },{ - spec: '3.3.3.3:4444' - expected: { host: '3.3.3.3', port: 4444 } - },{ - spec: '3.3.3.3' - expected: null - },{ - spec: '3.3.3.3:0x1111' - expected: null - },{ - spec: '3.3.3.3:-4444' - expected: null - },{ - spec: '3.3.3.3:65536' - expected: null - },{ - spec: '[1:2::a:f]:4444' - expected: { host: '1:2::a:f', port: 4444 } - },{ - spec: '[1:2::a:f]' - expected: null - },{ - spec: '[1:2::a:f]:0x1111' - expected: null - },{ - spec: '[1:2::a:f]:-4444' - expected: null - },{ - spec: '[1:2::a:f]:65536' - expected: null - },{ - spec: '[1:2::ffff:1.2.3.4]:4444' - expected: { host: '1:2::ffff:1.2.3.4', port: 4444 } - }] - announce 'testParseAddrSpec' - for test in TESTS - actual = Parse.address test.spec - if JSON.stringify(actual) == JSON.stringify(test.expected) - pass test.spec - else - fail test.spec, test.expected, actual - -testGetParamAddress = -> - DEFAULT = { host: '1.1.1.1', port: 2222 } - TESTS = [{ - query: {} - expected: DEFAULT - },{ - query: { addr: '3.3.3.3:4444' }, - expected: { host: '3.3.3.3', port: 4444 } - },{ - query: { x: '3.3.3.3:4444' } - expected: DEFAULT - },{ - query: { addr: '---' } - expected: null - }] - - announce 'testGetParamAddress' - for test in TESTS - actual = Params.getAddress test.query, 'addr', DEFAULT - if JSON.stringify(actual) == JSON.stringify(test.expected) - pass test.query - else - fail test.query, test.expected, actual - -testProxyPair = -> - announce 'testProxyPair' - fakeRelay = Parse.address '0.0.0.0:12345' - rateLimit = new DummyRateLimit() - destination = [] - fakeClient = - send: (d) -> destination.push d - pp = new ProxyPair(fakeClient, fakeRelay, rateLimit) - pp.connectRelay() - if null != pp.relay.onopen then pass 'relay.onopen' - else fail 'relay onopen must not be null.' - if null != pp.relay.onclose then pass 'relay.onclose' - else fail 'relay onclose must not be null.' - if null != pp.relay.onerror then pass 'relay.onerror' - else fail 'relay onerror must not be null.' - if null != pp.relay.onmessage then pass 'relay.onmessage' - else fail 'relay onmessage must not be null.' - # TODO: Test for flush - # pp.c2rSchedule.push { data: 'omg' } - # pp.flush() - # if destination == ['omg'] then pass 'flush' - # else fail 'flush', ['omg'], destination - -testBuildUrl() -testParseCookieString() -testParseQueryString() -testGetParamBoolean() -testParseAddress() -testGetParamAddress() -testProxyPair() diff --git a/proxy/spec.coffee b/proxy/spec.coffee new file mode 100644 index 0000000..3d8ae61 --- /dev/null +++ b/proxy/spec.coffee @@ -0,0 +1,180 @@ +### +jasmine tests for Snowflake +### + +# Stubs to fake browser functionality. +class WebSocket + OPEN: 1 + CLOSED: 0 +ui = {} + +describe 'BuildUrl', -> + it 'should parse just protocol and host', -> + expect(buildUrl('http', 'example.com')).toBe 'http://example.com' + it 'should handle different ports', -> + expect buildUrl 'http', 'example.com', 80 + .toBe 'http://example.com' + expect buildUrl 'http', 'example.com', 81 + .toBe 'http://example.com:81' + expect buildUrl 'http', 'example.com', 443 + .toBe 'http://example.com:443' + expect buildUrl 'http', 'example.com', 444 + .toBe 'http://example.com:444' + it 'should handle paths', -> + expect buildUrl 'http', 'example.com', 80, '/' + .toBe 'http://example.com/' + expect buildUrl 'http', 'example.com', 80,'/test?k=%#v' + .toBe 'http://example.com/test%3Fk%3D%25%23v' + expect buildUrl 'http', 'example.com', 80, '/test' + .toBe 'http://example.com/test' + it 'should handle params', -> + expect buildUrl 'http', 'example.com', 80, '/test', [['k', '%#v']] + .toBe 'http://example.com/test?k=%25%23v' + expect buildUrl 'http', 'example.com', 80, '/test', [['a', 'b'], ['c', 'd']] + .toBe 'http://example.com/test?a=b&c=d' + it 'should handle ips', -> + expect buildUrl 'http', '1.2.3.4' + .toBe 'http://1.2.3.4' + expect buildUrl 'http', '1:2::3:4' + .toBe 'http://[1:2::3:4]' + it 'should handle bogus', -> + expect buildUrl 'http', 'bog][us' + .toBe 'http://bog%5D%5Bus' + expect buildUrl 'http', 'bog:u]s' + .toBe 'http://bog%3Au%5Ds' + +describe 'Parse', -> + + describe 'cookie', -> + it 'parses correctly', -> + expect Parse.cookie '' + .toEqual {} + expect Parse.cookie 'a=b' + .toEqual { a: 'b' } + expect Parse.cookie 'a=b=c' + .toEqual { a: 'b=c' } + expect Parse.cookie 'a=b; c=d' + .toEqual { a: 'b', c: 'd' } + expect Parse.cookie 'a=b ; c=d' + .toEqual { a: 'b', c: 'd' } + expect Parse.cookie 'a= b' + .toEqual { a: 'b' } + expect Parse.cookie 'a=' + .toEqual { a: '' } + expect Parse.cookie 'key' + .toBeNull() + expect Parse.cookie 'key=%26%20' + .toEqual { key: '& ' } + expect Parse.cookie 'a=\'\'' + .toEqual { a: '\'\'' } + + describe 'address', -> + it 'parses IPv4', -> + expect Parse.address '' + .toBeNull() + expect Parse.address '3.3.3.3:4444' + .toEqual { host: '3.3.3.3', port: 4444 } + expect Parse.address '3.3.3.3' + .toBeNull() + expect Parse.address '3.3.3.3:0x1111' + .toBeNull() + expect Parse.address '3.3.3.3:-4444' + .toBeNull() + expect Parse.address '3.3.3.3:65536' + .toBeNull() + it 'parses IPv6', -> + expect Parse.address '[1:2::a:f]:4444' + .toEqual { host: '1:2::a:f', port: 4444 } + expect Parse.address '[1:2::a:f]' + .toBeNull() + expect Parse.address '[1:2::a:f]:0x1111' + .toBeNull() + expect Parse.address '[1:2::a:f]:-4444' + .toBeNull() + expect Parse.address '[1:2::a:f]:65536' + .toBeNull() + expect Parse.address '[1:2::ffff:1.2.3.4]:4444' + .toEqual { host: '1:2::ffff:1.2.3.4', port: 4444 } + +describe 'query string', -> + it 'should parse correctly', -> + expect Query.parse '' + .toEqual {} + expect Query.parse 'a=b' + .toEqual { a: 'b' } + expect Query.parse 'a=b=c' + .toEqual { a: 'b=c' } + expect Query.parse 'a=b&c=d' + .toEqual { a: 'b', c: 'd' } + expect Query.parse 'client=&relay=1.2.3.4%3A9001' + .toEqual { client: '', relay: '1.2.3.4:9001' } + expect Query.parse 'a=b%26c=d' + .toEqual { a: 'b&c=d' } + expect Query.parse 'a%3db=d' + .toEqual { 'a=b': 'd' } + expect Query.parse 'a=b+c%20d' + .toEqual { 'a': 'b c d' } + expect Query.parse 'a=b+c%2bd' + .toEqual { 'a': 'b c+d' } + expect Query.parse 'a+b=c' + .toEqual { 'a b': 'c' } + expect Query.parse 'a=b+c+d' + .toEqual { a: 'b c d' } + it 'uses the first appearance of duplicate key', -> + expect Query.parse 'a=b&c=d&a=e' + .toEqual { a: 'b', c: 'd' } + expect Query.parse 'a' + .toEqual { a: '' } + expect Query.parse '=b' + .toEqual { '': 'b' } + expect Query.parse '&a=b' + .toEqual { '': '', a: 'b' } + expect Query.parse 'a=b&' + .toEqual { a: 'b', '':'' } + expect Query.parse 'a=b&&c=d' + .toEqual { a: 'b', '':'', c: 'd' } + +describe 'Params', -> + describe 'bool', -> + getBool = (query) -> + Params.getBool (Query.parse query), 'param', false + it 'parses correctly', -> + expect(getBool 'param=true').toEqual true + expect(getBool 'param').toEqual true + expect(getBool 'param=').toEqual true + expect(getBool 'param=1').toEqual true + expect(getBool 'param=0').toEqual false + expect(getBool 'param=false').toEqual false + expect(getBool 'param=unexpected').toBeNull() + expect(getBool 'pram=true').toEqual false + + describe 'address', -> + DEFAULT = { host: '1.1.1.1', port: 2222 } + getAddress = (query) -> + Params.getAddress query, 'addr', DEFAULT + it 'parses correctly', -> + expect(getAddress {}).toEqual DEFAULT + expect getAddress { addr: '3.3.3.3:4444' } + .toEqual { host: '3.3.3.3', port: 4444 } + expect getAddress { x: '3.3.3.3:4444' } + .toEqual DEFAULT + expect getAddress { addr: '---' } + .toBeNull() + +describe 'ProxyPair', -> + fakeRelay = Parse.address '0.0.0.0:12345' + rateLimit = new DummyRateLimit() + destination = [] + fakeClient = send: (d) -> destination.push d + pp = new ProxyPair(fakeClient, fakeRelay, rateLimit) + it 'handles relay correctly', -> + pp.connectRelay() + expect(pp.relay.onopen).not.toBeNull() + expect(pp.relay.onclose).not.toBeNull() + expect(pp.relay.onerror).not.toBeNull() + expect(pp.relay.onmessage).not.toBeNull() + # TODO: Test for flush + # pp.c2rSchedule.push { data: 'omg' } + # pp.flush() + # if destination == ['omg'] then pass 'flush' + # else fail 'flush', ['omg'], destination diff --git a/proxy/websocket.coffee b/proxy/websocket.coffee index eca6f2a..03fa0e8 100644 --- a/proxy/websocket.coffee +++ b/proxy/websocket.coffee @@ -57,3 +57,4 @@ makeWebsocket = (addr) -> ws.binaryType = 'arraybuffer' ws +# module.exports.buildUrl = buildUrl