AWS = require('./helpers').AWS Buffer = AWS.util.Buffer describe 'uriEscape', -> e = AWS.util.uriEscape it 'escapes spaces as %20', -> expect(e('a b')).toEqual('a%20b') it 'escapes + as %2B', -> expect(e('a+b')).toEqual('a%2Bb') it 'escapes / as %2F', -> expect(e('a/b')).toEqual('a%2Fb') it 'escapes \' as %27', -> expect(e('a\'b')).toEqual('a%27b') it 'escapes * as %2A', -> expect(e('a*b')).toEqual('a%2Ab') it 'does not escape ~', -> expect(e('a~b')).toEqual('a~b') it 'encodes utf8 characters', -> expect(e('ёŝ')).toEqual('%D1%91%C5%9D') describe 'uriEscapePath', -> e = AWS.util.uriEscapePath it 'does not escape forward slashes', -> s = 'a&b/x=y/1+2/m?n' expect(e(s)).toEqual('a%26b/x%3Dy/1%2B2/m%3Fn') it 'leaves leading and trailing forward slashes in place', -> s = '/ab cd/' expect(e(s)).toEqual('/ab%20cd/') describe 'AWS.util.queryParamsToString', -> qpts = AWS.util.queryParamsToString it 'sorts query parameters before stringifying', -> expect(qpts(c: '1', b: '2', a: '3')).toEqual('a=3&b=2&c=1') it 'handles empty values', -> expect(qpts(a: '', b: '2')).toEqual('a=&b=2') it 'handles null/undefined values', -> expect(qpts(a: undefined, b: null)).toEqual('a&b') it 'calls uriEscape on each name and value', -> spy = spyOn(AWS.util, 'uriEscape').andCallThrough() qpts(c: '1', b: '2', a: '3') expect(spy.calls.length).toEqual(6) it 'handles values as lists', -> expect(qpts(a: ['1', '2', '3'], b: '4')).toEqual('a=1&a=2&a=3&b=4') it 'escapes list values', -> expect(qpts(a: ['+', '&', '*'], b: '4')).toEqual('a=%26&a=%2A&a=%2B&b=4') it 'does not provide value if value is null', -> expect(qpts(a: null, b: null)).toEqual('a&b') describe 'AWS.util.date', -> util = AWS.util.date describe 'getDate', -> it 'should return current date by default', -> oldDate = Date; now = {} mock = jasmine.createSpy().andReturn(now) `typeof window !== 'undefined' ? (window.Date = mock) : (Date = mock);` expect(util.getDate()).toBe(now) `typeof window !== 'undefined' ? (window.Date = oldDate) : (Date = oldDate);` describe 'iso8601', -> it 'should return date formatted as YYYYMMDDTHHnnssZ', -> date = new Date(600000); date.setMilliseconds(0) spyOn(util, 'getDate').andCallFake -> date expect(util.iso8601()).toEqual('1970-01-01T00:10:00.000Z') it 'should allow date parameter', -> date = new Date(660000); date.setMilliseconds(0) expect(util.iso8601(date)).toEqual('1970-01-01T00:11:00.000Z') describe 'rfc822', -> it 'should return date formatted as YYYYMMDDTHHnnssZ', -> date = new Date(600000); date.setMilliseconds(0) spyOn(util, 'getDate').andCallFake -> date expect(util.rfc822()).toMatch(/^Thu, 0?1 Jan 1970 00:10:00 (GMT|UTC)$/) it 'should allow date parameter', -> date = new Date(660000); date.setMilliseconds(0) expect(util.rfc822(date)).toMatch(/^Thu, 0?1 Jan 1970 00:11:00 (GMT|UTC)$/) describe 'unixTimestamp', -> it 'should return date formatted as unix timestamp', -> date = new Date(600000); date.setMilliseconds(0) spyOn(util, 'getDate').andCallFake -> date expect(util.unixTimestamp()).toEqual(600) it 'should allow date parameter', -> date = new Date(660000); date.setMilliseconds(0) expect(util.unixTimestamp(date)).toEqual(660) it 'should return date formatted as unix timestamp with milliseconds', -> spyOn(util, 'getDate').andCallFake -> new Date(600123) expect(util.unixTimestamp()).toEqual(600.123) describe 'AWS.util.string', -> len = AWS.util.string.byteLength describe 'byteLength', -> it 'handles null/undefined objects', -> expect(len(undefined)).toEqual(0) expect(len(null)).toEqual(0) it 'handles buffer input', -> expect(len(new Buffer('∂ƒ©∆'))).toEqual(10) it 'handles string input', -> expect(len('')).toEqual(0) expect(len('∂ƒ©∆')).toEqual(10) if AWS.util.isNode() it 'handles file object input (path property)', -> fs = require('fs') file = fs.createReadStream(__filename) fileLen = fs.lstatSync(file.path).size expect(len(file)).toEqual(fileLen) expect(len(path: __filename)).toEqual(fileLen) it 'fails if input is not a string, buffer, or file', -> err = null try len(3.14) catch e err = e expect(err.message).toEqual('Cannot determine length of 3.14') expect(err.object).toBe(3.14) it 'ignores path property unless it is a string', -> object = {} err = null try len(object) catch e err = e expect(err.message).toMatch(/Cannot determine length of /) expect(err.object).toBe(object) describe 'AWS.util.ini', -> describe 'parse', -> it 'parses an ini file', -> ini = ''' ; comment at the beginning of the line [section1] ; comment at end of line invalidline key1=value1 ; another comment key2 = value2;value3 [emptysection] ''' map = AWS.util.ini.parse(ini) expect(map.section1.key1).toEqual('value1') expect(map.section1.key2).toEqual('value2;value3') expect(map.emptysection).toBe(undefined) describe 'AWS.util.buffer', -> describe 'concat', -> it 'concatenates a list of buffers', -> buffer1 = new Buffer('abcdefg') buffer2 = new Buffer('hijklmn') buffer3 = AWS.util.buffer.concat([buffer1, buffer2]) expect(buffer3.length).toEqual(14) expect(buffer3.toString()).toEqual('abcdefghijklmn') describe 'AWS.util.crypto', -> util = AWS.util.crypto describe 'crc32', -> it 'returns the correct CRC32 value for binary data', -> buffer = new Buffer(4433); for i in [0...buffer.length] buffer.writeUInt8 i % 256, i expect(util.crc32(buffer)).toEqual(899332870) it 'handles String values', -> string = '{"ConsumedCapacityUnits":1.0}' expect(util.crc32(string)).toEqual(2614884069) describe 'toHex', -> it 'should convert binary data to hex string', -> expect(util.toHex('ABC')).toEqual('414243') describe 'hmac', -> input = 'foo' key = 'KEY' result = '116a3725a3540067a09e4dba64bb6b3fb27b4d98a1a2e2dbcb8b4cffa73585d5' it 'should return a keyed hash as a binary digest', -> expected = util.hmac(key, input) expect(util.toHex(expected)).toEqual(result) it 'should return a keyed hash as hex digest', -> expected = util.hmac(key, input, 'hex') expect(expected).toEqual(result) it 'accepts the crypto function as an argument', -> r = util.hmac('secret', 'the quick brown fox', 'base64', 'sha1') expect(r).toEqual('z1BzGT+uG/2qGzE1UHb5m/skn1E=') it 'accepts UTF-8 input for string', -> r = util.hmac('foo', 'å∆ç∂', 'hex') expect(r).toEqual('bc11556145cbe4935ba187b9f8ba0c12bae2c866e5795013dfe2d08cabc33e13') describe 'sha256', -> input = 'foo' result = '2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae' it 'should return binary data hashed with sha256', -> expected = util.sha256(input) expect(util.toHex(expected)).toEqual(result) it 'should return hex data hashed with sha256', -> expected = util.sha256(input, 'hex') expect(expected).toEqual(result) it 'accepts UTF-8 input', -> r = util.sha256('ß∂ƒ©', 'hex') expect(r).toEqual('3c01ddd413d2cacac59a255e4aade0d9058a8a9ea8b2dfe5bb2dc4ed132b4139') describe 'md5', -> input = 'foo' result = 'acbd18db4cc2f85cedef654fccc4a4d8' it 'should return binary data hashed with md5', -> expected = util.md5(input) expect(util.toHex(expected)).toEqual(result) it 'should return hex data hashed with md5', -> expected = util.md5(input, 'hex') expect(expected).toEqual(result) it 'accepts UTF-8 input', -> r = util.md5('ᅢ', 'hex') expect(r).toEqual('b497dbbe19fb58cddaeef11f9d40804c') describe 'AWS.util.each', -> it 'should iterate over a hash', -> parts = [] AWS.util.each {a: 1, b: 2, c: 3}, (key, item) -> parts.push([key + '_', item + 1]) expect(parts).toEqual([['a_', 2], ['b_', 3], ['c_', 4]]) it 'should iterate over an array', -> total = 0 AWS.util.each [1, 2, 3], (idx, item) -> total += item expect(total).toEqual(6) it 'should ignore inherited properties', -> objCtor = -> this.a = 1; this.b = 2; this.c = 3 objCtor.prototype = d: 4, e: 5, f: 6 obj = new objCtor() parts = [] AWS.util.each obj, (key, item) -> parts.push([key + '_', item + 1]) expect(parts).toEqual([['a_', 2], ['b_', 3], ['c_', 4]]) it 'callback should not change "this" scope', -> new -> this.class = 'MyClass' self = this AWS.util.each.apply this, [[1, 2, 3], -> expect(this).toBe(self)] it 'can abort out of loop', -> string = '' AWS.util.each {a: 1, b: 2, c: 3}, (key, item) -> return AWS.util.abort if item == 2 string += key expect(string).toEqual('a') describe 'AWS.util.arrayEach', -> it 'should iterate over arrays', -> total = 0 AWS.util.arrayEach [1, 2, 3], (item) -> total += item expect(total).toEqual(6) it 'should pass index as second parameter', -> lastIndex = null AWS.util.arrayEach [1, 2, 3], (item, idx) -> expect(typeof(idx)).toEqual('number') expect(lastIndex).toEqual(idx - 1) if lastIndex != null lastIndex = idx it 'can abort out of loop', -> total = 0 AWS.util.arrayEach [1, 2, 3], (item, idx) -> return AWS.util.abort if idx == 1 total += item expect(total).toEqual(1) describe 'AWS.util.copy', -> it 'does not copy null or undefined', -> expect(AWS.util.copy(null)).toEqual(null) expect(AWS.util.copy(undefined)).toEqual(undefined) it 'should perform a shallow copy of an object', -> obj = a: 1, b: 2, c: 3 copied = AWS.util.copy(obj) expect(copied).not.toBe(obj) expect(copied).toEqual(a: 1, b: 2, c: 3) it 'should copy inherited properties', -> objCtor = -> this.a = 1; this.b = 2; this.c = 3 objCtor.prototype = d: 4 obj = new objCtor() copied = AWS.util.copy(obj) expect(copied).not.toBe(obj) expect(copied).toEqual(a: 1, b: 2, c: 3, d: 4) describe 'AWS.util.merge', -> it 'should merge an object into another and return new object', -> obj = a: 1, b: 2, c: 3 newObj = AWS.util.merge(obj, {d: 4, e: 5, a: 6}) expect(newObj).toEqual(a: 6, b: 2, c: 3, d: 4, e: 5) expect(obj).toEqual(a: 1, b: 2, c: 3) describe 'AWS.util.update', -> it 'should merge an object into another', -> obj = a: 1, b: 2, c: 3 AWS.util.update(obj, {d: 4, e: 5, a: 6}) expect(obj).toEqual(a: 6, b: 2, c: 3, d: 4, e: 5) it 'should return the merged object', -> obj = a: 1, b: 2 expect(AWS.util.update(obj, c: 3)).toBe(obj) describe 'AWS.util.inherit', -> it 'should inherit an object and append features', -> Base = (value) -> this.defaultValue = value Base.prototype = main: -> 'main' other: 'other' Derived = AWS.util.inherit Base, constructor: (value) -> Base.apply(this, [value + 5]) main: -> 'notMain' foo: -> 'bar' derived = new Derived(5) expect(derived instanceof Base).toBeTruthy() expect(derived.constructor).toBe(Derived) expect(derived.main()).toEqual('notMain') expect(derived.other).toEqual('other') expect(derived.defaultValue).toEqual(10) expect(derived.foo()).toEqual('bar') it 'should create pass-through constructor if not defined', -> Base = AWS.util.inherit constructor: jasmine.createSpy() Derived = AWS.util.inherit Base, other: true derived = new Derived(1, 2, 'three') expect(derived.other).toEqual(true) expect(Base.prototype.constructor).toHaveBeenCalledWith(1, 2, 'three') describe 'AWS.util.mixin', -> it 'copies properties to other object prototype', -> obj = prototype: a: 1, b: 2 AWS.util.mixin(obj, prototype: b: 3, c: 4) expect(obj.prototype).toEqual(a: 1, b: 3, c: 4) it 'resets prototype constructor', -> obj = prototype: constructor: 'PASS' AWS.util.mixin(obj, prototype: constructor: 'FAIL') expect(obj.prototype).toEqual(constructor: 'PASS') it 'returns original klass', -> obj = prototype: foo: 1 out = AWS.util.mixin(obj, prototype: bar: 2) expect(out).toBe(obj) describe 'AWS.util.isType', -> it 'accepts function for type', -> expect(AWS.util.isType([], Array)).toEqual(true) it 'accepts string for type', -> expect(AWS.util.isType([], 'Array')).toEqual(true) describe 'AWS.util.isEmpty', -> it 'returns true when passed an empty object literal', -> expect(AWS.util.isEmpty({})).toEqual(true) it 'returns true when passed a non-empty object literal', -> expect(AWS.util.isEmpty({a:1})).toEqual(false) describe 'AWS.util.error', -> it 'returns the created error object with extra options', -> origError = new Error() err = AWS.util.error(origError, message: 'msg', code: 'code') expect(err).toBe(origError) expect(err.message).toEqual('msg') expect(err.code).toEqual('code') it 'accepts missing options', -> origError = new Error('ERROR') err = AWS.util.error(origError) expect(err).toBe(origError) expect(err.message).toEqual('ERROR') it 'maintains the original error message property', -> origError = new Error('ERROR') err = AWS.util.error(origError, {code: 'code'}) expect(err).toBe(origError) expect(err.message).toEqual('ERROR') expect(err.code).toEqual('code') it 'keeps track of the old error', -> origError = new Error('ERROR') origError.value = 1 err = AWS.util.error(origError, code: 'code', message: 'FOO') expect(err.originalError.message).toEqual('ERROR') expect(err.originalError.code).toEqual(undefined) expect(err.originalError.value).toEqual(1) describe 'AWS.util.base64', -> base64 = AWS.util.base64 describe 'encode', -> it 'encodes the given string', -> expect(base64.encode('foo')).toEqual('Zm9v') expect(base64.encode('ёŝ')).toEqual('0ZHFnQ==') describe 'decode', -> it 'decodes the given string', -> expect(base64.decode('Zm9v').toString()).toEqual('foo') expect(base64.decode('0ZHFnQ==').toString()).toEqual('ёŝ') describe 'AWS.util.jamespath', -> query = AWS.util.jamespath.query find = AWS.util.jamespath.find describe 'query', -> it 'can find a toplevel element of a data structure', -> expect(query('foo', foo: 'value')).toEqual(['value']) it 'can find a nested element of a data structure', -> expect(query('foo.bar.baz', foo: bar: baz: 'value')).toEqual(['value']) it 'can index an element (positive and negative indexes)', -> data = foo: bar: [{baz: 'wrong'}, {baz: 'right'}, {baz: 'wrong'}] expect(query('foo.bar[1].baz', data)).toEqual(['right']) expect(query('foo.bar[-2].baz', data)).toEqual(['right']) it 'can index an element with wildcard', -> data = foo: bar: [{baz: 'wrong'}, {baz: 'right'}, {baz: 'wrong'}] expect(query('foo.bar[*].baz', data)).toEqual(['wrong', 'right', 'wrong']) it 'returns empty array if element is not found', -> data = foo: notBar: baz: 'value' expect(query('foo.bar.baz', data)).toEqual([]) it 'allows multiple expressions to be ORed', -> data = foo: {key1: 'wrong'}, bar: {key2: 'right'} expect(query('foo.key2 or bar.key2', data)).toEqual(['right']) it 'returns multiple matches if a wildcard is used', -> data = foo: child1: bar: 'value1' child2: bar: 'value2' child3: bar: 'value3' expect(query('foo.*.bar', data)).toEqual(['value1', 'value2', 'value3']) it 'can support wildcard on both token and index', -> data = foo: child1: ['value1', 'value2'] child2: ['value3'] child4: 'notarray' expect(query('foo.*[*]', data)).toEqual(['value1', 'value2', 'value3']) it 'can support array flattening', -> data = foo: [ {bar: 1}, {bar: 2}, {bar: 3} ] expect(query('foo[].bar', data)).toEqual([1, 2, 3]) describe 'find', -> it 'returns the first match of query', -> expect(find('foo.*', foo: bar: 1, baz: 2)).toEqual(1) it 'returns null if no match is found', -> expect(find('invalid.*', foo: bar: 1, baz: 2)).toEqual(null)