Exemple #1
0
        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);
        }
Exemple #2
0
        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>();
        }
Exemple #3
0
        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);
        }
Exemple #4
0
        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>();
        }
Exemple #5
0
        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));
        }
Exemple #6
0
        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);
        }
Exemple #7
0
        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);
        }