-
Notifications
You must be signed in to change notification settings - Fork 474
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added automated end to end test to verify deployed components
* .gitignore Updated to ignore node modules * iothubs.bicep updated with lower default sku * README.md updated with "Automated End-to-End Test" commands * e2e/ NodeJs end to end test added * test/ Data files updated with more appropriate deviceId
- Loading branch information
Showing
10 changed files
with
2,207 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
output | ||
testResultSummary.json | ||
e2e/node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import * as chai from 'chai'; | ||
import { Mqtt } from 'azure-iot-device-mqtt'; | ||
import { Client, Message } from 'azure-iot-device'; | ||
import { BlobItem, ContainerClient } from '@azure/storage-blob'; | ||
chai.use(require('chai-subset')); | ||
|
||
const EVENT_SINK_CONTAINER = 'bloboutput'; | ||
const EXPECTED_LATENCY_MS = 7200; | ||
|
||
describe('Send to IoT Hub', () => { | ||
let iotClient: Client; | ||
let containerClient: ContainerClient; | ||
|
||
before(() => { | ||
chai.expect(process.env.DEVICE_CONNECTION_STRING).to.be.not.empty; | ||
chai.expect(process.env.AZURE_STORAGE_CONNECTION_STRING).to.be.not.empty; | ||
}); | ||
|
||
before(async () => { | ||
iotClient = Client.fromConnectionString(process.env.DEVICE_CONNECTION_STRING, Mqtt); | ||
await iotClient.open(); | ||
}); | ||
|
||
before(() => { | ||
containerClient = new ContainerClient(process.env.AZURE_STORAGE_CONNECTION_STRING, EVENT_SINK_CONTAINER); | ||
}); | ||
|
||
before(async () => { | ||
await deleteAllBlobs(); | ||
}); | ||
|
||
after(async function () { | ||
await iotClient.close(); | ||
}); | ||
|
||
afterEach(async () => { | ||
await deleteAllBlobs(); | ||
}); | ||
|
||
async function send(message: Message) { | ||
await iotClient.sendEvent(message); | ||
}; | ||
|
||
async function deleteAllBlobs() { | ||
for await (const blob of containerClient.listBlobsFlat()) { | ||
await containerClient.deleteBlob(blob.name); | ||
} | ||
} | ||
|
||
async function getFirstBlob(): Promise<BlobItem> { | ||
for await (const blob of containerClient.listBlobsFlat()) { | ||
return blob; | ||
} | ||
} | ||
|
||
async function getBlobData(blob: BlobItem): Promise<string> { | ||
const client = containerClient.getBlockBlobClient(blob.name); | ||
const response = await client.download(); | ||
return (await streamToBuffer(response.readableStreamBody!)).toString(); | ||
} | ||
|
||
function convertBlobData(blobData: string): any[] { | ||
return blobData.split('\n').map(entry => JSON.parse(entry)); | ||
} | ||
|
||
// A helper method used to read a Node.js readable stream into a Buffer | ||
async function streamToBuffer(readableStream: NodeJS.ReadableStream): Promise<Buffer> { | ||
return new Promise((resolve, reject) => { | ||
const chunks: Buffer[] = []; | ||
readableStream.on("data", (data: Buffer | string) => { | ||
chunks.push(data instanceof Buffer ? data : Buffer.from(data)); | ||
}); | ||
readableStream.on("end", () => { | ||
resolve(Buffer.concat(chunks)); | ||
}); | ||
readableStream.on("error", reject); | ||
}); | ||
} | ||
|
||
async function delay(factor = 1) { | ||
await new Promise(resolve => setTimeout(resolve, EXPECTED_LATENCY_MS * factor)); | ||
} | ||
|
||
describe('payload with temperature', () => { | ||
describe('greater than 27 degrees', () => { | ||
it('should contain entry in blob', async () => { | ||
const data = { | ||
deviceId: 'modern-data-warehouse-dataops/single_tech_samples/streamanalytics/e2e', | ||
temperature: 27.1 | ||
}; | ||
const message = new Message(JSON.stringify(data)); | ||
|
||
await send(message); | ||
|
||
await delay(); | ||
|
||
const blob = await getFirstBlob(); | ||
chai.expect(blob).to.not.be.undefined; | ||
const blobData = await getBlobData(blob); | ||
const entries = convertBlobData(blobData); | ||
chai.expect(entries).to.have.length(1); | ||
chai.expect(entries).to.containSubset([data]); | ||
}); | ||
}); | ||
|
||
describe('equal to 27 degrees', () => { | ||
it('should not contain entry in blob', async () => { | ||
const data = { | ||
deviceId: 'modern-data-warehouse-dataops/single_tech_samples/streamanalytics/e2e', | ||
temperature: 27 | ||
}; | ||
const message = new Message(JSON.stringify(data)); | ||
|
||
await send(message); | ||
|
||
await delay(2); | ||
|
||
const blob = await getFirstBlob(); | ||
chai.expect(blob).to.be.undefined; | ||
}); | ||
}); | ||
|
||
describe('less than 27 degrees', () => { | ||
it('should not contain entry in blob', async () => { | ||
const data = { | ||
deviceId: 'modern-data-warehouse-dataops/single_tech_samples/streamanalytics/e2e', | ||
temperature: 26.9 | ||
}; | ||
const message = new Message(JSON.stringify(data)); | ||
|
||
await send(message); | ||
|
||
await delay(2); | ||
|
||
const blob = await getFirstBlob(); | ||
chai.expect(blob).to.be.undefined; | ||
}); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.