public void ItCreatesTheRightNumberOfPartitions() { // Arrange var partitionSize = 3; var simulation = new SimulationModel { Id = SIM_ID, PartitioningComplete = false }; var deviceIdsByModel = new Dictionary <string, List <string> > { { "m1", new List <string> { "d1", "d2", "d3" } }, { "m2", new List <string> { "j3" } }, { "m3", new List <string> { "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11" } } }; this.simulations.Setup(x => x.GetAsync(SIM_ID)).ReturnsAsync(simulation); this.simulations.Setup(x => x.GetDeviceIdsByModel(simulation)).Returns(deviceIdsByModel); // Act var instance = this.GetTargetInstance(partitionSize); instance.CreateAsync(SIM_ID).CompleteOrTimeout(); // Assert this.partitionsStorage.Verify(x => x.CreateAsync(It.IsAny <StorageRecord>()), Times.Exactly(4)); }
public void ItStartsTheDeviceDeletionOnlyIfNotStarted() { // Arrange var simulationId = Guid.NewGuid().ToString(); var sim = new SimulationModel { Id = simulationId, Enabled = true, DevicesDeletionStarted = true, DeviceModels = new List <SimulationModel.DeviceModelRef> { new SimulationModel.DeviceModelRef { Id = "some", Count = 3 } } }; IDataRecord storageRecord = new DataRecord { Id = "foo" }; storageRecord.SetData(JsonConvert.SerializeObject(sim)).SetETag("*"); this.simulationsStorage.Setup(x => x.GetAsync(It.IsAny <string>())) .ReturnsAsync(storageRecord); // Act var result = this.target.TryToStartDevicesDeletionAsync(simulationId, this.devices.Object) .CompleteOrTimeout().Result; // Assert Assert.True(result); this.devices.Verify(x => x.DeleteListUsingJobsAsync(It.IsAny <IEnumerable <string> >()), Times.Never); }
public void ItStartsTheDeviceCreationOnlyIfNotStarted() { // Arrange var simulationId = Guid.NewGuid().ToString(); var sim = new SimulationModel { Id = simulationId, Enabled = true, DevicesCreationStarted = true, DeviceModels = new List <SimulationModel.DeviceModelRef> { new SimulationModel.DeviceModelRef { Id = "some", Count = 3 } } }; // Create a DocumentDB Document that will be used to create a StorageRecord object var document = new Document(); document.Id = "foo"; document.SetPropertyValue("Data", JsonConvert.SerializeObject(sim)); document.SetPropertyValue("ETag", "*"); var storageRecord = StorageRecord.FromDocumentDb(document); this.mockStorageRecords.Setup(x => x.GetAsync(It.IsAny <string>())) .ReturnsAsync(storageRecord); // Act var result = this.target.TryToStartDevicesCreationAsync(simulationId, this.devices.Object) .CompleteOrTimeout().Result; // Assert Assert.True(result); this.devices.Verify(x => x.CreateListUsingJobsAsync(It.IsAny <IEnumerable <string> >()), Times.Never); }
public void SimulationsCanBeUpserted() { // Arrange this.ThereAreSomeDeviceModels(); this.ThereAreNoSimulationsInTheStorage(); // Arrange the simulation data returned by the storage adapter var updatedValue = new ValueApiModel { ETag = "newETag" }; this.storage.Setup(x => x.UpdateAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>())) .ReturnsAsync(updatedValue); // Act var simulation = new SimulationModel { Id = SIMULATION_ID, Enabled = false, ETag = "oldETag" }; this.target.UpsertAsync(simulation).Wait(); // Assert this.storage.Verify( x => x.UpdateAsync(STORAGE_COLLECTION, SIMULATION_ID, It.IsAny <string>(), "oldETag")); Assert.Equal("newETag", simulation.ETag); }
public void ItCreatesNewSimulationsWithPartitioningStateNotComplete() { // Arrange this.simulationsStorage.Setup(x => x.GetAsync(It.IsAny <string>())) .ReturnsAsync((DataRecord)null); var sim = new SimulationModel { Id = "1", Enabled = true, PartitioningComplete = true }; IDataRecord record = new DataRecord(); record.SetData(JsonConvert.SerializeObject(sim)); this.simulationsStorage.Setup(x => x.UpsertAsync(It.IsAny <IDataRecord>())) .ReturnsAsync(record); // Act SimulationModel result = this.target.UpsertAsync(sim, false) .CompleteOrTimeout().Result; // Assert Assert.False(result.PartitioningComplete); }
public void ItTriggersPartitionsDeletionWhenASimulationIsDisabled() { // Arrange var sim = new SimulationModel { Id = "1", Enabled = true, PartitioningComplete = true, ETag = "*" }; IDataRecord storageRecord = new DataRecord(); storageRecord.SetData(JsonConvert.SerializeObject(sim)).SetETag("*"); this.simulationsStorage.Setup(x => x.GetAsync(It.IsAny <string>())) .ReturnsAsync(storageRecord); this.simulationsStorage.Setup(x => x.UpsertAsync(It.IsAny <IDataRecord>(), It.IsAny <string>())) .ReturnsAsync(storageRecord); // Act var update = new SimulationModel { Id = sim.Id, Enabled = false, PartitioningComplete = true, ETag = "*" }; var result = this.target.UpsertAsync(update, false).CompleteOrTimeout().Result; // Assert Assert.False(result.Enabled); Assert.False(result.PartitioningComplete); }
private async Task <Models.Simulation> SaveAsync(Models.Simulation simulation, string eTag) { simulation.Modified = DateTimeOffset.UtcNow; // When a simulation is disabled, its partitions are deleted - this triggers the deletion if (!simulation.Enabled) { simulation.PartitioningComplete = false; } var result = await this.simulationsStorage.UpsertAsync( new StorageRecord { Id = simulation.Id, Data = JsonConvert.SerializeObject(simulation) }, eTag ); // Use the new ETag provided by the storage simulation.ETag = result.ETag; simulation.Id = result.Id; this.log.Info("Simulation written to storage", () => new { simulation.Id, simulation.Enabled, simulation.PartitioningComplete }); return(simulation); }
public void UpsertFailsWhenETagsDoNotMatch() { // Arrange const string ETAG1 = "ETag 001"; const string ETAG2 = "ETag 002"; // Mock simulation that will be returned from storage // Create a mock Cosmos DB SQL Document object that will contain a // different ETag than the one we're trying to use to upsert var updatedSimulation = new SimulationModel { Id = SIMULATION_ID, Name = "Test Simulation 2", ETag = ETAG2 }; var record = new DataRecord { Id = "foo" }; record.SetETag(ETAG2); record.SetData(JsonConvert.SerializeObject(updatedSimulation)); this.simulationsStorage.Setup(x => x.GetAsync(It.IsAny <string>())).ReturnsAsync(record); // Act + Assert var initialSimulation = new SimulationModel { Id = SIMULATION_ID, Name = "Test Simulation 1", ETag = ETAG1 }; Assert.ThrowsAsync <ResourceOutOfDateException>( async() => await this.target.UpsertAsync(initialSimulation, false)) .CompleteOrTimeout(); }
public void CreateDefaultSimulation() { // Arrange const int DEFAULT_DEVICE_COUNT = 1; this.ThereAreNoSimulationsInTheStorage(); this.ThereAreSomeDeviceModels(); var simulation = new SimulationModel(); this.StorageReturnsSimulationRecordOnCreate(simulation); // Return the StorageRecord object that will be passed to StorageRecords this.mockStorageRecords.Setup( x => x.UpsertAsync( It.IsAny <StorageRecord>(), It.IsAny <string>())) .ReturnsAsync((StorageRecord storageRecord, string eTag) => storageRecord); // Act SimulationModel result = this.target.InsertAsync(simulation, "default").Result; // Assert Assert.False(result.PartitioningComplete); Assert.Equal(this.models.Count, result.DeviceModels.Count); for (var i = 0; i < this.models.Count; i++) { Assert.Equal(this.models[i].Id, result.DeviceModels[i].Id); Assert.Equal(DEFAULT_DEVICE_COUNT, result.DeviceModels[i].Count); } }
public void ThereAreNoNullPropertiesInTheDeviceModel() { // Arrange this.ThereAreSomeDeviceModels(); this.ThereAreNoSimulationsInTheStorage(); // Arrange the simulation data returned by the storage adapter var simulation = new SimulationModel { Id = SIMULATION_ID, ETag = "ETag0", Enabled = true, Version = 1 }; var updatedValue = new ValueApiModel { Key = SIMULATION_ID, Data = JsonConvert.SerializeObject(simulation), ETag = simulation.ETag }; this.storage.Setup(x => x.UpdateAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>())) .ReturnsAsync(updatedValue); // Act this.target.UpsertAsync(simulation).Wait(); // Assert this.storage.Verify(x => x.UpdateAsync( STORAGE_COLLECTION, SIMULATION_ID, It.Is <string>(s => !s.Contains("null")), "ETag0")); }
public void UpsertRequiresIdWhileInsertDoesNot() { // Arrange var s1 = new SimulationModel() { Name = "Test Simulation 1" }; var s2 = new SimulationModel() { Name = "Test Simulation 2" }; this.ThereAreNoSimulationsInTheStorage(); this.StorageReturnsSimulationRecordOnCreate(s1); var s2StorageRecord = new StorageRecord { Id = s2.Id, Data = JsonConvert.SerializeObject(s2), }; this.mockStorageRecords.Setup(x => x.UpsertAsync(It.IsAny <StorageRecord>())) .ReturnsAsync(s2StorageRecord); // Act - No exception occurs this.target.InsertAsync(s1).Wait(Constants.TEST_TIMEOUT); // Act + Assert Assert.ThrowsAsync <InvalidInputException>(async() => await this.target.UpsertAsync(s2)) .Wait(Constants.TEST_TIMEOUT); }
public void ItCreatesNewSimulationsWithPartitioningStateNotComplete() { // Arrange this.mockStorageRecords.Setup(x => x.GetAsync(It.IsAny <string>())) .ReturnsAsync((StorageRecord)null); var sim = new SimulationModel { Id = "1", Enabled = true, PartitioningComplete = true }; var record = new ValueApiModel { Key = "1", Data = JsonConvert.SerializeObject(sim), }; // Create a DocumentDB Document that will be used to create a StorageRecord object var document = new Document(); document.Id = "foo"; document.SetPropertyValue("Data", JsonConvert.SerializeObject(sim)); var storageRecord = StorageRecord.FromDocumentDb(document); this.mockStorageRecords.Setup(x => x.UpsertAsync(It.IsAny <StorageRecord>())) .ReturnsAsync(storageRecord); // Act SimulationModel result = this.target.UpsertAsync(sim).CompleteOrTimeout().Result; // Assert Assert.False(result.PartitioningComplete); }
public void ItGenerateDevicesIdGroupedByModel() { // Arrange var sim = new SimulationModel { Id = "1", Enabled = true, PartitioningComplete = true, DeviceModels = new List <SimulationModel.DeviceModelRef> { new SimulationModel.DeviceModelRef { Id = "modelA", Count = 5 }, new SimulationModel.DeviceModelRef { Id = "modelB", Count = 2 }, new SimulationModel.DeviceModelRef { Id = "modelC", Count = 4 } } }; // Act Dictionary <string, List <string> > result = this.target.GetDeviceIdsByModel(sim); // Assert Assert.Equal(3, result.Count); Assert.True(result.ContainsKey("modelA")); Assert.True(result.ContainsKey("modelB")); Assert.True(result.ContainsKey("modelC")); Assert.Equal(5, result["modelA"].Count); Assert.Equal(2, result["modelB"].Count); Assert.Equal(4, result["modelC"].Count); }
public void ItStartsTheDeviceBulkDeletionUsingJobs() { // Arrange var eTagValue = "*"; var sim = new SimulationModel { Id = "1", Enabled = true, PartitioningComplete = true, ETag = eTagValue }; IDataRecord storageRecord = new DataRecord { Id = sim.Id }; storageRecord.SetData(JsonConvert.SerializeObject(sim)).SetETag(eTagValue); this.simulationsStorage.Setup(x => x.GetAsync(It.IsAny <string>())) .ReturnsAsync(storageRecord); // Act var result = this.target.TryToStartDevicesDeletionAsync(sim.Id, this.devices.Object) .CompleteOrTimeout().Result; // Assert Assert.True(result); this.devices.Verify(x => x.DeleteListUsingJobsAsync(It.IsAny <IEnumerable <string> >()), Times.Once); this.simulationsStorage.Verify( x => x.UpsertAsync( It.Is <IDataRecord>( sr => JsonConvert.DeserializeObject <SimulationModel>(sr.GetData()).DevicesDeletionStarted), It.IsAny <string>()), Times.Once()); }
/// <summary> /// Create or Replace a simulation. /// The logic works under the assumption that there is only one simulation with id "1". /// </summary> public async Task <Models.Simulation> UpsertAsync(Models.Simulation simulation) { if (simulation.Id != SIMULATION_ID) { this.log.Warn("Invalid simulation ID. Only one simulation is allowed", () => { }); throw new InvalidInputException("Invalid simulation ID. Use ID '" + SIMULATION_ID + "'."); } var simulations = await this.GetListAsync(); if (simulations.Count > 0) { this.log.Info("Modifying simulation via PUT.", () => { }); if (simulation.ETag == "*") { simulation.ETag = simulations[0].ETag; this.log.Info("The client used ETag='*' choosing to overwrite the current simulation", () => { }); } if (simulation.ETag != simulations[0].ETag) { this.log.Error("Invalid ETag. Running simulation ETag is:'", () => new { simulations }); throw new ResourceOutOfDateException("Invalid ETag. Running simulation ETag is:'" + simulations[0].ETag + "'."); } simulation.Created = simulations[0].Created; simulation.Modified = DateTimeOffset.UtcNow; simulation.Version = simulations[0].Version + 1; } else { this.log.Info("Creating new simulation via PUT.", () => { }); // new simulation simulation.Created = DateTimeOffset.UtcNow; simulation.Modified = simulation.Created; simulation.Version = 1; } // Note: forcing the ID because only one simulation can be created simulation.Id = SIMULATION_ID; // TODO if write to storage adapter fails, the iothub connection string // will still be stored to disk. Storing the encrypted string using // storage adapter would address this // https://github.com/Azure/device-simulation-dotnet/issues/129 simulation.IotHubConnectionString = await this.connectionStringManager.RedactAndStoreAsync(simulation.IotHubConnectionString); var result = await this.storage.UpdateAsync( STORAGE_COLLECTION, SIMULATION_ID, JsonConvert.SerializeObject(simulation), simulation.ETag); // Return the new ETag provided by the storage simulation.ETag = result.ETag; return(simulation); }
/// <summary> /// Create a simulation. /// </summary> public async Task <Models.Simulation> InsertAsync(Models.Simulation simulation, string template = "") { // TODO: complete validation if (!string.IsNullOrEmpty(template) && template.ToLowerInvariant() != "default") { this.log.Warn("Unknown template name", () => new { template }); throw new InvalidInputException("Unknown template name. Try 'default'."); } var simulations = await this.GetListAsync(); if (simulations.Count > 0) { this.log.Warn("There is already a simulation", () => { }); throw new ConflictingResourceException( "There is already a simulation. Only one simulation can be created."); } // Note: forcing the ID because only one simulation can be created simulation.Id = SIMULATION_ID; simulation.Created = DateTimeOffset.UtcNow; simulation.Modified = simulation.Created; simulation.Version = 1; // Create default simulation if (!string.IsNullOrEmpty(template) && template.ToLowerInvariant() == "default") { var types = await this.deviceModels.GetListAsync(); simulation.DeviceModels = new List <Models.Simulation.DeviceModelRef>(); foreach (var type in types) { simulation.DeviceModels.Add(new Models.Simulation.DeviceModelRef { Id = type.Id, Count = DEVICES_PER_MODEL_IN_DEFAULT_TEMPLATE }); } } // TODO if write to storage adapter fails, the iothub connection string // will still be stored to disk. Storing the encrypted string using // storage adapter would address this // https://github.com/Azure/device-simulation-dotnet/issues/129 simulation.IotHubConnectionString = await this.connectionStringManager.RedactAndStoreAsync(simulation.IotHubConnectionString); // Note: using UpdateAsync because the service generates the ID var result = await this.storage.UpdateAsync( STORAGE_COLLECTION, SIMULATION_ID, JsonConvert.SerializeObject(simulation), "*"); simulation.ETag = result.ETag; return(simulation); }
/// <summary> /// Create a simulation. /// </summary> public async Task <Models.Simulation> InsertAsync(Models.Simulation simulation, string template = "") { var usingDefaultTemplate = !string.IsNullOrEmpty(template) && template.ToLowerInvariant() == DEFAULT_TEMPLATE_NAME; if (!string.IsNullOrEmpty(template) && template.ToLowerInvariant() != DEFAULT_TEMPLATE_NAME) { this.log.Warn("Unknown template name", () => new { template }); throw new InvalidInputException("Unknown template name. Try 'default'."); } if (!usingDefaultTemplate && string.IsNullOrEmpty(simulation.Name)) { this.log.Warn("Missing simulation name", () => new { }); throw new InvalidInputException("Simulation name is required."); } // Note: forcing the ID because only one simulation can be created simulation.Id = usingDefaultTemplate ? DEFAULT_SIMULATION_ID : Guid.NewGuid().ToString(); simulation.Created = DateTimeOffset.UtcNow; // Create default simulation if (usingDefaultTemplate) { simulation.Name = "Default Simulation"; var types = await this.deviceModels.GetListAsync(); simulation.DeviceModels = new List <Models.Simulation.DeviceModelRef>(); foreach (var type in types) { simulation.DeviceModels.Add(new Models.Simulation.DeviceModelRef { Id = type.Id, Count = DEVICES_PER_MODEL_IN_DEFAULT_TEMPLATE }); } simulation.IotHubConnectionStrings = new List <string> { ServicesConfig.USE_DEFAULT_IOTHUB }; } for (var index = 0; index < simulation.IotHubConnectionStrings.Count; index++) { var connString = await this.connectionStringManager.RedactAndSaveAsync(simulation.IotHubConnectionStrings[index]); if (!simulation.IotHubConnectionStrings.Contains(connString)) { simulation.IotHubConnectionStrings[index] = connString; } } // This value cannot be set by the user, we set it here and make sure it's "false" simulation.PartitioningComplete = false; return(await this.SaveAsync(simulation, "*")); }
public void UpsertWillSucceedWhenETagsMatch() { // Arrange const string ETAG1 = "ETag 001"; const string ETAG2 = "ETag 002"; // Mock simulation that will be returned from storage var existingSimulation = new SimulationModel { Id = SIMULATION_ID, Name = "Test Simulation 2", ETag = ETAG1 }; var updatedStorageRecord = new StorageRecord { Id = SIMULATION_ID, Data = JsonConvert.SerializeObject(existingSimulation), }; // Create a mock DocumentDB Document object that will contain the // same ETag value as the one we're trying to use to upsert with. var document = new Document(); document.Id = "foo"; document.SetPropertyValue("_etag", ETAG1); document.SetPropertyValue("Data", JsonConvert.SerializeObject(existingSimulation)); var mockStorageRecord = StorageRecord.FromDocumentDb(document); // Initial simulation var initialSimulation = new SimulationModel { Id = SIMULATION_ID, Name = "Test Simulation 1", ETag = ETAG1 }; var initialStorageRecord = new StorageRecord { Id = SIMULATION_ID, Data = JsonConvert.SerializeObject(initialSimulation), }; // Create a second document that will be returned after the upsert, // which will contain an updated ETag var upsertResultDocument = new Document(); upsertResultDocument.Id = "bar"; upsertResultDocument.SetPropertyValue("_etag", ETAG2); upsertResultDocument.SetPropertyValue("Data", JsonConvert.SerializeObject(initialSimulation)); var upsertResultStorageRecord = StorageRecord.FromDocumentDb(upsertResultDocument); this.mockStorageRecords.Setup(x => x.GetAsync(It.IsAny <string>())).ReturnsAsync(mockStorageRecord); this.mockStorageRecords.Setup(x => x.UpsertAsync(It.IsAny <StorageRecord>(), It.IsAny <string>())).ReturnsAsync(upsertResultStorageRecord); // Act var returnedSimulationTask = this.target.UpsertAsync(initialSimulation); returnedSimulationTask.Wait(Constants.TEST_TIMEOUT); // Assert Assert.Matches(ETAG2, returnedSimulationTask.Result.ETag); }
public void UpsertUsesOptimisticConcurrency() { // Arrange const string ETAG1 = "001"; const string ETAG2 = "002"; // Initial simulation var simulation1 = new SimulationModel { Id = SIMULATION_ID, ETag = ETAG1 }; var storageRecord1 = new ValueApiModel { Key = SIMULATION_ID, Data = JsonConvert.SerializeObject(simulation1), ETag = simulation1.ETag }; var storageList1 = new ValueListApiModel(); storageList1.Items.Add(storageRecord1); // Simulation after update var simulation2 = new SimulationModel { Id = SIMULATION_ID, ETag = ETAG2 }; var storageRecord2 = new ValueApiModel { Key = SIMULATION_ID, Data = JsonConvert.SerializeObject(simulation2), ETag = simulation2.ETag }; var storageList2 = new ValueListApiModel(); storageList2.Items.Add(storageRecord2); // Initial setup - the ETag matches this.storage.Setup(x => x.GetAllAsync(STORAGE_COLLECTION)).ReturnsAsync(storageList1); this.storage.Setup(x => x.UpdateAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>())) .ReturnsAsync(storageRecord2); // Act - No exception because ETag matches // Note: the call to UpsertAsync modifies the object, don't reuse the variable later this.target.UpsertAsync(simulation1).Wait(Constants.TEST_TIMEOUT); // Arrange - the ETag won't match this.storage.Setup(x => x.GetAllAsync(STORAGE_COLLECTION)).ReturnsAsync(storageList2); // Act + Assert var simulationOutOfDate = new SimulationModel { Id = SIMULATION_ID, ETag = ETAG1 }; Assert.ThrowsAsync <ResourceOutOfDateException>( async() => await this.target.UpsertAsync(simulationOutOfDate)) .Wait(Constants.TEST_TIMEOUT); }
// This creates sample simulations that will be shown on simulation dashboard by default private async Task SeedSimulationsAsync(string templateName) { string content; var fileName = templateName + ".json"; var filePath = Path.Combine(this.config.SeedTemplateFolder, fileName); if (this.fileSystem.Exists(filePath)) { content = this.fileSystem.ReadAllText(filePath); } else { this.log.Debug("Template not found for setting sample simulations."); return; } Models.Simulation simulation = null; try { var simulationList = JsonConvert.DeserializeObject <List <Models.Simulation> >(content); if (simulationList == null || simulationList.Count == 0) { return; } for (int index = 0; index < simulationList.Count; index++) { // We need to start creating simulations starting with id 1 as it is treated as default simulation // and is referenced in Welcome page on UI simulation = simulationList[index]; var simulationId = index + 1; simulation.Id = simulationId.ToString(); try { // Check if there is an existing simulation with the given id // if it exists then skip creating a new simulation await this.GetAsync(simulation.Id); } catch (ResourceNotFoundException) { // create a simulation if no sample simulation exists with provided id. simulation.StartTime = DateTimeOffset.UtcNow; await this.UpsertAsync(simulation); } } } catch (Exception ex) { var message = "Failed to create sample simulation. " + " template: " + JsonConvert.SerializeObject(simulation); this.log.Error(message, ex); } }
public void UpsertRequiresIdWhileInsertDoesNot() { // Act var s1 = new SimulationModel(); var s2 = new SimulationModel(); this.target.InsertAsync(s1); // Act + Assert Assert.ThrowsAsync <InvalidInputException>(() => this.target.UpsertAsync(s2)); }
public void InitialMetadataAfterCreation() { // Arrange this.ThereAreNoSimulationsInTheStorage(); this.ThereAreSomeDeviceModels(); // Act SimulationModel result = this.target.InsertAsync(new SimulationModel(), "default").Result; // Assert Assert.Equal(result.Created, result.Modified); }
private async Task <Models.Simulation> ResetSimulationStatisticsAsync(Models.Simulation simulation) { // Reset ActualStartTime, which is used to calculate statistics // from the moment when the simulation starts connecting devices and sending telemetry, // i.e. after the devices have been created. simulation.ActualStartTime = null; // Delete statistics records on simulation start await this.simulationStatistics.DeleteSimulationStatisticsAsync(simulation.Id); return(simulation); }
public void CreateSimulationWithoutId() { // Arrange this.ThereAreNoSimulationsInTheStorage(); this.ThereAreSomeDeviceModels(); // Act SimulationModel result = this.target.InsertAsync(new SimulationModel(), "default").Result; // Assert Assert.NotEmpty(result.Id); }
private async Task <bool> TryToUpdateSimulationAsync(Models.Simulation simulation) { try { await this.SaveAsync(simulation, simulation.ETag); } catch (ConflictingResourceException e) { this.log.Warn("Update failed, another client modified the simulation record", e); return(false); } return(true); }
public async Task <Models.Simulation> InsertAsync(Models.Simulation simulation, string template = "") { // TODO: complete validation if (!string.IsNullOrEmpty(template) && template.ToLowerInvariant() != "default") { this.log.Warn("Unknown template name", () => new { template }); throw new InvalidInputException("Unknown template name. Try 'default'."); } var simulations = await this.GetListAsync(); if (simulations.Count > 0) { this.log.Warn("There is already a simulation", () => { }); throw new ConflictingResourceException( "There is already a simulation. Only one simulation can be created."); } // Note: forcing the ID because only one simulation can be created simulation.Id = SIMULATION_ID; simulation.Created = DateTimeOffset.UtcNow; simulation.Modified = simulation.Created; simulation.Version = 1; // Create default simulation if (!string.IsNullOrEmpty(template) && template.ToLowerInvariant() == "default") { var types = this.deviceModels.GetList(); simulation.DeviceModels = new List <Models.Simulation.DeviceModelRef>(); foreach (var type in types) { simulation.DeviceModels.Add(new Models.Simulation.DeviceModelRef { Id = type.Id, Count = DEVICES_PER_MODEL_IN_DEFAULT_TEMPLATE }); } } // Note: using UpdateAsync because the service generates the ID var result = await this.storage.UpdateAsync( STORAGE_COLLECTION, SIMULATION_ID, JsonConvert.SerializeObject(simulation), "*"); simulation.Etag = result.ETag; return(simulation); }
public void InitialMetadataAfterCreation() { // Arrange this.ThereAreNoSimulationsInTheStorage(); this.ThereAreSomeDeviceModels(); this.StorageReturnsSimulationRecordOnCreate(); // Act SimulationModel result = this.target.InsertAsync(new SimulationModel(), "default").Result; // Assert Assert.False(result.PartitioningComplete); Assert.True(Math.Abs(result.Modified.ToUnixTimeSeconds() - result.Created.ToUnixTimeSeconds()) < 10); }
public void ItChangesTheSimulationStatusWhenTheDeviceCreationIsComplete() { // Arrange var simulationId = Guid.NewGuid().ToString(); var sim = new SimulationModel { Id = simulationId, Enabled = true, DevicesCreationStarted = false, DeviceModels = new List <SimulationModel.DeviceModelRef> { new SimulationModel.DeviceModelRef { Id = "some", Count = 3 } } }; IDataRecord storageRecord = new DataRecord { Id = simulationId }; storageRecord.SetData(JsonConvert.SerializeObject(sim)).SetETag("*"); this.simulationsStorage.Setup(x => x.GetAsync(It.IsAny <string>())) .ReturnsAsync(storageRecord); // Act var result = this.target.TryToSetDeviceCreationCompleteAsync(simulationId) .CompleteOrTimeout().Result; // Assert - operation succeeded Assert.True(result); // Assert - DevicesCreationComplete is set to true Func <string, SimulationModel> deserialize = JsonConvert.DeserializeObject <SimulationModel>; this.simulationsStorage.Verify(x => x.BuildRecord(sim.Id, It.Is <string>( sr => deserialize(sr).DevicesCreationComplete == true)), Times.Once()); // Assert - DevicesDeletionComplete, DeviceDeletionJobId and DevicesDeletionStarted are reset this.simulationsStorage.Verify(x => x.BuildRecord(sim.Id, It.Is <string>( sr => deserialize(sr).DevicesDeletionComplete == false)), Times.Once()); this.simulationsStorage.Verify(x => x.BuildRecord(sim.Id, It.Is <string>( sr => deserialize(sr).DeviceDeletionJobId == null)), Times.Once()); this.simulationsStorage.Verify(x => x.BuildRecord(sim.Id, It.Is <string>( sr => deserialize(sr).DevicesDeletionStarted == false)), Times.Once()); // Assert - only one write this.simulationsStorage.Verify(x => x.UpsertAsync(It.IsAny <IDataRecord>(), It.IsAny <string>()), Times.Once()); }
public void CreatingMultipleSimulationsIsNotAllowed() { // Arrange this.ThereAreSomeDeviceModels(); this.ThereIsAnEnabledSimulationInTheStorage(); // Act + Assert var s = new SimulationModel { Id = Guid.NewGuid().ToString(), Enabled = false }; Assert.ThrowsAsync <ConflictingResourceException>(() => this.target.InsertAsync(s)); Assert.ThrowsAsync <ConflictingResourceException>(() => this.target.UpsertAsync(s)); }
public void CreateSimulationWithId() { // Arrange this.ThereAreSomeDeviceModels(); this.ThereAreNoSimulationsInTheStorage(); // Act var simulation = new SimulationModel { Id = "123" }; SimulationModel result = this.target.InsertAsync(simulation, "default").Result; // Assert Assert.Equal(simulation.Id, result.Id); }