Skip to content
This repository has been archived by the owner on Oct 11, 2023. It is now read-only.

Commit

Permalink
Add device model scripts endpoint (#231)
Browse files Browse the repository at this point in the history
* Add device model scripts endpoint
  • Loading branch information
maple10001 authored and hathind-ms committed Aug 27, 2018
1 parent 4462cd4 commit b8dab03
Show file tree
Hide file tree
Showing 28 changed files with 1,477 additions and 110 deletions.
313 changes: 313 additions & 0 deletions Services.Test/DeviceModelScriptsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Diagnostics;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Exceptions;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Models;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.StorageAdapter;
using Moq;
using Newtonsoft.Json;
using Services.Test.helpers;
using Xunit;

namespace Services.Test
{
public class DeviceModelScriptsTest
{
private const string STORAGE_COLLECTION = "deviceModelScripts";
private readonly Mock<IStorageAdapterClient> storage;
private readonly Mock<ILogger> logger;
private readonly DeviceModelScripts target;

public DeviceModelScriptsTest()
{
this.storage = new Mock<IStorageAdapterClient>();
this.logger = new Mock<ILogger>();

this.target = new DeviceModelScripts(
this.storage.Object,
this.logger.Object);
}

[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void InitialListIsEmpty()
{
// Arrange
this.ThereAreNoDeviceModelScriptsInStorage();

// Act
var result = this.target.GetListAsync().Result;

// Assert
Assert.Empty(result);
}

[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ItCreatesDeviceModelScriptInStorage()
{
// Arrange
var id = Guid.NewGuid().ToString();
var eTag = Guid.NewGuid().ToString();
var deviceModelScript = new DeviceModelScript { Id = id, ETag = eTag };

this.storage
.Setup(x => x.UpdateAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.ReturnsAsync(this.BuildValueApiModel(deviceModelScript));

// Act
DeviceModelScript result = this.target.InsertAsync(deviceModelScript).Result;

// Assert
Assert.NotNull(result);
Assert.Equal(deviceModelScript.Id, result.Id);
Assert.Equal(deviceModelScript.ETag, result.ETag);

this.storage.Verify(
x => x.UpdateAsync(STORAGE_COLLECTION, deviceModelScript.Id, It.IsAny<string>(), null), Times.Once());
}

[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void DeviceModelScriptsCanBeUpserted()
{
// Arrange
var id = Guid.NewGuid().ToString();

var deviceModelScript = new DeviceModelScript { Id = id, ETag = "oldEtag" };
this.TheScriptExists(id, deviceModelScript);

var updatedSimulationScript = new DeviceModelScript { Id = id, ETag = "newETag" };
this.storage
.Setup(x => x.UpdateAsync(
STORAGE_COLLECTION,
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>()))
.ReturnsAsync(this.BuildValueApiModel(updatedSimulationScript));

// Act
this.target.UpsertAsync(deviceModelScript)
.Wait(Constants.TEST_TIMEOUT);

// Assert
this.storage.Verify(x => x.GetAsync(STORAGE_COLLECTION, id), Times.Once);
this.storage.Verify(x => x.UpdateAsync(
STORAGE_COLLECTION,
id,
It.Is<string>(json => JsonConvert.DeserializeObject<DeviceModelScript>(json).Id == id && !json.Contains("ETag")),
"oldEtag"), Times.Once());

Assert.Equal(updatedSimulationScript.Id, deviceModelScript.Id);
// The call to UpsertAsync() modifies the object, so the ETags will match
Assert.Equal(updatedSimulationScript.ETag, deviceModelScript.ETag);
}

[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ItCreatesDeviceModelScriptWhenSimulationScriptNotFoundInUpserting()
{
// Arrange
var id = Guid.NewGuid().ToString();
var deviceModelScript = new DeviceModelScript { Id = id, ETag = "Etag" };
this.TheScriptDoesntExist(id);
this.storage
.Setup(x => x.UpdateAsync(
STORAGE_COLLECTION,
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>()))
.ReturnsAsync(this.BuildValueApiModel(deviceModelScript));

// Act
this.target.UpsertAsync(deviceModelScript).Wait(TimeSpan.FromSeconds(30));

// Assert - the app uses PUT with given ID
this.storage.Verify(x => x.UpdateAsync(
STORAGE_COLLECTION,
id,
It.IsAny<string>(),
null));
}

[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ItThrowsConflictingResourceExceptionIfEtagDoesNotMatchInUpserting()
{
// Arrange
var id = Guid.NewGuid().ToString();
var deviceModelScriptInStorage = new DeviceModelScript { Id = id, ETag = "ETag" };
this.TheScriptExists(id, deviceModelScriptInStorage);

// Act & Assert
var deviceModelScript = new DeviceModelScript { Id = id, ETag = "not-matching-Etag" };
Assert.ThrowsAsync<ConflictingResourceException>(
async () => await this.target.UpsertAsync(deviceModelScript))
.Wait(Constants.TEST_TIMEOUT);
}

[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ItThrowsExceptionWhenInsertDeviceModelScriptFailed()
{
// Arrange
var deviceModelScript = new DeviceModelScript { Id = "id", ETag = "Etag" };
this.storage
.Setup(x => x.UpdateAsync(
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>()))
.ThrowsAsync(new SomeException());

// Act & Assert
Assert.ThrowsAsync<ExternalDependencyException>(
async () => await this.target.InsertAsync(deviceModelScript))
.Wait(Constants.TEST_TIMEOUT);
}

[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ItFailsToUpsertWhenUnableToFetchScriptFromStorage()
{
// Arrange
var deviceModelScript = new DeviceModelScript { Id = "id", ETag = "Etag" };
this.storage
.Setup(x => x.GetAsync(It.IsAny<string>(), It.IsAny<string>()))
.ThrowsAsync(new SomeException());

// Act & Assert
Assert.ThrowsAsync<ExternalDependencyException>(
async () => await this.target.UpsertAsync(deviceModelScript))
.Wait(Constants.TEST_TIMEOUT);
}

[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ItFailsToUpsertWhenStorageUpdateFails()
{
// Arrange
var id = Guid.NewGuid().ToString();
var deviceModelScript = new DeviceModelScript { Id = id, ETag = "Etag" };
this.TheScriptExists(id, deviceModelScript);

this.storage
.Setup(x => x.UpdateAsync(
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>()))
.ThrowsAsync(new SomeException());

// Act & Assert
Assert.ThrowsAsync<ExternalDependencyException>(
async () => await this.target.UpsertAsync(deviceModelScript))
.Wait(Constants.TEST_TIMEOUT);
}

[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ItThrowsExternalDependencyExceptionWhenFailedFetchingDeviceModelScriptInStorage()
{
// Arrange
var deviceModelScript = new DeviceModelScript { Id = "id", ETag = "Etag" };

// Act
var ex = Record.Exception(() => this.target.UpsertAsync(deviceModelScript).Result);

// Assert
Assert.IsType<ExternalDependencyException>(ex.InnerException);
}

[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ItThrowsExceptionWhenDeleteDeviceModelScriptFailed()
{
// Arrange
this.storage
.Setup(x => x.DeleteAsync(
It.IsAny<string>(),
It.IsAny<string>()))
.ThrowsAsync(new SomeException());

// Act & Assert
Assert.ThrowsAsync<ExternalDependencyException>(
async () => await this.target.DeleteAsync("someId"))
.Wait(Constants.TEST_TIMEOUT);
}

[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ItFailsToGetDeviceModelScriptsWhenStorageFails()
{
// Arrange
this.storage
.Setup(x => x.GetAsync(It.IsAny<string>(), It.IsAny<string>()))
.ThrowsAsync(new SomeException());

// Act & Assert
Assert.ThrowsAsync<ExternalDependencyException>(
async () => await this.target.GetAsync("someId"))
.Wait(Constants.TEST_TIMEOUT);
}

[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ItThrowsExceptionWhenGetListOfDeviceModelScriptsDeserializeFailed()
{
// Arrange
this.SetupAListOfInvalidDeviceModelScriptsInStorage();

// Act & Assert
Assert.ThrowsAsync<ExternalDependencyException>(
async () => await this.target.GetListAsync())
.Wait(Constants.TEST_TIMEOUT);
}

[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ItThrowsExceptionWhenGetDeviceModelScriptByInvalidId()
{
// Act & Assert
Assert.ThrowsAsync<InvalidInputException>(
async () => await this.target.GetAsync(string.Empty))
.Wait(Constants.TEST_TIMEOUT);
}

private void SetupAListOfInvalidDeviceModelScriptsInStorage()
{
var list = new ValueListApiModel();
var value = new ValueApiModel
{
Key = "key",
Data = "{ 'invalid': json",
ETag = "etag"
};
list.Items.Add(value);

this.storage
.Setup(x => x.GetAllAsync(It.IsAny<string>()))
.ReturnsAsync(list);
}

private void TheScriptDoesntExist(string id)
{
this.storage
.Setup(x => x.GetAsync(STORAGE_COLLECTION, id))
.Throws<ResourceNotFoundException>();
}

private void TheScriptExists(string id, DeviceModelScript deviceModelScript)
{
this.storage
.Setup(x => x.GetAsync(STORAGE_COLLECTION, id))
.ReturnsAsync(this.BuildValueApiModel(deviceModelScript));
}

private ValueApiModel BuildValueApiModel(DeviceModelScript deviceModelScript)
{
return new ValueApiModel
{
Key = deviceModelScript.Id,
Data = JsonConvert.SerializeObject(deviceModelScript),
ETag = deviceModelScript.ETag
};
}

private void ThereAreNoDeviceModelScriptsInStorage()
{
this.storage
.Setup(x => x.GetAllAsync(STORAGE_COLLECTION))
.ReturnsAsync(new ValueListApiModel());
}
}
}
40 changes: 24 additions & 16 deletions Services.Test/Simulation/JavascriptInterpreterTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System;
using System.Collections.Generic;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Diagnostics;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Models;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Runtime;
Expand All @@ -21,6 +22,7 @@ public class JavascriptInterpreterTest
private readonly Mock<IServicesConfig> config;
private readonly Mock<ILogger> logger;
private readonly Mock<ISmartDictionary> properties;
private readonly Mock<IDeviceModelScripts> simulationScripts;
private readonly JavascriptInterpreter target;

public JavascriptInterpreterTest(ITestOutputHelper log)
Expand All @@ -31,19 +33,25 @@ public JavascriptInterpreterTest(ITestOutputHelper log)
this.config.SetupGet(x => x.DeviceModelsFolder).Returns("./data/devicemodels/");
this.config.SetupGet(x => x.DeviceModelsScriptsFolder).Returns("./data/devicemodels/scripts/");
this.properties = new Mock<ISmartDictionary>();

this.simulationScripts = new Mock<IDeviceModelScripts>();
this.logger = new Mock<ILogger>();

this.target = new JavascriptInterpreter(this.config.Object, this.logger.Object);
this.target = new JavascriptInterpreter(
this.simulationScripts.Object,
this.config.Object,
this.logger.Object);
}

[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ReturnedStateIsIntact()
{
// Arrange
SmartDictionary deviceState = new SmartDictionary();

var filename = "chiller-01-state.js";

var script = new Script
{
Path = "chiller-01-state.js"
};
var context = new Dictionary<string, object>
{
["currentTime"] = DateTimeOffset.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:sszzz"),
Expand All @@ -64,7 +72,7 @@ public void ReturnedStateIsIntact()
deviceState.SetAll(state);

// Act
this.target.Invoke(filename, context, deviceState, this.properties.Object);
this.target.Invoke(script, context, deviceState, this.properties.Object);

// Assert
Assert.Equal(state.Count, deviceState.GetAll().Count);
Expand All @@ -83,18 +91,18 @@ public void TestJavascriptFiles()
// Arrange
SmartDictionary deviceState = new SmartDictionary();

var files = new List<string>
var files = new List<Script>
{
"chiller-01-state.js",
"chiller-02-state.js",
"elevator-01-state.js",
"elevator-02-state.js",
"engine-01-state.js",
"engine-02-state.js",
"prototype-01-state.js",
"prototype-02-state.js",
"truck-01-state.js",
"truck-02-state.js"
new Script { Path = "chiller-01-state.js" },
new Script { Path = "chiller-02-state.js" },
new Script { Path = "elevator-01-state.js" },
new Script { Path = "elevator-02-state.js" },
new Script { Path = "engine-01-state.js" },
new Script { Path = "engine-02-state.js" },
new Script { Path = "prototype-01-state.js" },
new Script { Path = "prototype-02-state.js" },
new Script { Path = "truck-01-state.js" },
new Script { Path = "truck-02-state.js" }
};
var context = new Dictionary<string, object>
{
Expand Down
Loading

0 comments on commit b8dab03

Please sign in to comment.