Expand linting to include "test" and "types" folders (#447)

This commit is contained in:
Eric Jizba 2021-11-19 10:32:27 -08:00 коммит произвёл GitHub
Родитель 2b68cbb806
Коммит f7392cd458
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 1475 добавлений и 1396 удалений

Просмотреть файл

@ -17,13 +17,19 @@
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-floating-promises": "off",
"@typescript-eslint/no-misused-promises": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-unnecessary-type-assertion": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/require-await": "off",
"@typescript-eslint/restrict-plus-operands": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/unbound-method": "off",
@ -31,5 +37,10 @@
"no-useless-escape": "off",
"prefer-rest-params": "off",
"prefer-spread": "off"
}
},
"ignorePatterns": [
"**/*.js",
"dist",
"azure-functions-language-worker-protobuf"
]
}

Просмотреть файл

@ -59,8 +59,8 @@
"build-nomaps": "npm run build -- --sourceMap false",
"gen": "node scripts/generateProtos.js",
"test": "mocha -r ts-node/register ./test/**/*.ts --reporter mocha-multi-reporters --reporter-options configFile=test/mochaReporterOptions.json",
"lint": "eslint src",
"lint-fix": "eslint src --fix",
"lint": "eslint .",
"lint-fix": "eslint . --fix",
"watch": "node ./node_modules/typescript/bin/tsc --watch",
"prepare": "husky install"
},

Просмотреть файл

@ -1,193 +1,192 @@
import { getNormalizedBindingData, toRpcHttp, getBindingDefinitions, fromTypedData } from '../src/converters';
import { FunctionInfo } from '../src/FunctionInfo';
import { expect } from 'chai';
import * as sinon from 'sinon';
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
import 'mocha';
import { fromString } from 'long';
import 'mocha';
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
import { fromTypedData, getBindingDefinitions, getNormalizedBindingData, toRpcHttp } from '../src/converters';
import { FunctionInfo } from '../src/FunctionInfo';
describe('Binding Converters', () => {
it('normalizes binding trigger metadata for HTTP', () => {
var mockRequest: rpc.ITypedData = toRpcHttp({ url: "https://mock"});
var triggerDataMock: { [k: string]: rpc.ITypedData } = {
"Headers": {
json: JSON.stringify({Connection: 'Keep-Alive'})
},
"Req": mockRequest,
"Sys": {
json: JSON.stringify({MethodName: 'test-js', UtcNow: '2018', RandGuid: '3212'})
},
"$request": {
string: "Https://mock/"
},
"falsyZero": {
string : "0",
},
"falsyFalse": {
string : "false",
},
"falsyNull": {
string : "null"
},
"falsyEmptyString": {
string : "",
},
"falsyUndefined": {
string : undefined
}
};
var request: rpc.IInvocationRequest = <rpc.IInvocationRequest> {
triggerMetadata: triggerDataMock,
invocationId: "12341"
}
var bindingData = getNormalizedBindingData(request);
// Verify conversion to camelCase
expect(bindingData.invocationId).to.equal('12341');
expect(bindingData.headers.connection).to.equal('Keep-Alive');
expect(bindingData.req.http.url).to.equal("https://mock");
expect(bindingData.sys.methodName).to.equal('test-js');
expect(bindingData.sys.utcNow).to.equal('2018');
expect(bindingData.sys.randGuid).to.equal('3212');
expect(bindingData.$request).to.equal('Https://mock/');
expect(bindingData.falsyZero).to.equal(0);
expect(bindingData.falsyFalse).to.equal(false);
expect(bindingData.falsyNull).to.equal(null);
expect(bindingData.falsyEmptyString.string).to.equal("");
expect(bindingData.falsyUndefined.string).to.equal(undefined);
// Verify accessing original keys is undefined
expect(bindingData.Sys).to.be.undefined;
expect(bindingData.sys.UtcNow).to.be.undefined;
});
it('normalizes binding trigger metadata containing arrays', () => {
var triggerDataMock: { [k: string]: rpc.ITypedData } = {
"EnqueuedMessages": {
json: JSON.stringify(["Hello 1", "Hello 2"])
},
"SequenceNumberArray": {
json: JSON.stringify([1, 2])
},
"Properties": {
json: JSON.stringify({"Greetings": ["Hola", "Salut", "Konichiwa"], "SequenceNumber": [1, 2, 3]})
},
"Sys": {
json: JSON.stringify({MethodName: 'test-js', UtcNow: '2018', RandGuid: '3212'})
}
};
var request: rpc.IInvocationRequest = <rpc.IInvocationRequest> {
triggerMetadata: triggerDataMock,
invocationId: "12341"
}
var bindingData = getNormalizedBindingData(request);
// Verify conversion to camelCase
expect(bindingData.invocationId).to.equal('12341');
expect(Array.isArray(bindingData.enqueuedMessages)).to.be.true;
expect(bindingData.enqueuedMessages.length).to.equal(2);
expect(bindingData.enqueuedMessages[1]).to.equal("Hello 2");
expect(Array.isArray(bindingData.sequenceNumberArray)).to.be.true;
expect(bindingData.sequenceNumberArray.length).to.equal(2);
expect(bindingData.sequenceNumberArray[0]).to.equal(1);
expect(bindingData.sys.methodName).to.equal('test-js');
expect(bindingData.sys.utcNow).to.equal('2018');
expect(bindingData.sys.randGuid).to.equal('3212');
// Verify that nested arrays are converted correctly
let properties = bindingData.properties;
expect(Array.isArray(properties.greetings)).to.be.true;
expect(properties.greetings.length).to.equal(3);
expect(properties.greetings[1]).to.equal("Salut");
expect(Array.isArray(properties.sequenceNumber)).to.be.true;
expect(properties.sequenceNumber.length).to.equal(3);
expect(properties.sequenceNumber[0]).to.equal(1);
// Verify accessing original keys is undefined
expect(bindingData.Sys).to.be.undefined;
expect(bindingData.sys.UtcNow).to.be.undefined;
});
it('catologues binding definitions', () => {
let functionMetaData: rpc.IRpcFunctionMetadata = <rpc.IRpcFunctionMetadata> {
name: "MyFunction",
directory: ".",
scriptFile: "index.js",
bindings: {
req: {
type: "httpTrigger",
direction: rpc.BindingInfo.Direction.in
it('normalizes binding trigger metadata for HTTP', () => {
const mockRequest: rpc.ITypedData = toRpcHttp({ url: 'https://mock' });
const triggerDataMock: { [k: string]: rpc.ITypedData } = {
Headers: {
json: JSON.stringify({ Connection: 'Keep-Alive' }),
},
res: {
type: "http",
direction: rpc.BindingInfo.Direction.out
Req: mockRequest,
Sys: {
json: JSON.stringify({ MethodName: 'test-js', UtcNow: '2018', RandGuid: '3212' }),
},
firstQueueOutput: {
type: "queue",
direction: rpc.BindingInfo.Direction.out
$request: {
string: 'Https://mock/',
},
noDirection: {
type: "queue"
}
}
};
falsyZero: {
string: '0',
},
falsyFalse: {
string: 'false',
},
falsyNull: {
string: 'null',
},
falsyEmptyString: {
string: '',
},
falsyUndefined: {
string: undefined,
},
};
let functionInfo: FunctionInfo = new FunctionInfo(functionMetaData);
var bindingDefinitions = getBindingDefinitions(functionInfo);
// Verify conversion to camelCase
expect(bindingDefinitions.length).to.equal(4);
expect(bindingDefinitions[0].name).to.equal("req");
expect(bindingDefinitions[0].direction).to.equal("in");
expect(bindingDefinitions[0].type).to.equal("httpTrigger");
expect(bindingDefinitions[1].name).to.equal("res");
expect(bindingDefinitions[1].direction).to.equal("out");
expect(bindingDefinitions[1].type).to.equal("http");
expect(bindingDefinitions[2].name).to.equal("firstQueueOutput");
expect(bindingDefinitions[2].direction).to.equal("out");
expect(bindingDefinitions[2].type).to.equal("queue");
expect(bindingDefinitions[3].name).to.equal("noDirection");
expect(bindingDefinitions[3].direction).to.be.undefined;
expect(bindingDefinitions[3].type).to.equal("queue");
});
const request: rpc.IInvocationRequest = <rpc.IInvocationRequest>{
triggerMetadata: triggerDataMock,
invocationId: '12341',
};
it('deserializes string data with fromTypedData', () => {
let data = fromTypedData({ string: "foo" });
expect(data).to.equal("foo");
});
const bindingData = getNormalizedBindingData(request);
// Verify conversion to camelCase
expect(bindingData.invocationId).to.equal('12341');
expect(bindingData.headers.connection).to.equal('Keep-Alive');
expect(bindingData.req.http.url).to.equal('https://mock');
expect(bindingData.sys.methodName).to.equal('test-js');
expect(bindingData.sys.utcNow).to.equal('2018');
expect(bindingData.sys.randGuid).to.equal('3212');
expect(bindingData.$request).to.equal('Https://mock/');
expect(bindingData.falsyZero).to.equal(0);
expect(bindingData.falsyFalse).to.equal(false);
expect(bindingData.falsyNull).to.equal(null);
expect(bindingData.falsyEmptyString.string).to.equal('');
expect(bindingData.falsyUndefined.string).to.equal(undefined);
// Verify accessing original keys is undefined
expect(bindingData.Sys).to.be.undefined;
expect(bindingData.sys.UtcNow).to.be.undefined;
});
it('deserializes json data with fromTypedData', () => {
let data = fromTypedData({ json: "\{ \"foo\": \"bar\" }" });
expect(data && data["foo"]).to.equal("bar");
});
it('normalizes binding trigger metadata containing arrays', () => {
const triggerDataMock: { [k: string]: rpc.ITypedData } = {
EnqueuedMessages: {
json: JSON.stringify(['Hello 1', 'Hello 2']),
},
SequenceNumberArray: {
json: JSON.stringify([1, 2]),
},
Properties: {
json: JSON.stringify({ Greetings: ['Hola', 'Salut', 'Konichiwa'], SequenceNumber: [1, 2, 3] }),
},
Sys: {
json: JSON.stringify({ MethodName: 'test-js', UtcNow: '2018', RandGuid: '3212' }),
},
};
const request: rpc.IInvocationRequest = <rpc.IInvocationRequest>{
triggerMetadata: triggerDataMock,
invocationId: '12341',
};
it('deserializes byte data with fromTypedData', () => {
let buffer = Buffer.from("hello");
let data = fromTypedData({ bytes: buffer });
expect(data && data["buffer"]).to.equal(buffer.buffer);
});
const bindingData = getNormalizedBindingData(request);
// Verify conversion to camelCase
expect(bindingData.invocationId).to.equal('12341');
expect(Array.isArray(bindingData.enqueuedMessages)).to.be.true;
expect(bindingData.enqueuedMessages.length).to.equal(2);
expect(bindingData.enqueuedMessages[1]).to.equal('Hello 2');
expect(Array.isArray(bindingData.sequenceNumberArray)).to.be.true;
expect(bindingData.sequenceNumberArray.length).to.equal(2);
expect(bindingData.sequenceNumberArray[0]).to.equal(1);
expect(bindingData.sys.methodName).to.equal('test-js');
expect(bindingData.sys.utcNow).to.equal('2018');
expect(bindingData.sys.randGuid).to.equal('3212');
// Verify that nested arrays are converted correctly
const properties = bindingData.properties;
expect(Array.isArray(properties.greetings)).to.be.true;
expect(properties.greetings.length).to.equal(3);
expect(properties.greetings[1]).to.equal('Salut');
expect(Array.isArray(properties.sequenceNumber)).to.be.true;
expect(properties.sequenceNumber.length).to.equal(3);
expect(properties.sequenceNumber[0]).to.equal(1);
// Verify accessing original keys is undefined
expect(bindingData.Sys).to.be.undefined;
expect(bindingData.sys.UtcNow).to.be.undefined;
});
it('deserializes collectionBytes data with fromTypedData', () => {
let fooBuffer = Buffer.from("foo");
let barBuffer = Buffer.from("bar");
let data = fromTypedData({ collectionBytes: { bytes: [fooBuffer, barBuffer] } });
expect(data && data[0] && data[0]["buffer"]).to.equal(fooBuffer.buffer);
expect(data && data[1] && data[1]["buffer"]).to.equal(barBuffer.buffer);
});
it('catologues binding definitions', () => {
const functionMetaData: rpc.IRpcFunctionMetadata = <rpc.IRpcFunctionMetadata>{
name: 'MyFunction',
directory: '.',
scriptFile: 'index.js',
bindings: {
req: {
type: 'httpTrigger',
direction: rpc.BindingInfo.Direction.in,
},
res: {
type: 'http',
direction: rpc.BindingInfo.Direction.out,
},
firstQueueOutput: {
type: 'queue',
direction: rpc.BindingInfo.Direction.out,
},
noDirection: {
type: 'queue',
},
},
};
it('deserializes collectionString data with fromTypedData', () => {
let data = fromTypedData({ collectionString: { string: ["foo", "bar"] } });
expect(data && data[0]).to.equal("foo");
expect(data && data[1]).to.equal("bar");
});
const functionInfo: FunctionInfo = new FunctionInfo(functionMetaData);
it('deserializes collectionDouble data with fromTypedData', () => {
let data = fromTypedData({ collectionDouble: { double: [1.1, 2.2] } });
expect(data && data[0]).to.equal(1.1);
expect(data && data[1]).to.equal(2.2);
});
const bindingDefinitions = getBindingDefinitions(functionInfo);
// Verify conversion to camelCase
expect(bindingDefinitions.length).to.equal(4);
expect(bindingDefinitions[0].name).to.equal('req');
expect(bindingDefinitions[0].direction).to.equal('in');
expect(bindingDefinitions[0].type).to.equal('httpTrigger');
expect(bindingDefinitions[1].name).to.equal('res');
expect(bindingDefinitions[1].direction).to.equal('out');
expect(bindingDefinitions[1].type).to.equal('http');
expect(bindingDefinitions[2].name).to.equal('firstQueueOutput');
expect(bindingDefinitions[2].direction).to.equal('out');
expect(bindingDefinitions[2].type).to.equal('queue');
expect(bindingDefinitions[3].name).to.equal('noDirection');
expect(bindingDefinitions[3].direction).to.be.undefined;
expect(bindingDefinitions[3].type).to.equal('queue');
});
it('deserializes collectionSint64 data with fromTypedData', () => {
let data = fromTypedData({ collectionSint64: { sint64: [123, fromString("9007199254740992")] } });
expect(data && data[0]).to.equal(123);
expect(data && data[1]).to.equal("9007199254740992");
});
})
it('deserializes string data with fromTypedData', () => {
const data = fromTypedData({ string: 'foo' });
expect(data).to.equal('foo');
});
it('deserializes json data with fromTypedData', () => {
const data = fromTypedData({ json: '{ "foo": "bar" }' });
expect(data && data['foo']).to.equal('bar');
});
it('deserializes byte data with fromTypedData', () => {
const buffer = Buffer.from('hello');
const data = fromTypedData({ bytes: buffer });
expect(data && data['buffer']).to.equal(buffer.buffer);
});
it('deserializes collectionBytes data with fromTypedData', () => {
const fooBuffer = Buffer.from('foo');
const barBuffer = Buffer.from('bar');
const data = fromTypedData({ collectionBytes: { bytes: [fooBuffer, barBuffer] } });
expect(data && data[0] && data[0]['buffer']).to.equal(fooBuffer.buffer);
expect(data && data[1] && data[1]['buffer']).to.equal(barBuffer.buffer);
});
it('deserializes collectionString data with fromTypedData', () => {
const data = fromTypedData({ collectionString: { string: ['foo', 'bar'] } });
expect(data && data[0]).to.equal('foo');
expect(data && data[1]).to.equal('bar');
});
it('deserializes collectionDouble data with fromTypedData', () => {
const data = fromTypedData({ collectionDouble: { double: [1.1, 2.2] } });
expect(data && data[0]).to.equal(1.1);
expect(data && data[1]).to.equal(2.2);
});
it('deserializes collectionSint64 data with fromTypedData', () => {
const data = fromTypedData({ collectionSint64: { sint64: [123, fromString('9007199254740992')] } });
expect(data && data[0]).to.equal(123);
expect(data && data[1]).to.equal('9007199254740992');
});
});

Просмотреть файл

@ -1,26 +1,25 @@
import { CreateContextAndInputs, LogCallback, ResultCallback } from '../src/Context';
import { Context } from "@azure/functions";
import { FunctionInfo } from '../src/FunctionInfo';
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
import * as sinon from 'sinon';
import { Context } from '@azure/functions';
import { expect } from 'chai';
import 'mocha';
import * as sinon from 'sinon';
import { isFunction } from 'util';
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
import { CreateContextAndInputs } from '../src/Context';
import { FunctionInfo } from '../src/FunctionInfo';
const timerTriggerInput: rpc.IParameterBinding = {
name: "myTimer",
name: 'myTimer',
data: {
json: JSON.stringify({
"Schedule":{
Schedule: {},
ScheduleStatus: {
Last: '2016-10-04T10:15:00+00:00',
LastUpdated: '2016-10-04T10:16:00+00:00',
Next: '2016-10-04T10:20:00+00:00',
},
"ScheduleStatus": {
"Last":"2016-10-04T10:15:00+00:00",
"LastUpdated":"2016-10-04T10:16:00+00:00",
"Next":"2016-10-04T10:20:00+00:00"
},
"IsPastDue":false
})
}
IsPastDue: false,
}),
},
};
describe('Context', () => {
@ -29,248 +28,265 @@ describe('Context', () => {
let _resultCallback: any;
beforeEach(() => {
let info: FunctionInfo = new FunctionInfo({ name: 'test' });
let msg: rpc.IInvocationRequest = {
const info: FunctionInfo = new FunctionInfo({ name: 'test' });
const msg: rpc.IInvocationRequest = {
functionId: 'id',
invocationId: '1',
inputData: []
inputData: [],
};
_logger = sinon.spy();
_resultCallback = sinon.spy();
let { context, inputs } = CreateContextAndInputs(info, msg, _logger, _resultCallback);
const { context, inputs } = CreateContextAndInputs(info, msg, _logger, _resultCallback);
_context = context;
});
it ('camelCases timer trigger input when appropriate', async () => {
var msg: rpc.IInvocationRequest = <rpc.IInvocationRequest> {
it('camelCases timer trigger input when appropriate', async () => {
const msg: rpc.IInvocationRequest = <rpc.IInvocationRequest>{
functionId: 'id',
invocationId: '1',
inputData: [timerTriggerInput]
inputData: [timerTriggerInput],
};
let info: FunctionInfo = new FunctionInfo({
const info: FunctionInfo = new FunctionInfo({
name: 'test',
bindings: {
myTimer: {
type: "timerTrigger",
type: 'timerTrigger',
direction: 0,
dataType: 0
}
}
dataType: 0,
},
},
});
let workerOutputs = CreateContextAndInputs(info, msg, _logger, _resultCallback);
let myTimerWorker = workerOutputs.inputs[0];
const workerOutputs = CreateContextAndInputs(info, msg, _logger, _resultCallback);
const myTimerWorker = workerOutputs.inputs[0];
expect(myTimerWorker.schedule).to.be.empty;
expect(myTimerWorker.scheduleStatus.last).to.equal("2016-10-04T10:15:00+00:00");
expect(myTimerWorker.scheduleStatus.lastUpdated).to.equal("2016-10-04T10:16:00+00:00");
expect(myTimerWorker.scheduleStatus.next).to.equal("2016-10-04T10:20:00+00:00");
expect(myTimerWorker.scheduleStatus.last).to.equal('2016-10-04T10:15:00+00:00');
expect(myTimerWorker.scheduleStatus.lastUpdated).to.equal('2016-10-04T10:16:00+00:00');
expect(myTimerWorker.scheduleStatus.next).to.equal('2016-10-04T10:20:00+00:00');
expect(myTimerWorker.isPastDue).to.equal(false);
});
it ('Does not add sys to bindingData for non-http', async () => {
var msg: rpc.IInvocationRequest = <rpc.IInvocationRequest> {
it('Does not add sys to bindingData for non-http', async () => {
const msg: rpc.IInvocationRequest = <rpc.IInvocationRequest>{
functionId: 'id',
invocationId: '1',
inputData: [timerTriggerInput]
inputData: [timerTriggerInput],
};
let info: FunctionInfo = new FunctionInfo({
const info: FunctionInfo = new FunctionInfo({
name: 'test',
bindings: {
bindings: {
req: {
type: "http",
type: 'http',
direction: 0,
dataType: 1
}
}
dataType: 1,
},
},
});
let { context } = CreateContextAndInputs(info, msg, _logger, _resultCallback);
const { context } = CreateContextAndInputs(info, msg, _logger, _resultCallback);
expect(context.bindingData.sys).to.be.undefined;
expect(context.bindingData.invocationId).to.equal("1");
expect(context.invocationId).to.equal("1");
expect(context.bindingData.invocationId).to.equal('1');
expect(context.invocationId).to.equal('1');
});
it ('Adds correct sys properties for bindingData and http', async () => {
var inputDataValue: rpc.IParameterBinding = {
name: "req",
it('Adds correct sys properties for bindingData and http', async () => {
const inputDataValue: rpc.IParameterBinding = {
name: 'req',
data: {
http: {
body:
{
string: "blahh"
}
}
}
body: {
string: 'blahh',
},
},
},
};
var msg: rpc.IInvocationRequest = <rpc.IInvocationRequest> {
const msg: rpc.IInvocationRequest = <rpc.IInvocationRequest>{
functionId: 'id',
invocationId: '1',
inputData: [inputDataValue]
inputData: [inputDataValue],
};
let info: FunctionInfo = new FunctionInfo({
const info: FunctionInfo = new FunctionInfo({
name: 'test',
bindings: {
bindings: {
req: {
type: "http",
type: 'http',
direction: 0,
dataType: 1
}
}
dataType: 1,
},
},
});
let { context } = CreateContextAndInputs(info, msg, _logger, _resultCallback);
const { context } = CreateContextAndInputs(info, msg, _logger, _resultCallback);
const { bindingData } = context;
expect(bindingData.sys.methodName).to.equal("test");
expect(bindingData.sys.methodName).to.equal('test');
expect(bindingData.sys.randGuid).to.not.be.undefined;
expect(bindingData.sys.utcNow).to.not.be.undefined;
expect(bindingData.invocationId).to.equal("1");
expect(context.invocationId).to.equal("1");
expect(bindingData.invocationId).to.equal('1');
expect(context.invocationId).to.equal('1');
});
it ('Adds correct header and query properties for bindingData and http using nullable values', async () => {
var inputDataValue: rpc.IParameterBinding = {
name: "req",
it('Adds correct header and query properties for bindingData and http using nullable values', async () => {
const inputDataValue: rpc.IParameterBinding = {
name: 'req',
data: {
http: {
body:
{
string: "blahh"
body: {
string: 'blahh',
},
nullableHeaders: {
header1: {
value: "value1"
value: 'value1',
},
header2: {
value: ""
}
value: '',
},
},
nullableQuery: {
query1: {
value: "value1"
value: 'value1',
},
query2: {
value: undefined
}
}
}
}
value: undefined,
},
},
},
},
};
var msg: rpc.IInvocationRequest = <rpc.IInvocationRequest> {
const msg: rpc.IInvocationRequest = <rpc.IInvocationRequest>{
functionId: 'id',
invocationId: '1',
inputData: [inputDataValue]
inputData: [inputDataValue],
};
let info: FunctionInfo = new FunctionInfo({
const info: FunctionInfo = new FunctionInfo({
name: 'test',
bindings: {
bindings: {
req: {
type: "http",
type: 'http',
direction: 0,
dataType: 1
}
}
dataType: 1,
},
},
});
let { context } = CreateContextAndInputs(info, msg, _logger, _resultCallback);
const { context } = CreateContextAndInputs(info, msg, _logger, _resultCallback);
const { bindingData } = context;
expect(bindingData.invocationId).to.equal("1");
expect(bindingData.headers.header1).to.equal("value1");
expect(bindingData.headers.header2).to.equal("");
expect(bindingData.query.query1).to.equal("value1");
expect(bindingData.query.query2).to.equal("");
expect(context.invocationId).to.equal("1");
expect(bindingData.invocationId).to.equal('1');
expect(bindingData.headers.header1).to.equal('value1');
expect(bindingData.headers.header2).to.equal('');
expect(bindingData.query.query1).to.equal('value1');
expect(bindingData.query.query2).to.equal('');
expect(context.invocationId).to.equal('1');
});
it ('Adds correct header and query properties for bindingData and http using non-nullable values', async () => {
var inputDataValue: rpc.IParameterBinding = {
name: "req",
it('Adds correct header and query properties for bindingData and http using non-nullable values', async () => {
const inputDataValue: rpc.IParameterBinding = {
name: 'req',
data: {
http: {
body:
{
string: "blahh"
body: {
string: 'blahh',
},
headers: {
header1: "value1"
header1: 'value1',
},
query: {
query1: "value1"
}
}
}
query1: 'value1',
},
},
},
};
var msg: rpc.IInvocationRequest = <rpc.IInvocationRequest> {
const msg: rpc.IInvocationRequest = <rpc.IInvocationRequest>{
functionId: 'id',
invocationId: '1',
inputData: [inputDataValue]
inputData: [inputDataValue],
};
let info: FunctionInfo = new FunctionInfo({
const info: FunctionInfo = new FunctionInfo({
name: 'test',
bindings: {
bindings: {
req: {
type: "http",
type: 'http',
direction: 0,
dataType: 1
}
}
dataType: 1,
},
},
});
let { context } = CreateContextAndInputs(info, msg, _logger, _resultCallback);
const { context } = CreateContextAndInputs(info, msg, _logger, _resultCallback);
const { bindingData } = context;
expect(bindingData.invocationId).to.equal("1");
expect(bindingData.headers.header1).to.equal("value1");
expect(bindingData.query.query1).to.equal("value1");
expect(context.invocationId).to.equal("1");
expect(bindingData.invocationId).to.equal('1');
expect(bindingData.headers.header1).to.equal('value1');
expect(bindingData.query.query1).to.equal('value1');
expect(context.invocationId).to.equal('1');
});
it ('async function logs error on calling context.done', async () => {
it('async function logs error on calling context.done', async () => {
await callUserFunc(BasicAsync.asyncAndCallback, _context);
sinon.assert.calledOnce(_logger);
sinon.assert.calledWith(_logger, rpc.RpcLog.Level.Error, rpc.RpcLog.RpcLogCategory.User, "Error: Choose either to return a promise or call 'done'. Do not use both in your script.");
sinon.assert.calledWith(
_logger,
rpc.RpcLog.Level.Error,
rpc.RpcLog.RpcLogCategory.User,
"Error: Choose either to return a promise or call 'done'. Do not use both in your script."
);
});
it ('async function calls callback and returns value without context.done', async () => {
it('async function calls callback and returns value without context.done', async () => {
await callUserFunc(BasicAsync.asyncPlainFunction, _context);
sinon.assert.calledOnce(_resultCallback);
sinon.assert.calledWith(_resultCallback, null, { bindings: { }, return: "hello" });
sinon.assert.calledWith(_resultCallback, null, { bindings: {}, return: 'hello' });
});
it ('function logs error on calling context.done more than once', () => {
it('function logs error on calling context.done more than once', () => {
callUserFunc(BasicCallback.callbackTwice, _context);
sinon.assert.calledOnce(_logger);
sinon.assert.calledWith(_logger, rpc.RpcLog.Level.Error, rpc.RpcLog.RpcLogCategory.User, "Error: 'done' has already been called. Please check your script for extraneous calls to 'done'.");
sinon.assert.calledWith(
_logger,
rpc.RpcLog.Level.Error,
rpc.RpcLog.RpcLogCategory.User,
"Error: 'done' has already been called. Please check your script for extraneous calls to 'done'."
);
});
it ('function logs error on calling context.log after context.done() called', () => {
it('function logs error on calling context.log after context.done() called', () => {
callUserFunc(BasicCallback.callbackOnce, _context);
_context.log("");
_context.log('');
sinon.assert.calledTwice(_logger);
sinon.assert.calledWith(_logger, rpc.RpcLog.Level.Warning, rpc.RpcLog.RpcLogCategory.System, "Warning: Unexpected call to 'log' on the context object after function execution has completed. Please check for asynchronous calls that are not awaited or calls to 'done' made before function execution completes. Function name: test. Invocation Id: 1. Learn more: https://go.microsoft.com/fwlink/?linkid=2097909 ");
sinon.assert.calledWith(
_logger,
rpc.RpcLog.Level.Warning,
rpc.RpcLog.RpcLogCategory.System,
"Warning: Unexpected call to 'log' on the context object after function execution has completed. Please check for asynchronous calls that are not awaited or calls to 'done' made before function execution completes. Function name: test. Invocation Id: 1. Learn more: https://go.microsoft.com/fwlink/?linkid=2097909 "
);
});
it ('function logs error on calling context.log from non-awaited async call', async () => {
it('function logs error on calling context.log from non-awaited async call', async () => {
await callUserFunc(BasicAsync.asyncPlainFunction, _context);
_context.log("");
_context.log('');
sinon.assert.calledTwice(_logger);
sinon.assert.calledWith(_logger, rpc.RpcLog.Level.Warning, rpc.RpcLog.RpcLogCategory.System, "Warning: Unexpected call to 'log' on the context object after function execution has completed. Please check for asynchronous calls that are not awaited or calls to 'done' made before function execution completes. Function name: test. Invocation Id: 1. Learn more: https://go.microsoft.com/fwlink/?linkid=2097909 ");
sinon.assert.calledWith(
_logger,
rpc.RpcLog.Level.Warning,
rpc.RpcLog.RpcLogCategory.System,
"Warning: Unexpected call to 'log' on the context object after function execution has completed. Please check for asynchronous calls that are not awaited or calls to 'done' made before function execution completes. Function name: test. Invocation Id: 1. Learn more: https://go.microsoft.com/fwlink/?linkid=2097909 "
);
});
it ('function calls callback correctly with bindings', () => {
it('function calls callback correctly with bindings', () => {
callUserFunc(BasicCallback.callbackOnce, _context);
sinon.assert.calledOnce(_resultCallback);
sinon.assert.calledWith(_resultCallback, undefined, { bindings: { hello: "world" }, return: undefined });
sinon.assert.calledWith(_resultCallback, undefined, { bindings: { hello: 'world' }, return: undefined });
});
it ('empty function does not call callback', () => {
it('empty function does not call callback', () => {
callUserFunc(BasicCallback.callbackNone, _context);
sinon.assert.notCalled(_resultCallback);
});
})
});
// async test functions
class BasicAsync {
@ -278,8 +294,8 @@ class BasicAsync {
context.done();
}
public static async asyncPlainFunction(context: Context) {
return "hello";
public static async asyncPlainFunction(context: Context) {
return 'hello';
}
}
@ -291,20 +307,20 @@ class BasicCallback {
}
public static callbackOnce(context) {
context.bindings = { "hello": "world" };
context.bindings = { hello: 'world' };
context.done();
}
public static callbackNone(context) {
}
public static callbackNone(context) {}
}
// Does logic in WorkerChannel to call the user function
function callUserFunc(myFunc, context: Context): Promise<any> {
let result = myFunc(context);
if (result && isFunction(result.then)) {
result = result.then(result => (<any>context.done)(null, result, true))
.catch(err => (<any>context.done)(err, null, true));
result = result
.then((result) => (<any>context.done)(null, result, true))
.catch((err) => (<any>context.done)(err, null, true));
}
return result;
}
}

Просмотреть файл

@ -1,64 +1,62 @@
import { FunctionInfo } from '../src/FunctionInfo';
import { expect } from 'chai';
import * as sinon from 'sinon';
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
import 'mocha';
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
import { FunctionInfo } from '../src/FunctionInfo';
describe('FunctionInfo', () => {
/** NullableBool */
it('gets $return output binding converter for http', () => {
let metadata: rpc.IRpcFunctionMetadata = {
bindings: {
req: {
type: "httpTrigger",
direction: 0,
dataType: 1
/** NullableBool */
it('gets $return output binding converter for http', () => {
const metadata: rpc.IRpcFunctionMetadata = {
bindings: {
req: {
type: 'httpTrigger',
direction: 0,
dataType: 1,
},
$return: {
type: 'http',
direction: 1,
dataType: 1,
},
},
$return: {
type: "http",
direction: 1,
dataType: 1
}
}
};
let funcInfo = new FunctionInfo(metadata);
expect(funcInfo.getReturnBinding().converter.name).to.equal("toRpcHttp");
});
};
it('"hasHttpTrigger" is true for http', () => {
let metadata: rpc.IRpcFunctionMetadata = {
bindings: {
req: {
type: "httpTrigger",
direction: 0,
dataType: 1
}
}
};
let funcInfo = new FunctionInfo(metadata);
expect(funcInfo.getReturnBinding()).to.be.undefined;
expect(funcInfo.hasHttpTrigger).to.be.true;
});
const funcInfo = new FunctionInfo(metadata);
expect(funcInfo.getReturnBinding().converter.name).to.equal('toRpcHttp');
});
it('gets $return output binding converter for TypedData', () => {
let metadata: rpc.IRpcFunctionMetadata = {
bindings: {
input: {
type: "queue",
direction: 0,
dataType: 1
it('"hasHttpTrigger" is true for http', () => {
const metadata: rpc.IRpcFunctionMetadata = {
bindings: {
req: {
type: 'httpTrigger',
direction: 0,
dataType: 1,
},
},
$return: {
type: "queue",
direction: 1,
dataType: 1
}
}
};
let funcInfo = new FunctionInfo(metadata);
expect(funcInfo.getReturnBinding().converter.name).to.equal("toTypedData");
});
})
};
const funcInfo = new FunctionInfo(metadata);
expect(funcInfo.getReturnBinding()).to.be.undefined;
expect(funcInfo.hasHttpTrigger).to.be.true;
});
it('gets $return output binding converter for TypedData', () => {
const metadata: rpc.IRpcFunctionMetadata = {
bindings: {
input: {
type: 'queue',
direction: 0,
dataType: 1,
},
$return: {
type: 'queue',
direction: 1,
dataType: 1,
},
},
};
const funcInfo = new FunctionInfo(metadata);
expect(funcInfo.getReturnBinding().converter.name).to.equal('toTypedData');
});
});

Просмотреть файл

@ -1,119 +1,127 @@
import { WorkerChannel } from '../src/WorkerChannel';
import { FunctionLoader } from '../src/FunctionLoader';
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
import 'mocha';
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
import { FunctionLoader } from '../src/FunctionLoader';
import { WorkerChannel } from '../src/WorkerChannel';
import mock = require('mock-require');
const chai = require('chai')
const expect = chai.expect
chai.use(require('chai-as-promised'))
const chai = require('chai');
const expect = chai.expect;
chai.use(require('chai-as-promised'));
describe('FunctionLoader', () => {
var channel: WorkerChannel;
var loader: FunctionLoader;
var require;
var functions;
var context, logs, bindingValues;
let channel: WorkerChannel;
let loader: FunctionLoader;
let require;
let functions;
let context, logs, bindingValues;
beforeEach(() => {
loader = new FunctionLoader();
logs = [];
bindingValues = {};
context = {
_inputs: [],
bindings: {},
log: (message) => logs.push(message),
bind: (val, cb) => {
bindingValues = val;
cb && cb(val);
}
};
});
it ('throws unable to determine function entry point', async () => {
mock('test', {});
await expect(
loader.load('functionId', <rpc.IRpcFunctionMetadata> {
scriptFile: 'test'
})).to.be.rejectedWith("Unable to determine function entry point. If multiple functions are exported, you must indicate the entry point, either by naming it 'run' or 'index', or by naming it explicitly via the 'entryPoint' metadata property.");
});
it ('does not load proxy function', async () => {
mock('test', {});
await loader.load('functionId', <rpc.IRpcFunctionMetadata> {
isProxy: true
});
expect(() => {
loader.getFunc('functionId')
}).to.throw("Function code for 'functionId' is not loaded and cannot be invoked.");
});
it ('throws unable to determine function entry point with entryPoint name', async () => {
mock('test', { test: {} });
let entryPoint = 'wrongEntryPoint'
await expect(
loader.load('functionId', <rpc.IRpcFunctionMetadata> {
scriptFile: 'test',
entryPoint: entryPoint
})).to.be.rejectedWith(`Unable to determine function entry point: ${entryPoint}. If multiple functions are exported, you must indicate the entry point, either by naming it 'run' or 'index', or by naming it explicitly via the 'entryPoint' metadata property.`);
});
it ('throws the resolved entry point is not a function', async () => {
mock('test', { test: {} });
let entryPoint = 'test'
await expect(
loader.load('functionId', <rpc.IRpcFunctionMetadata> {
scriptFile: 'test',
entryPoint: entryPoint
})).to.be.rejectedWith("The resolved entry point is not a function and cannot be invoked by the functions runtime. Make sure the function has been correctly exported.");
});
it ('allows use of \'this\' in loaded user function', async () => {
var FuncObject = /** @class */ (function () {
function FuncObject(this: any) {
this.prop = true;
}
FuncObject.prototype.index = function (ctx) {
ctx.bindings.prop = this.test();
ctx.done();
};
FuncObject.prototype.test = function () {
return this.prop;
};
return FuncObject;
}());
mock('test', new FuncObject());
await loader.load('functionId', <rpc.IRpcFunctionMetadata> {
scriptFile: 'test',
entryPoint: 'test'
beforeEach(() => {
loader = new FunctionLoader();
logs = [];
bindingValues = {};
context = {
_inputs: [],
bindings: {},
log: (message) => logs.push(message),
bind: (val, cb) => {
bindingValues = val;
cb && cb(val);
},
};
});
var userFunction = loader.getFunc('functionId');
userFunction(context, (results) => {
expect(results).to.eql({ prop: true });
});
});
it ('allows to return a promise from async user function', async () => {
mock('test', { test: async () => {} });
await loader.load('functionId', <rpc.IRpcFunctionMetadata> {
scriptFile: 'test',
entryPoint: 'test'
it('throws unable to determine function entry point', async () => {
mock('test', {});
await expect(
loader.load('functionId', <rpc.IRpcFunctionMetadata>{
scriptFile: 'test',
})
).to.be.rejectedWith(
"Unable to determine function entry point. If multiple functions are exported, you must indicate the entry point, either by naming it 'run' or 'index', or by naming it explicitly via the 'entryPoint' metadata property."
);
});
var userFunction = loader.getFunc('functionId');
var result = userFunction();
it('does not load proxy function', async () => {
mock('test', {});
await loader.load('functionId', <rpc.IRpcFunctionMetadata>{
isProxy: true,
});
expect(result).to.be.not.an('undefined');
expect(result.then).to.be.a('function');
});
expect(() => {
loader.getFunc('functionId');
}).to.throw("Function code for 'functionId' is not loaded and cannot be invoked.");
});
afterEach(() => {
mock.stopAll()
});
})
it('throws unable to determine function entry point with entryPoint name', async () => {
mock('test', { test: {} });
const entryPoint = 'wrongEntryPoint';
await expect(
loader.load('functionId', <rpc.IRpcFunctionMetadata>{
scriptFile: 'test',
entryPoint: entryPoint,
})
).to.be.rejectedWith(
`Unable to determine function entry point: ${entryPoint}. If multiple functions are exported, you must indicate the entry point, either by naming it 'run' or 'index', or by naming it explicitly via the 'entryPoint' metadata property.`
);
});
it('throws the resolved entry point is not a function', async () => {
mock('test', { test: {} });
const entryPoint = 'test';
await expect(
loader.load('functionId', <rpc.IRpcFunctionMetadata>{
scriptFile: 'test',
entryPoint: entryPoint,
})
).to.be.rejectedWith(
'The resolved entry point is not a function and cannot be invoked by the functions runtime. Make sure the function has been correctly exported.'
);
});
it("allows use of 'this' in loaded user function", async () => {
const FuncObject = /** @class */ (function () {
function FuncObject(this: any) {
this.prop = true;
}
FuncObject.prototype.index = function (ctx) {
ctx.bindings.prop = this.test();
ctx.done();
};
FuncObject.prototype.test = function () {
return this.prop;
};
return FuncObject;
})();
mock('test', new FuncObject());
await loader.load('functionId', <rpc.IRpcFunctionMetadata>{
scriptFile: 'test',
entryPoint: 'test',
});
const userFunction = loader.getFunc('functionId');
userFunction(context, (results) => {
expect(results).to.eql({ prop: true });
});
});
it('allows to return a promise from async user function', async () => {
mock('test', { test: async () => {} });
await loader.load('functionId', <rpc.IRpcFunctionMetadata>{
scriptFile: 'test',
entryPoint: 'test',
});
const userFunction = loader.getFunc('functionId');
const result = userFunction();
expect(result).to.be.not.an('undefined');
expect(result.then).to.be.a('function');
});
afterEach(() => {
mock.stopAll();
});
});

Просмотреть файл

@ -1,165 +1,171 @@
import { toNullableBool, toNullableString, toNullableDouble, toNullableTimestamp, fromRpcTraceContext } from '../src/converters';
import { expect } from 'chai';
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
import 'mocha';
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
import {
fromRpcTraceContext,
toNullableBool,
toNullableDouble,
toNullableString,
toNullableTimestamp,
} from '../src/converters';
describe('Rpc Converters', () => {
/** NullableBool */
it('converts true to NullableBool', () => {
let nullable = toNullableBool(true, "test");
expect(nullable && nullable.value).to.equal(true);
});
it('converts false to NullableBool', () => {
let nullable = toNullableBool(false, "test");
expect(nullable && nullable.value).to.equal(false);
});
it('throws and does not converts string to NullableBool', () => {
expect(() => {
toNullableBool(<any>"true", "test");
}).to.throw("A 'boolean' type was expected instead of a 'string' type. Cannot parse value of 'test'.")
});
it('Converts IRpcTraceContext to tracecontext', () => {
let traceparentvalue = "tracep";
let tracestatevalue = "traces";
let attributesvalue = {"traceparent": "traceparent", "tracestate": "tracestate"};
let input = <rpc.IRpcTraceContext>({
traceParent: traceparentvalue,
traceState: tracestatevalue,
attributes: attributesvalue
/** NullableBool */
it('converts true to NullableBool', () => {
const nullable = toNullableBool(true, 'test');
expect(nullable && nullable.value).to.equal(true);
});
let traceContext = fromRpcTraceContext(input);
expect(traceparentvalue).to.equal(traceContext.traceparent);
expect(tracestatevalue).to.equal(traceContext.tracestate);
expect(attributesvalue).to.equal(traceContext.attributes);
});
it('converts false to NullableBool', () => {
const nullable = toNullableBool(false, 'test');
expect(nullable && nullable.value).to.equal(false);
});
it('Converts null traceContext to empty values', () => {
let traceContext = fromRpcTraceContext(null);
expect(traceContext.traceparent).to.be.undefined;
expect(traceContext.tracestate).to.be.undefined;
expect(traceContext.attributes).to.be.undefined;
});
it('throws and does not converts string to NullableBool', () => {
expect(() => {
toNullableBool(<any>'true', 'test');
}).to.throw("A 'boolean' type was expected instead of a 'string' type. Cannot parse value of 'test'.");
});
it('Converts undefined traceContext to empty values', () => {
let traceContext = fromRpcTraceContext(undefined);
expect(traceContext.traceparent).to.be.undefined;
expect(traceContext.tracestate).to.be.undefined;
expect(traceContext.attributes).to.be.undefined;
});
it('Converts IRpcTraceContext to tracecontext', () => {
const traceparentvalue = 'tracep';
const tracestatevalue = 'traces';
const attributesvalue = { traceparent: 'traceparent', tracestate: 'tracestate' };
it('does not converts null to NullableBool', () => {
let nullable = toNullableBool(<any>null, "test");
expect(nullable && nullable.value).to.be.undefined;
});
const input = <rpc.IRpcTraceContext>{
traceParent: traceparentvalue,
traceState: tracestatevalue,
attributes: attributesvalue,
};
/** NullableString */
it('converts string to NullableString', () => {
let input = "hello";
let nullable = toNullableString(input, "test");
expect(nullable && nullable.value).to.equal(input);
});
const traceContext = fromRpcTraceContext(input);
it('converts empty string to NullableString', () => {
let input = "";
let nullable = toNullableString(input, "test");
expect(nullable && nullable.value).to.equal(input);
});
expect(traceparentvalue).to.equal(traceContext.traceparent);
expect(tracestatevalue).to.equal(traceContext.tracestate);
expect(attributesvalue).to.equal(traceContext.attributes);
});
it('throws and does not convert number to NullableString', () => {
expect(() => {
toNullableString(<any>123, "test");
}).to.throw("A 'string' type was expected instead of a 'number' type. Cannot parse value of 'test'.");
});
it('Converts null traceContext to empty values', () => {
const traceContext = fromRpcTraceContext(null);
expect(traceContext.traceparent).to.be.undefined;
expect(traceContext.tracestate).to.be.undefined;
expect(traceContext.attributes).to.be.undefined;
});
it('does not convert null to NullableString', () => {
let nullable = toNullableString(<any>null, "test");
expect(nullable && nullable.value).to.be.undefined;
});
it('Converts undefined traceContext to empty values', () => {
const traceContext = fromRpcTraceContext(undefined);
expect(traceContext.traceparent).to.be.undefined;
expect(traceContext.tracestate).to.be.undefined;
expect(traceContext.attributes).to.be.undefined;
});
/** NullableDouble */
it('converts number to NullableDouble', () => {
let input = 1234567;
let nullable = toNullableDouble(input, "test");
expect(nullable && nullable.value).to.equal(input);
});
it('does not converts null to NullableBool', () => {
const nullable = toNullableBool(<any>null, 'test');
expect(nullable && nullable.value).to.be.undefined;
});
it('converts 0 to NullableDouble', () => {
let input = 0;
let nullable = toNullableDouble(input, "test");
expect(nullable && nullable.value).to.equal(input);
});
/** NullableString */
it('converts string to NullableString', () => {
const input = 'hello';
const nullable = toNullableString(input, 'test');
expect(nullable && nullable.value).to.equal(input);
});
it('converts negative number to NullableDouble', () => {
let input = -11234567;
let nullable = toNullableDouble(input, "test");
expect(nullable && nullable.value).to.equal(input);
});
it('converts empty string to NullableString', () => {
const input = '';
const nullable = toNullableString(input, 'test');
expect(nullable && nullable.value).to.equal(input);
});
it('converts numeric string to NullableDouble', () => {
let input = "1234567";
let nullable = toNullableDouble(input, "test");
expect(nullable && nullable.value).to.equal(1234567);
});
it('throws and does not convert number to NullableString', () => {
expect(() => {
toNullableString(<any>123, 'test');
}).to.throw("A 'string' type was expected instead of a 'number' type. Cannot parse value of 'test'.");
});
it('converts float string to NullableDouble', () => {
let input = "1234567.002";
let nullable = toNullableDouble(input, "test");
expect(nullable && nullable.value).to.equal(1234567.002);
});
it('does not convert null to NullableString', () => {
const nullable = toNullableString(<any>null, 'test');
expect(nullable && nullable.value).to.be.undefined;
});
it('throws and does not convert non-number string to NullableDouble', () => {
expect(() => {
toNullableDouble(<any>"123hellohello!!111", "test");
}).to.throw("A 'number' type was expected instead of a 'string' type. Cannot parse value of 'test'.");
});
/** NullableDouble */
it('converts number to NullableDouble', () => {
const input = 1234567;
const nullable = toNullableDouble(input, 'test');
expect(nullable && nullable.value).to.equal(input);
});
it('does not convert undefined to NullableDouble', () => {
let nullable = toNullableDouble(undefined, "test");
expect(nullable && nullable.value).to.be.undefined;
});
it('converts 0 to NullableDouble', () => {
const input = 0;
const nullable = toNullableDouble(input, 'test');
expect(nullable && nullable.value).to.equal(input);
});
/** NullableTimestamp */
it('converts Date to NullableTimestamp', () => {
let input = new Date("1/2/2014")
let nullable = toNullableTimestamp(input, "test");
let secondInput = Math.round((<any>input).getTime() / 1000);
expect(nullable && nullable.value && nullable.value.seconds).to.equal(secondInput);
});
it('converts negative number to NullableDouble', () => {
const input = -11234567;
const nullable = toNullableDouble(input, 'test');
expect(nullable && nullable.value).to.equal(input);
});
it('converts Date.now to NullableTimestamp', () => {
let input = Date.now();
let nullable = toNullableTimestamp(input, "test");
let secondInput = Math.round(input / 1000);
expect(nullable && nullable.value && nullable.value.seconds).to.equal(secondInput);
});
it('converts numeric string to NullableDouble', () => {
const input = '1234567';
const nullable = toNullableDouble(input, 'test');
expect(nullable && nullable.value).to.equal(1234567);
});
it('converts milliseconds to NullableTimestamp', () => {
let input = Date.now();
let nullable = toNullableTimestamp(input, "test");
let secondInput = Math.round(input / 1000);
expect(nullable && nullable.value && nullable.value.seconds).to.equal(secondInput);
});
it('converts float string to NullableDouble', () => {
const input = '1234567.002';
const nullable = toNullableDouble(input, 'test');
expect(nullable && nullable.value).to.equal(1234567.002);
});
it('does not convert string to NullableTimestamp', () => {
expect(() => {
toNullableTimestamp(<any>"1/2/3 2014", "test");
}).to.throw("A 'number' or 'Date' input was expected instead of a 'string'. Cannot parse value of 'test'.");
});
it('throws and does not convert non-number string to NullableDouble', () => {
expect(() => {
toNullableDouble(<any>'123hellohello!!111', 'test');
}).to.throw("A 'number' type was expected instead of a 'string' type. Cannot parse value of 'test'.");
});
it('does not convert object to NullableTimestamp', () => {
expect(() => {
toNullableTimestamp(<any>{ time: 100 }, "test");
}).to.throw("A 'number' or 'Date' input was expected instead of a 'object'. Cannot parse value of 'test'.");
});
it('does not convert undefined to NullableDouble', () => {
const nullable = toNullableDouble(undefined, 'test');
expect(nullable && nullable.value).to.be.undefined;
});
it('does not convert undefined to NullableTimestamp', () => {
let nullable = toNullableTimestamp(undefined, "test");
expect(nullable && nullable.value).to.be.undefined;
});
})
/** NullableTimestamp */
it('converts Date to NullableTimestamp', () => {
const input = new Date('1/2/2014');
const nullable = toNullableTimestamp(input, 'test');
const secondInput = Math.round((<any>input).getTime() / 1000);
expect(nullable && nullable.value && nullable.value.seconds).to.equal(secondInput);
});
it('converts Date.now to NullableTimestamp', () => {
const input = Date.now();
const nullable = toNullableTimestamp(input, 'test');
const secondInput = Math.round(input / 1000);
expect(nullable && nullable.value && nullable.value.seconds).to.equal(secondInput);
});
it('converts milliseconds to NullableTimestamp', () => {
const input = Date.now();
const nullable = toNullableTimestamp(input, 'test');
const secondInput = Math.round(input / 1000);
expect(nullable && nullable.value && nullable.value.seconds).to.equal(secondInput);
});
it('does not convert string to NullableTimestamp', () => {
expect(() => {
toNullableTimestamp(<any>'1/2/3 2014', 'test');
}).to.throw("A 'number' or 'Date' input was expected instead of a 'string'. Cannot parse value of 'test'.");
});
it('does not convert object to NullableTimestamp', () => {
expect(() => {
toNullableTimestamp(<any>{ time: 100 }, 'test');
}).to.throw("A 'number' or 'Date' input was expected instead of a 'object'. Cannot parse value of 'test'.");
});
it('does not convert undefined to NullableTimestamp', () => {
const nullable = toNullableTimestamp(undefined, 'test');
expect(nullable && nullable.value).to.be.undefined;
});
});

Просмотреть файл

@ -1,127 +1,128 @@
import { toRpcHttpCookieList, toRpcHttp } from '../src/converters';
import { Cookie } from "@azure/functions";
import { Cookie } from '@azure/functions';
import { expect } from 'chai';
import * as sinon from 'sinon';
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
import 'mocha';
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
import { toRpcHttp, toRpcHttpCookieList } from '../src/converters';
describe('Rpc Converters', () => {
/** NullableBool */
it('converts http cookies', () => {
let cookieInputs =
[
{
name: "mycookie",
value: "myvalue",
maxAge: 200000
},
{
name: "mycookie2",
value: "myvalue2",
path: "/",
maxAge: "200000"
},
{
name: "mycookie3-expires",
value: "myvalue3-expires",
expires: new Date('December 17, 1995 03:24:00 PST')
}
];
let rpcCookies = toRpcHttpCookieList(<Cookie[]>cookieInputs);
expect(rpcCookies[0].name).to.equal("mycookie");
expect(rpcCookies[0].value).to.equal("myvalue");
expect((<any>rpcCookies[0].maxAge).value).to.equal(200000);
/** NullableBool */
it('converts http cookies', () => {
const cookieInputs = [
{
name: 'mycookie',
value: 'myvalue',
maxAge: 200000,
},
{
name: 'mycookie2',
value: 'myvalue2',
path: '/',
maxAge: '200000',
},
{
name: 'mycookie3-expires',
value: 'myvalue3-expires',
expires: new Date('December 17, 1995 03:24:00 PST'),
},
];
expect(rpcCookies[1].name).to.equal("mycookie2");
expect(rpcCookies[1].value).to.equal("myvalue2");
expect((<any>rpcCookies[1].path).value).to.equal("/");
expect((<any>rpcCookies[1].maxAge).value).to.equal(200000);
const rpcCookies = toRpcHttpCookieList(<Cookie[]>cookieInputs);
expect(rpcCookies[0].name).to.equal('mycookie');
expect(rpcCookies[0].value).to.equal('myvalue');
expect((<any>rpcCookies[0].maxAge).value).to.equal(200000);
expect(rpcCookies[2].name).to.equal("mycookie3-expires");
expect(rpcCookies[2].value).to.equal("myvalue3-expires");
expect((<any>rpcCookies[2].expires).value.seconds).to.equal(819199440);
});
expect(rpcCookies[1].name).to.equal('mycookie2');
expect(rpcCookies[1].value).to.equal('myvalue2');
expect((<any>rpcCookies[1].path).value).to.equal('/');
expect((<any>rpcCookies[1].maxAge).value).to.equal(200000);
it('converts http cookie SameSite', () => {
let cookieInputs: Cookie[] =
[
{
name: "none-cookie",
value: "myvalue",
sameSite: "None"
},
{
name: "lax-cookie",
value: "myvalue",
sameSite: "Lax"
},
{
name: "strict-cookie",
value: "myvalue",
sameSite: "Strict"
},
{
name: "default-cookie",
value: "myvalue"
}
];
let rpcCookies = toRpcHttpCookieList(<Cookie[]>cookieInputs);
expect(rpcCookies[0].name).to.equal("none-cookie");
expect(rpcCookies[0].sameSite).to.equal(rpc.RpcHttpCookie.SameSite.ExplicitNone);
expect(rpcCookies[2].name).to.equal('mycookie3-expires');
expect(rpcCookies[2].value).to.equal('myvalue3-expires');
expect((<any>rpcCookies[2].expires).value.seconds).to.equal(819199440);
});
expect(rpcCookies[1].name).to.equal("lax-cookie");
expect(rpcCookies[1].sameSite).to.equal(rpc.RpcHttpCookie.SameSite.Lax);
it('converts http cookie SameSite', () => {
const cookieInputs: Cookie[] = [
{
name: 'none-cookie',
value: 'myvalue',
sameSite: 'None',
},
{
name: 'lax-cookie',
value: 'myvalue',
sameSite: 'Lax',
},
{
name: 'strict-cookie',
value: 'myvalue',
sameSite: 'Strict',
},
{
name: 'default-cookie',
value: 'myvalue',
},
];
expect(rpcCookies[2].name).to.equal("strict-cookie");
expect(rpcCookies[2].sameSite).to.equal(rpc.RpcHttpCookie.SameSite.Strict);
const rpcCookies = toRpcHttpCookieList(<Cookie[]>cookieInputs);
expect(rpcCookies[0].name).to.equal('none-cookie');
expect(rpcCookies[0].sameSite).to.equal(rpc.RpcHttpCookie.SameSite.ExplicitNone);
expect(rpcCookies[3].name).to.equal("default-cookie");
expect(rpcCookies[3].sameSite).to.equal(rpc.RpcHttpCookie.SameSite.None);
});
expect(rpcCookies[1].name).to.equal('lax-cookie');
expect(rpcCookies[1].sameSite).to.equal(rpc.RpcHttpCookie.SameSite.Lax);
it('throws on invalid input', () => {
expect(() => {
let cookieInputs = [
expect(rpcCookies[2].name).to.equal('strict-cookie');
expect(rpcCookies[2].sameSite).to.equal(rpc.RpcHttpCookie.SameSite.Strict);
expect(rpcCookies[3].name).to.equal('default-cookie');
expect(rpcCookies[3].sameSite).to.equal(rpc.RpcHttpCookie.SameSite.None);
});
it('throws on invalid input', () => {
expect(() => {
const cookieInputs = [
{
name: 123,
value: "myvalue",
maxAge: 200000
value: 'myvalue',
maxAge: 200000,
},
{
name: "mycookie2",
value: "myvalue2",
path: "/",
maxAge: "200000"
name: 'mycookie2',
value: 'myvalue2',
path: '/',
maxAge: '200000',
},
{
name: "mycookie3-expires",
value: "myvalue3-expires",
expires: new Date('December 17, 1995 03:24:00')
name: 'mycookie3-expires',
value: 'myvalue3-expires',
expires: new Date('December 17, 1995 03:24:00'),
},
{
name: "mycookie3-expires",
value: "myvalue3-expires",
expires: new Date("")
}
name: 'mycookie3-expires',
value: 'myvalue3-expires',
expires: new Date(''),
},
];
toRpcHttpCookieList(<Cookie[]>cookieInputs);
}).to.throw("");
});
it('throws on array as http response', () => {
expect(() => {
let response = ["one", 2, "3"];
toRpcHttp(response);
}).to.throw("The HTTP response must be an 'object' type that can include properties such as 'body', 'status', and 'headers'. Learn more: https://go.microsoft.com/fwlink/?linkid=2112563");
});
toRpcHttpCookieList(<Cookie[]>cookieInputs);
}).to.throw('');
});
it('throws on string as http response', () => {
expect(() => {
let response = "My output string";
toRpcHttp(response);
}).to.throw("The HTTP response must be an 'object' type that can include properties such as 'body', 'status', and 'headers'. Learn more: https://go.microsoft.com/fwlink/?linkid=2112563");
});
})
it('throws on array as http response', () => {
expect(() => {
const response = ['one', 2, '3'];
toRpcHttp(response);
}).to.throw(
"The HTTP response must be an 'object' type that can include properties such as 'body', 'status', and 'headers'. Learn more: https://go.microsoft.com/fwlink/?linkid=2112563"
);
});
it('throws on string as http response', () => {
expect(() => {
const response = 'My output string';
toRpcHttp(response);
}).to.throw(
"The HTTP response must be an 'object' type that can include properties such as 'body', 'status', and 'headers'. Learn more: https://go.microsoft.com/fwlink/?linkid=2112563"
);
});
});

Просмотреть файл

@ -1,20 +1,20 @@
import { IEventStream } from '../src/GrpcService';
import { EventEmitter } from 'events';
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
import * as sinon from 'sinon';
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
import { IEventStream } from '../src/GrpcService';
export class TestEventStream extends EventEmitter implements IEventStream {
written: sinon.SinonSpy;
constructor() {
super();
this.written = sinon.spy();
}
write(message: rpc.IStreamingMessage) {
this.written(message);
}
end(): void { }
written: sinon.SinonSpy;
constructor() {
super();
this.written = sinon.spy();
}
write(message: rpc.IStreamingMessage) {
this.written(message);
}
end(): void {}
addTestMessage(msg: rpc.IStreamingMessage) {
this.emit('data', rpc.StreamingMessage.create(msg));
}
}
addTestMessage(msg: rpc.IStreamingMessage) {
this.emit('data', rpc.StreamingMessage.create(msg));
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,15 +1,45 @@
import { startNodeWorker } from '../src/Worker';
import { expect } from 'chai';
import 'mocha';
import { startNodeWorker } from '../src/Worker';
describe('Worker', () => {
it('throws error on incorrect args: grpcMaxMessageLength 0', () => {
var args = ["/node", "nodejsWorker.js", "--host", "120.0.0.0", "--port" ,"8000", "--workerId", "bd2e3e80-46ba", "--requestId", "bd2e3e80-46ba", "--grpcMaxMessageLength", "0"];
expect(() => { startNodeWorker(args) }).to.throw("gRPC client connection info is missing or incorrect ('grpcMaxMessageLength' is 0).");
});
it('throws error on incorrect args: grpcMaxMessageLength 0', () => {
const args = [
'/node',
'nodejsWorker.js',
'--host',
'120.0.0.0',
'--port',
'8000',
'--workerId',
'bd2e3e80-46ba',
'--requestId',
'bd2e3e80-46ba',
'--grpcMaxMessageLength',
'0',
];
expect(() => {
startNodeWorker(args);
}).to.throw("gRPC client connection info is missing or incorrect ('grpcMaxMessageLength' is 0).");
});
it('throws error on incorrect args: grpcMaxMessageLength 0 and null requestId', () => {
var args = ["/node", "nodejsWorker.js", "--host", "120.0.0.0", "--port" ,"8000", "--workerId", "bd2e3e80-46ba", "--grpcMaxMessageLength", "0"];
expect(() => { startNodeWorker(args) }).to.throw("gRPC client connection info is missing or incorrect ('requestId' is undefined, 'grpcMaxMessageLength' is 0).");
});
})
it('throws error on incorrect args: grpcMaxMessageLength 0 and null requestId', () => {
const args = [
'/node',
'nodejsWorker.js',
'--host',
'120.0.0.0',
'--port',
'8000',
'--workerId',
'bd2e3e80-46ba',
'--grpcMaxMessageLength',
'0',
];
expect(() => {
startNodeWorker(args);
}).to.throw(
"gRPC client connection info is missing or incorrect ('requestId' is undefined, 'grpcMaxMessageLength' is 0)."
);
});
});

Просмотреть файл

@ -1,7 +1,7 @@
// This file will be compiled by multiple versions of TypeScript as decribed in ./test/TypesTests.ts to verify there are no errors
import { AzureFunction, Context, HttpRequest, HttpMethod, Cookie } from "@azure/functions";
const get: HttpMethod = "GET";
import { AzureFunction, Context, Cookie, HttpMethod, HttpRequest } from '@azure/functions';
const get: HttpMethod = 'GET';
const runHttp: AzureFunction = async function (context: Context, req: HttpRequest) {
if (req.method === get) {
@ -11,16 +11,16 @@ const runHttp: AzureFunction = async function (context: Context, req: HttpReques
context.log('JavaScript HTTP trigger function processed a request.');
if (req.query.name || (req.body && req.body.name)) {
context.res = {
status: "200",
body: "Hello " + (req.query.name || req.body.name)
status: '200',
body: 'Hello ' + (req.query.name || req.body.name),
};
} else {
context.res = {
status: 400,
body: "Please pass a name on the query string or in the request body"
body: 'Please pass a name on the query string or in the request body',
};
}
}
};
const runServiceBus: AzureFunction = function (context: Context, myQueueItem: string) {
context.log('Node.js ServiceBus queue trigger function processed message', myQueueItem);
@ -35,46 +35,46 @@ const runHttpReturn: AzureFunction = async function (context: Context, req: Http
context.log('JavaScript HTTP trigger function processed a request.');
if (req.query.name || (req.body && req.body.name)) {
return {
status: "200",
body: "Hello " + (req.query.name || req.body.name)
status: '200',
body: 'Hello ' + (req.query.name || req.body.name),
};
} else {
return {
status: 400,
body: "Please pass a name on the query string or in the request body"
body: 'Please pass a name on the query string or in the request body',
};
}
}
};
const runFunction: AzureFunction = async function (context: Context) {
context.log("Ran function");
return "Ran function";
}
context.log('Ran function');
return 'Ran function';
};
const cookieFunction: AzureFunction = async function (context: Context) {
let cookies: Cookie[] = [
const cookies: Cookie[] = [
{
name: "cookiename",
value: "cookievalue",
expires: Date.now()
}
name: 'cookiename',
value: 'cookievalue',
expires: Date.now(),
},
];
context.res = {
cookies,
body: "just a normal body"
body: 'just a normal body',
};
}
};
const runHttpWithQueue: AzureFunction = async function (context: Context, req: HttpRequest, queueItem: Buffer) {
context.log("Http-triggered function with " + req.method + " method.");
context.log("Pulling in queue item " + queueItem);
context.log('Http-triggered function with ' + req.method + ' method.');
context.log('Pulling in queue item ' + queueItem);
return;
}
};
const returnWithContextDone: AzureFunction = function (context: Context, req: HttpRequest) {
context.log.info("Writing to queue");
context.log.info('Writing to queue');
context.done(null, { myOutput: { text: 'hello there, world', noNumber: true } });
}
};
export { runHttp, cookieFunction, runHttpReturn, runServiceBus, runFunction, runHttpWithQueue, returnWithContextDone };
@ -87,15 +87,15 @@ export const runTypedReturn: AzureFunction = async (context, request: HttpReques
// value1: "Test1"
// };
return {
value: "Test"
value: 'Test',
};
}
};
export const runTypedReturn1: AzureFunction = async (context, request): Promise<CustomOutput> => {
// return { // ts(2322) error
// value1: "Test1"
// };
return {
value: "Test"
value: 'Test',
};
}
};