public void ThrowsWhenTheReasonExceptionIsNotAnApiException() { var state = new MarkEntityAsUnsyncableState <IThreadSafeTestModel>(dataSource, TestModel.Unsyncable); var exception = new TestException(); Action callingStart = () => state.Start( (exception, new TestModel(1, SyncStatus.SyncNeeded))).SingleAsync().Wait(); callingStart.Should().Throw <TestException>().Where(e => e == exception); }
public void ThrowsWhenArgumentsAreNull(bool hasEntity, bool hasReason) { var entity = hasEntity ? new TestModel(-1, SyncStatus.SyncNeeded) : (IThreadSafeTestModel)null; Exception reason = hasReason ? new ApiException(request, response, "Test.") : null; var state = new MarkEntityAsUnsyncableState <IThreadSafeTestModel>(dataSource, TestModel.Unsyncable); Action callingStart = () => state.Start((reason, entity)).SingleAsync().Wait(); callingStart.Should().Throw <ArgumentNullException>(); }
public async Task TheSyncStatusOfTheEntityChangesToSyncFailedWhenEverythingWorks() { var entity = new TestModel(1, SyncStatus.SyncNeeded); var state = new MarkEntityAsUnsyncableState <IThreadSafeTestModel>(dataSource, TestModel.Unsyncable); prepareBatchUpdate(entity); var transition = await state.Start((new BadRequestException(request, response), entity)).SingleAsync(); var unsyncableEntity = ((Transition <IThreadSafeTestModel>)transition).Parameter; unsyncableEntity.SyncStatus.Should().Be(SyncStatus.SyncFailed); }
public void ThrowsWhenDatabaseOperationFails() { var state = new MarkEntityAsUnsyncableState <IThreadSafeTestModel>(dataSource, TestModel.Unsyncable); dataSource .OverwriteIfOriginalDidNotChange(null, null) .ReturnsForAnyArgs(_ => throw new TestException()); Action callingStart = () => state.Start( (new ApiException(request, response, "Test."), new TestModel(1, SyncStatus.SyncNeeded))).SingleAsync().Wait(); callingStart.Should().Throw <TestException>(); }
public async Task TheUpdatedEntityHasTheSameIdAsTheOriginalEntity() { var entity = new TestModel(1, SyncStatus.SyncNeeded); var state = new MarkEntityAsUnsyncableState <IThreadSafeTestModel>(dataSource, TestModel.Unsyncable); prepareBatchUpdate(entity); await state.Start((new BadRequestException(request, response), entity)).SingleAsync(); await dataSource .Received() .OverwriteIfOriginalDidNotChange( Arg.Is(entity), Arg.Is <IThreadSafeTestModel>(updatedEntity => updatedEntity.Id == entity.Id)); }
public void TheErrorMessageMatchesTheMessageFromTheReasonException(NonNull <string> message) { var entity = new TestModel(1, SyncStatus.SyncNeeded); var response = Substitute.For <IResponse>(); response.RawData.Returns(message.Get); var reason = new BadRequestException(request, response); var state = new MarkEntityAsUnsyncableState <IThreadSafeTestModel>(dataSource, TestModel.Unsyncable); prepareBatchUpdate(entity); var transition = state.Start((reason, entity)).SingleAsync().Wait(); var unsyncableEntity = ((Transition <IThreadSafeTestModel>)transition).Parameter; unsyncableEntity.LastSyncErrorMessage.Should().Be(message.Get); }
public void TheOnlyThingThatChangesInTheUnsyncableEntityIsTheSyncStatusAndLastSyncErrorMessage() { var entity = new TestModel(1, SyncStatus.SyncNeeded); var reason = new BadRequestException(request, response); var state = new MarkEntityAsUnsyncableState <IThreadSafeTestModel>(dataSource, TestModel.Unsyncable); prepareBatchUpdate(entity); var transition = state.Start((reason, entity)).SingleAsync().Wait(); var unsyncableEntity = ((Transition <IThreadSafeTestModel>)transition).Parameter; entity.Should().BeEquivalentTo(unsyncableEntity, options => options.IncludingProperties() .Excluding(x => x.LastSyncErrorMessage) .Excluding(x => x.SyncStatus)); }
private static LookForChangeToPushState <TDatabase, TThreadsafe> configureCreateOnlyPush <TModel, TDatabase, TThreadsafe>( ITransitionConfigurator transitions, IStateResult entryPoint, IDataSource <TThreadsafe, TDatabase> dataSource, IAnalyticsService analyticsService, ICreatingApiClient <TModel> creatingApi, ILeakyBucket minutesLeakyBucket, IRateLimiter rateLimiter, WaitForAWhileState waitForAWhileState, Func <TModel, TThreadsafe> toClean, Func <TThreadsafe, string, TThreadsafe> toUnsyncable) where TModel : IIdentifiable, ILastChangedDatable where TDatabase : class, TModel, IDatabaseSyncable where TThreadsafe : class, TDatabase, IThreadSafeModel { var lookForChange = new LookForChangeToPushState <TDatabase, TThreadsafe>(dataSource); var chooseOperation = new ChooseSyncOperationState <TThreadsafe>(); var create = new CreateEntityState <TModel, TDatabase, TThreadsafe>(creatingApi, dataSource, analyticsService, minutesLeakyBucket, rateLimiter, toClean); var processClientError = new ProcessClientErrorState <TThreadsafe>(); var unsyncable = new MarkEntityAsUnsyncableState <TThreadsafe>(dataSource, toUnsyncable); transitions.ConfigureTransition(entryPoint, lookForChange); transitions.ConfigureTransition(lookForChange.ChangeFound, chooseOperation); transitions.ConfigureTransition(chooseOperation.CreateEntity, create); transitions.ConfigureTransition(chooseOperation.UpdateEntity, new InvalidTransitionState($"Updating is not supported for {typeof(TModel).Name} during Push sync.")); transitions.ConfigureTransition(chooseOperation.DeleteEntity, new InvalidTransitionState($"Deleting is not supported for {typeof(TModel).Name} during Push sync.")); transitions.ConfigureTransition(chooseOperation.DeleteEntityLocally, new InvalidTransitionState($"Deleting locally is not supported for {typeof(TModel).Name} during Push sync.")); transitions.ConfigureTransition(create.ClientError, processClientError); transitions.ConfigureTransition(create.ServerError, new FailureState()); transitions.ConfigureTransition(create.UnknownError, new FailureState()); transitions.ConfigureTransition(create.PreventOverloadingServer, waitForAWhileState); transitions.ConfigureTransition(processClientError.UnresolvedTooManyRequests, new FailureState()); transitions.ConfigureTransition(processClientError.Unresolved, unsyncable); transitions.ConfigureTransition(create.EntityChanged, new InvalidTransitionState($"Entity cannot have changed since updating is not supported for {typeof(TModel).Name} during Push sync.")); transitions.ConfigureTransition(create.Done, lookForChange); transitions.ConfigureTransition(unsyncable.Done, lookForChange); return(lookForChange); }