mirror of
https://github.com/serverless/serverless.git
synced 2026-01-18 14:58:43 +00:00
New variables engine supports escape syntax (so e.g. "\${foo:}" was not recognized as variable notation.
Still there was no logic that would clear "\" characters. This patch fixes that
572 lines
18 KiB
JavaScript
572 lines
18 KiB
JavaScript
'use strict';
|
|
|
|
const { expect } = require('chai');
|
|
|
|
const ServerlessError = require('../../../../../lib/serverless-error');
|
|
const parse = require('../../../../../lib/configuration/variables/parse');
|
|
|
|
describe('test/unit/lib/configuration/variables/parse.test.js', () => {
|
|
describe('Valid', () => {
|
|
it('should support partially variable value at begin of a string', () =>
|
|
expect(parse('${type:address}foo')).to.deep.equal([
|
|
{ start: 0, end: 15, sources: [{ type: 'type', address: { value: 'address' } }] },
|
|
]));
|
|
|
|
it('should support partially variable value at end of a string', () =>
|
|
expect(parse('foo${type:address}')).to.deep.equal([
|
|
{ start: 3, end: 18, sources: [{ type: 'type', address: { value: 'address' } }] },
|
|
]));
|
|
|
|
it('should support partially variable value in a middle of a string', () =>
|
|
expect(parse('foo${type:address}bar')).to.deep.equal([
|
|
{ start: 3, end: 18, sources: [{ type: 'type', address: { value: 'address' } }] },
|
|
]));
|
|
|
|
// ${type:}
|
|
it('should support type only notation', () =>
|
|
expect(parse('${type:}')).to.deep.equal([{ sources: [{ type: 'type' }] }]));
|
|
|
|
// ${type:address}
|
|
it('should support type and address', () =>
|
|
expect(parse('${type:address}')).to.deep.equal([
|
|
{ sources: [{ type: 'type', address: { value: 'address' } }] },
|
|
]));
|
|
|
|
// ${type(param)}
|
|
it('should support param', () =>
|
|
expect(parse('${type(param)}')).to.deep.equal([
|
|
{ sources: [{ type: 'type', params: [{ value: 'param' }] }] },
|
|
]));
|
|
|
|
// ${type(param1, param2)}
|
|
it('should support multiple params', () =>
|
|
expect(parse('${type(param1, param2)}')).to.deep.equal([
|
|
{ sources: [{ type: 'type', params: [{ value: 'param1' }, { value: 'param2' }] }] },
|
|
]));
|
|
|
|
// ${type(param1, ",},${\"param2", param3)}
|
|
it('should support double quoted params', () =>
|
|
expect(parse('${type(param1, ",},${\\"param2", param3)}')).to.deep.equal([
|
|
{
|
|
sources: [
|
|
{
|
|
type: 'type',
|
|
params: [{ value: 'param1' }, { value: ',},${"param2' }, { value: 'param3' }],
|
|
},
|
|
],
|
|
},
|
|
]));
|
|
|
|
// ${type(param1, ',},${\'param2' )}
|
|
it('should support single quoted params', () =>
|
|
expect(parse("${type(param1, ',},${param2' )}")).to.deep.equal([
|
|
{
|
|
sources: [
|
|
{
|
|
type: 'type',
|
|
params: [{ value: 'param1' }, { value: ',},${param2' }],
|
|
},
|
|
],
|
|
},
|
|
]));
|
|
|
|
// ${type(param1, 232, param3)}
|
|
it('should support number params', () =>
|
|
expect(parse('${type(param1, 232, param3)}')).to.deep.equal([
|
|
{
|
|
sources: [
|
|
{
|
|
type: 'type',
|
|
params: [{ value: 'param1' }, { value: 232 }, { value: 'param3' }],
|
|
},
|
|
],
|
|
},
|
|
]));
|
|
|
|
// ${type(param1, true, param3)}
|
|
it('should support boolean params', () =>
|
|
expect(parse('${type(param1, true, param3)}')).to.deep.equal([
|
|
{
|
|
sources: [
|
|
{
|
|
type: 'type',
|
|
params: [{ value: 'param1' }, { value: true }, { value: 'param3' }],
|
|
},
|
|
],
|
|
},
|
|
]));
|
|
|
|
// ${type(param1, null, param3) }
|
|
it('should support null params', () =>
|
|
expect(parse('${type(param1, null, param3) }')).to.deep.equal([
|
|
{
|
|
sources: [
|
|
{
|
|
type: 'type',
|
|
params: [{ value: 'param1' }, { value: null }, { value: 'param3' }],
|
|
},
|
|
],
|
|
},
|
|
]));
|
|
|
|
// ${type(param):address}
|
|
it('should support param and address', () =>
|
|
expect(parse('${type(param):address}')).to.deep.equal([
|
|
{
|
|
sources: [{ type: 'type', params: [{ value: 'param' }], address: { value: 'address' } }],
|
|
},
|
|
]));
|
|
|
|
// ${type(param):",},${\"address"}
|
|
it('should support quoted address', () =>
|
|
expect(parse('${type(param):",},${\\"address"}')).to.deep.equal([
|
|
{
|
|
sources: [
|
|
{ type: 'type', params: [{ value: 'param' }], address: { value: ',},${"address' } },
|
|
],
|
|
},
|
|
]));
|
|
|
|
// ${type(param):'}$address' }
|
|
it('should support single quoted address', () =>
|
|
expect(parse("${type(param):'}$address' }")).to.deep.equal([
|
|
{
|
|
sources: [
|
|
{ type: 'type', params: [{ value: 'param' }], address: { value: '}$address' } },
|
|
],
|
|
},
|
|
]));
|
|
|
|
// ${type1(param1), type2(param2):address2, type3:, type4:address4}
|
|
it('should support fallback sources', () =>
|
|
expect(
|
|
parse('${type1(param1), type2(param2):"address2", type3:, type4:address4}')
|
|
).to.deep.equal([
|
|
{
|
|
sources: [
|
|
{ type: 'type1', params: [{ value: 'param1' }] },
|
|
{ type: 'type2', params: [{ value: 'param2' }], address: { value: 'address2' } },
|
|
{ type: 'type3' },
|
|
{ type: 'type4', address: { value: 'address4' } },
|
|
],
|
|
},
|
|
]));
|
|
|
|
// ${type(param), "foo, bar"}
|
|
it('should support double quoted string as fallback source', () =>
|
|
expect(parse('${type(param), "foo, bar"}')).to.deep.equal([
|
|
{
|
|
sources: [{ type: 'type', params: [{ value: 'param' }] }, { value: 'foo, bar' }],
|
|
},
|
|
]));
|
|
|
|
// ${type(param), 'foo, bar' }
|
|
it('should support single quoted string as fallback source', () => {
|
|
expect(parse("${type(param), 'foo, bar' }")).to.deep.equal([
|
|
{
|
|
sources: [{ type: 'type', params: [{ value: 'param' }] }, { value: 'foo, bar' }],
|
|
},
|
|
]);
|
|
});
|
|
|
|
// ${type(param), 232}
|
|
it('should support number as a fallback source', () =>
|
|
expect(parse('${type(param), 232}')).to.deep.equal([
|
|
{
|
|
sources: [{ type: 'type', params: [{ value: 'param' }] }, { value: 232 }],
|
|
},
|
|
]));
|
|
|
|
// ${type(param), true}
|
|
it('should support bolean "true" as a fallback source', () =>
|
|
expect(parse('${type(param), true}')).to.deep.equal([
|
|
{
|
|
sources: [{ type: 'type', params: [{ value: 'param' }] }, { value: true }],
|
|
},
|
|
]));
|
|
|
|
// ${type(param), false }
|
|
it('should support bolean "false" as a fallback source', () =>
|
|
expect(parse('${type(param), false }')).to.deep.equal([
|
|
{
|
|
sources: [{ type: 'type', params: [{ value: 'param' }] }, { value: false }],
|
|
},
|
|
]));
|
|
|
|
// ${type(param), null}
|
|
it('should support null as a fallback source', () =>
|
|
expect(parse('${type(param), null}')).to.deep.equal([
|
|
{
|
|
sources: [{ type: 'type', params: [{ value: 'param' }] }, { value: null }],
|
|
},
|
|
]));
|
|
|
|
// ${type()}
|
|
it('should support empty parens', () =>
|
|
expect(parse('${type()}')).to.deep.equal([
|
|
{
|
|
sources: [{ type: 'type', params: [] }],
|
|
},
|
|
]));
|
|
|
|
// ${type(\t)}
|
|
it('should ignore any whitespace between brackets', () =>
|
|
expect(parse('${type(\t)}')).to.deep.equal([{ sources: [{ type: 'type', params: [] }] }]));
|
|
|
|
// ${type(,,)}
|
|
it('should support skipping arguments', () =>
|
|
expect(parse('${type(,,)}')).to.deep.equal([
|
|
{
|
|
sources: [{ type: 'type', params: [{ value: null }, { value: null }] }],
|
|
},
|
|
]));
|
|
|
|
// ${type(${innerType(innerParam, ${deep:}):innerAddress}, foo${bar:}): address}
|
|
it('should support variables in params', () =>
|
|
expect(
|
|
parse('${type(${innerType(innerParam, ${deep:}):innerAddress}, foo${bar:}): address}')
|
|
).to.deep.equal([
|
|
{
|
|
sources: [
|
|
{
|
|
type: 'type',
|
|
params: [
|
|
{
|
|
value: '${innerType(innerParam, ${deep:}):innerAddress}',
|
|
variables: [
|
|
{
|
|
sources: [
|
|
{
|
|
type: 'innerType',
|
|
params: [
|
|
{ value: 'innerParam' },
|
|
{ value: '${deep:}', variables: [{ sources: [{ type: 'deep' }] }] },
|
|
],
|
|
address: { value: 'innerAddress' },
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
value: 'foo${bar:}',
|
|
variables: [
|
|
{
|
|
start: 3,
|
|
end: 10,
|
|
sources: [{ type: 'bar' }],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
address: { value: 'address' },
|
|
},
|
|
],
|
|
},
|
|
]));
|
|
|
|
// ${type(params):${innerType(innerParam):innerAddress}}
|
|
// ${type(param):foo${innerType(innerParam)}}
|
|
it('should support variables in address', () => {
|
|
expect(parse('${type(param):${innerType(innerParam):innerAddress}}')).to.deep.equal([
|
|
{
|
|
sources: [
|
|
{
|
|
type: 'type',
|
|
params: [{ value: 'param' }],
|
|
address: {
|
|
value: '${innerType(innerParam):innerAddress}',
|
|
variables: [
|
|
{
|
|
sources: [
|
|
{
|
|
type: 'innerType',
|
|
params: [{ value: 'innerParam' }],
|
|
address: { value: 'innerAddress' },
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
]);
|
|
expect(parse('${type(param):foo${innerType(innerParam)}}')).to.deep.equal([
|
|
{
|
|
sources: [
|
|
{
|
|
type: 'type',
|
|
params: [{ value: 'param' }],
|
|
address: {
|
|
value: 'foo${innerType(innerParam)}',
|
|
variables: [
|
|
{
|
|
start: 3,
|
|
end: 27,
|
|
sources: [
|
|
{
|
|
type: 'innerType',
|
|
params: [{ value: 'innerParam' }],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
]);
|
|
});
|
|
|
|
// ${type.dot(param)}
|
|
it('should support dots in type notation', () =>
|
|
expect(parse('${type.dot(param)}')).to.deep.equal([
|
|
{ sources: [{ type: 'type.dot', params: [{ value: 'param' }] }] },
|
|
]));
|
|
|
|
// ${type.us-east-1(param)}
|
|
it('should support hyphens in type notation', () =>
|
|
expect(parse('${type.us-east-1(param)}')).to.deep.equal([
|
|
{ sources: [{ type: 'type.us-east-1', params: [{ value: 'param' }] }] },
|
|
]));
|
|
|
|
// ${AWS::${type:address}}
|
|
it('should support variable nested in foreign variable', () =>
|
|
expect(parse('${AWS::${type:address}}')).to.deep.equal([
|
|
{ start: 7, end: 22, sources: [{ type: 'type', address: { value: 'address' } }] },
|
|
]));
|
|
|
|
// ${sourceDirect:}elo${sourceIncomplete:}
|
|
it('should support multiple variables in a value', () =>
|
|
expect(parse('${type1:}elo${type2:}')).to.deep.equal([
|
|
{ start: 0, end: 9, sources: [{ type: 'type1' }] },
|
|
{ start: 12, end: 21, sources: [{ type: 'type2' }] },
|
|
]));
|
|
|
|
// ${s:${s:}, 1}
|
|
// https://github.com/serverless/serverless/issues/8999
|
|
it("should recognize variables in address, if it's followed by source", () =>
|
|
expect(parse('${s:${s:}, 1}')).to.deep.equal([
|
|
{
|
|
sources: [
|
|
{
|
|
type: 's',
|
|
address: {
|
|
value: '${s:}',
|
|
variables: [{ sources: [{ type: 's' }] }],
|
|
},
|
|
},
|
|
{ value: 1 },
|
|
],
|
|
},
|
|
]));
|
|
|
|
// ${s:, s:${s:}}
|
|
// https://github.com/serverless/serverless/issues/9010
|
|
it('should resolve nested sources, when at least one parent source was resolved', () =>
|
|
expect(parse('${s:, s:${s:}}')).to.deep.equal([
|
|
{
|
|
sources: [
|
|
{ type: 's' },
|
|
{
|
|
type: 's',
|
|
address: {
|
|
value: '${s:}',
|
|
variables: [{ sources: [{ type: 's' }] }],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
]));
|
|
});
|
|
|
|
describe('Invalid', () => {
|
|
// ${type(${invalid.notation}):address}
|
|
it('should reject invalid configuration in params', () =>
|
|
expect(() => parse('${type(${invalid.notation}):address}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_TYPE'));
|
|
|
|
// ${type(params):${innerType(innerParam):${sdfs.fefef}
|
|
it('should reject invalid configuration in address', () =>
|
|
expect(() => parse('${type(params):${innerType(innerParam):${sdfs.fefef}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_TYPE'));
|
|
|
|
// ${type:foo:bar}
|
|
it('should reject ":" strings in address literal', () =>
|
|
expect(() => parse('${type:foo:bar}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_ADDRESS'));
|
|
|
|
// ${type:address
|
|
it('should detect not closed variable', () => {
|
|
expect(() => parse('${type:address'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'UNTERMINATED_VARIABLE');
|
|
expect(() => parse('${type(foo)'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'UNTERMINATED_VARIABLE');
|
|
expect(() => parse('${s:, s:${s:}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'UNTERMINATED_VARIABLE');
|
|
expect(() => parse('${s:, s:${s:'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'UNTERMINATED_VARIABLE');
|
|
});
|
|
|
|
// ${type("\u")}
|
|
it('should reject invalid string literal', () =>
|
|
expect(() => parse('${type("\\u")}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_STRING_LITERAL'));
|
|
|
|
// '${type:foo, elo}
|
|
it('should reject invalid source literal', () =>
|
|
expect(() => parse('${type:foo, elo}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_LITERAL_SOURCE'));
|
|
|
|
// ${type('foo')bar}
|
|
it('should reject missing colon for address', () =>
|
|
expect(() => parse("${type('foo')bar}"))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_ADDRESS'));
|
|
|
|
// ${type:"address"marko}
|
|
it('should reject invalid address configuration', () =>
|
|
expect(() => parse('${type:"address"marko}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_ADDRESS'));
|
|
|
|
// ${type:foo, ---}
|
|
// ${type:foo, 000:}
|
|
// ${type:foo, 000()}
|
|
// ${type:foo, aa--}
|
|
it('should reject invalid following source', () => {
|
|
expect(() => parse('${type:foo, ___}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_SOURCE');
|
|
|
|
expect(() => parse('${type:foo, --}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_LITERAL_SOURCE');
|
|
|
|
expect(() => parse('${type:foo, 000:}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_SOURCE');
|
|
|
|
expect(() => parse('${type:foo, 000()}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_SOURCE');
|
|
|
|
expect(() => parse('${type:foo, aa--}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_LITERAL_SOURCE');
|
|
|
|
expect(() => parse('${type:foo, aa__}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_SOURCE');
|
|
|
|
expect(() => parse('${type:foo,"dev",20}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_SOURCE');
|
|
});
|
|
|
|
// ${type(${AWS::Region})}
|
|
// ${type(${foo::Region})}
|
|
it('should reject nested foreign variables', () => {
|
|
expect(() => parse('${type(${AWS::Region})}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_TYPE');
|
|
|
|
expect(() => parse('${type(${foo::Region})}'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_ADDRESS');
|
|
});
|
|
|
|
// ${type(foo})
|
|
// ${type(foo,})
|
|
it('should reject closing bracket at unexpected location', () => {
|
|
expect(() => parse('${type(foo})'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_PARAM');
|
|
expect(() => parse('${type(foo,})'))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_PARAM');
|
|
});
|
|
|
|
// ${type('foo'bar)}
|
|
it('should reject unexpected content after param string', () =>
|
|
expect(() => parse("${type('foo'bar)}"))
|
|
.to.throw(ServerlessError)
|
|
.with.property('code', 'INVALID_VARIABLE_PARAM'));
|
|
});
|
|
|
|
describe('Foreign', () => {
|
|
// ${}
|
|
it('should ignore empty value', () => expect(parse('${}')).to.equal(null));
|
|
|
|
// ${${AWS::Region}}
|
|
it('should ignore nested foreign notations', () =>
|
|
expect(parse('${${AWS::Region}}')).to.equal(null));
|
|
|
|
// ${type}
|
|
it('should ignore just type name string', () => expect(parse('${type}')).to.equal(null));
|
|
|
|
// ${AWS::Region}
|
|
// ${foo::Region}
|
|
it('should ignore double clon vars ', () => {
|
|
expect(parse('${AWS::Region}')).to.equal(null);
|
|
expect(parse('${foo::Region}')).to.equal(null);
|
|
});
|
|
|
|
// ${Database}
|
|
// ${stageVariables.stageName}
|
|
it('should ignore AWS CF references', () => {
|
|
expect(parse('${Database}')).to.equal(null);
|
|
expect(parse('foo ${stageVariables.stageName} var')).to.equal(null);
|
|
});
|
|
|
|
// ${sour${inner.type}ce}
|
|
it('should ignore nested not supported notations', () =>
|
|
expect(parse('fo${bla${foo}}o ${so,ur${inner.type}ce} var')).to.equal(null));
|
|
});
|
|
|
|
describe('Not used', () => {
|
|
// foo bar ()
|
|
it('should return null', () => expect(parse('fo$o b$$ar ()')).to.equal(null));
|
|
|
|
// foo\${elo:}
|
|
it('should support escape character', () =>
|
|
expect(parse('e\\${s:}n\\$${s:}qe\\\\\\${s:}qn\\\\${s:}')).to.deep.equal([
|
|
{
|
|
start: 1,
|
|
end: 2,
|
|
value: '',
|
|
},
|
|
{
|
|
start: 10,
|
|
end: 15,
|
|
sources: [{ type: 's' }],
|
|
},
|
|
{
|
|
start: 17,
|
|
end: 20,
|
|
value: '\\',
|
|
},
|
|
{
|
|
start: 27,
|
|
end: 29,
|
|
value: '\\',
|
|
},
|
|
{
|
|
start: 29,
|
|
end: 34,
|
|
sources: [{ type: 's' }],
|
|
},
|
|
]));
|
|
});
|
|
});
|