ReactiveUI Testing - reactiveui

I am attempting to see if the results of a view model are performing the correct actions.
My observables are setup as follows:
public FilterBoxViewModel()
{
var asyncFilterResults = this.filterItemsCommand.RegisterAsyncTask(x =>
this.PerformFilter(x as string));
this.filteredItems = new ObservableAsPropertyHelper<IEnumerable<IFilterBoxItem>>(
asyncFilterResults, _ => this.RaisePropertyChanged("FilteredItems"));
this.WhenAnyValue(x => x.SearchTerm)
.Throttle(TimeSpan.FromMilliseconds(50))
.Skip(1)
.Subscribe(this.filterItemsCommand.Execute);
}
Then further down I have
private async Task<IEnumerable<IFilterBoxItem>> PerformFilter(string searchTerm)
{
if (string.IsNullOrWhiteSpace(searchTerm))
{
return Enumerable.Empty<IFilterBoxItem>();
}
// Perform getting the items on the main thread and async await the results.
// This is provide a immutable version of the results so we don't cause
// threading issues.
var items = await Observable.Start(
() => this.FilterBoxManager.RootElements.GetAllItemsEnumerable()
.ToList().Select(x => new { Name = x.Name, Item = x }),
RxApp.MainThreadScheduler);
return
items.Where(x =>
x.Name.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) >= 0)
.Select(x => x.Item);
}
In my test, I am running the test schedular and advancing it, however, I am getting the PerformFilter performing at different times than I expect
eg my test is:
(new TestScheduler()).With(scheduler =>
{
var viewModel = new FilterBoxViewModel();
var testManager = new TestManager { RootElements = this.sampleItems };
viewModel.FilterBoxManager = testManager;
viewModel.SearchTerm = "folder";
scheduler.AdvanceBy(TimeSpan.FromMilliseconds(51).Ticks);
Assert.AreEqual(viewModel.FilteredItems.Select(x => x.Name), folderSearchResults);
viewModel.SearchTerm = "apple";
Assert.AreEqual(viewModel.FilteredItems.Select(x => x.Name), appleSearchResults);
});
How do I make the tester more predictable?
I am running ReactiveUI 5.5.1 and in a XAML application.

Your Throttle doesn't set a scheduler, this is a classic TestScheduler mistake

Related

Create custom exchange-to-exchange binding using MassTransit

I have microservice-based system which works with documents. Service publishes DocflowErrorMq, ImportedDocflowMq events, and other services are subscribed to these events. Critical service DocflowRegistry should process messages quickly, so we have to introduce multiple consumers. On the other hand message order shouldn't be broken and competing consumer doesn't suite. Consistent hash exchange distributes messages by routing key equals to document id, messages related to one document goes to one queue. So, we have simple manual scaling. I can't create binding between MqModels.Docflows:ImportedDocflowMq and docflow-process-dr exchanges (marked red on Diagram). Is it possible to create it with MassTransit?
DocflowRegistry service config:
services.AddMassTransit(x =>
{
x.AddConsumer<DocflowSendingErrorTestConsumer>();
x.AddConsumer<DocflowImportTestConsumer>();
x.UsingRabbitMq((context, cfg) =>
{
var virtualHost = configuration["RabbitMq:Settings:VirtualHost"] ?? "/";
cfg.Host(configuration["RabbitMqHost"], virtualHost, h =>
{
h.Username(configuration["RabbitMqUserName"]);
h.Password(configuration["RabbitMqPassword"]);
});
cfg.ReceiveEndpoint("docflow.process-1.docflowregistry", e =>
{
e.ConfigureConsumer<DocflowSendingErrorTestConsumer>(context);
e.ConfigureConsumer<DocflowImportTestConsumer>(context);
e.Bind("docflow-process-dr", x =>
{
x.Durable = true;
x.AutoDelete = false;
x.ExchangeType = "x-consistent-hash";
x.RoutingKey = "1";
});
e.ConfigureConsumeTopology = false;
e.SingleActiveConsumer = true;
});
cfg.ReceiveEndpoint("docflow.process-2.docflowregistry", e =>
{
e.ConfigureConsumer<DocflowSendingErrorTestConsumer>(context);
e.ConfigureConsumer<DocflowImportTestConsumer>(context);
e.Bind("docflow-process-dr", x =>
{
x.Durable = true;
x.AutoDelete = false;
x.ExchangeType = "x-consistent-hash";
x.RoutingKey = "1";
});
e.ConfigureConsumeTopology = false;
e.ConcurrentMessageLimit = 1;
e.SingleActiveConsumer = true;
});
});
});
Config of TodoList service:
services.AddMassTransit(x =>
{
x.AddConsumer<DocflowSendingErrorTestConsumer>();
x.AddConsumer<DocflowImportTestConsumer>();
x.UsingRabbitMq((context, cfg) =>
{
var virtualHost = configuration["RabbitMq:Settings:VirtualHost"] ?? "/";
cfg.Host(configuration["RabbitMqHost"], virtualHost, h =>
{
h.Username(configuration["RabbitMqUserName"]);
h.Password(configuration["RabbitMqPassword"]);
});
cfg.ReceiveEndpoint("docflow-process-todolist", e =>
{
e.ConfigureConsumer<DocflowSendingErrorTestConsumer>(context);
e.ConfigureConsumer<DocflowImportTestConsumer>(context);
e.SingleActiveConsumer = true;
});
});
});
Publish code:
var endPoint = await _massTransitBus.GetPublishSendEndpoint<DocflowErrorMq>();
var docflowGuid = Guid.NewGuid();
await endPoint.Send(new DocflowErrorMq
{
DocflowId = docflowGuid,
AbonentId = Guid.NewGuid()
},
context =>
{
context.SetRoutingKey(docflowGuid.ToString());
});
Create an interface, DocflowProcessDr, and make each of those message contracts published implement it. Then, you can configure the publish topology for that interface in the bus:
cfg.Message<DocflowProcessDr>(x => x.SetEntityName("docflow-process-dr"));
cfg.Publish<DocflowProcessDr>(x =>
{
x.ExchangeType = "x-consistent-hash";
});
Since MassTransit will create a polymorphic topology on the broker, you'll have an exchange-to-exchange binding between the published type and the interface.
Then, just publish the message:
var docflowGuid = Guid.NewGuid();
var endPoint = await _massTransitBus.Publish<DocflowErrorMq>(new DocflowErrorMq
{
DocflowId = docflowGuid,
AbonentId = Guid.NewGuid()
},
context =>
{
context.SetRoutingKey(docflowGuid.ToString());
});
Calling GetPublishSendEndpoint<T>() is weird, don't encourage it.

How to get results using a loop inside of switch map

I have the next code, and it was working properly. to execute a request to my method fetchDropdownDataByFederationId, but now I have a requirement to execute the same method x number of times.
fetchInProgress(queryString?): Observable<IPerson[]> {
let PersonList: IPerson[] = [];
return this.getItems<IPerson[]>('', queryString).pipe(
take(1),
switchMap((wls: IPerson[]) => {
PersonList = [...wls];
//const createdbyIds = [...new Set(wls.map((f) => f.createdBy))];
return this.teamPageService.getInformation(wls.createdBy);
}),
map((teams:any) => {
console.log('> teams', teams);
for (let i = 0; i < PersonList.length; i++) {
//update information
}
//console.log('> Final value: ', PersonList);
return PersonList;
})
);
}
But, I'm not finding a way to execute my SwitchMap x number of times and get the results back to use them in my map method to parse the information.
I just moved my SwitchMap to mergeMap, something like this:
mergeMap((wls: IWalklist[]) => {
//let allIds = wls.contact.map(id => this.getSingleData(id._id) );
let drops: Dropdown[] = [];
walklistList = [...wls];
const allIds = [...new Set(wls.map((f) => f.createdBy))];
return forkJoin(...allIds).pipe(
map((idDataArray) => {
drops.push(
this.teamPageService.getInformation('');
);
return drops;
})
)
}),
But still no luck.
Can some help me? how can I fix it?

Elasticsearch BulkAll with NEST: Maximum capacity exceeded

I'm using the following code to bulkindex documents. It works for everything except my Product model, but only when I try to index more than a few documents. If I do only 1 document it works fine. If I do 10, it fails. My Product model is not very complex, but it does have some nested documents with infinite self-referencing loops, but I did add ReferenceLoopHandling.Ignore to handle it.
public bool BulkIndex<T>(IEnumerable<T> items) where T : class
{
var waitHandle = new CountdownEvent(1);
var bulkAll = _client.BulkAll(items, b => b
.BackOffRetries(2)
.BackOffTime(TimeSpan.FromSeconds(5))
.RefreshOnCompleted(true)
.MaxDegreeOfParallelism(4)
.Size(100)
.Index(typeof(T).Name.ToLower())
);
bulkAll.Subscribe(new BulkAllObserver(
onNext: (b) => { Console.Write("."); },
onError: (e) => { throw e; },
onCompleted: () => waitHandle.Signal()
));
waitHandle.Wait();
return true;
}
new JsonNetSerializer(builtInSerializer, connectionSettings, () => new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
}))

How to return from within an observer?

I was trying to return filter function but return doesn't seem to work with callbacks. Here this.store.let(getIsPersonalized$) is an observable emitting boolean values and this.store.let(getPlayerSearchResults$) is an observable emiting objects of video class.
How do I run this synchronously, can I avoid asynchronus callback altogether given that I can't modify the observables received from store.
isPersonalized$ = this.store.let(getIsPersonalized$);
videos$ = this.store.let(getPlayerSearchResults$)
.map((vids) => this.myFilter(vids));
myFilter(vids) {
this.isPersonalized$.subscribe((x){
if(x){
return this.fileterX(vids);//Return from here
}
else {
return this.filterY(vids);//Or Return from here
}
});
}
fileterX(vids) {
return vids.filter((vid) => vids.views>100;);
}
fileterY(vids) {
return vids.filter((vid) => vids.views<20;);
}
I got it working this way, you don't need myFilter(vids) at all if you can get the branching out on isPersonalized$'s subscribe. Here is the updated code.
this.store.let(getIsPersonalized$);
videos$: Observable<any>;
ngOnInit() {
this.isPersonalized$.subscribe((x) => {
if (x) {
this.videos$ = this.store.let(getPlayerSearchResults$)
.map((vids) => this. fileterX(vids));
} else {
this.videos$ = this.store.let(getPlayerSearchResults$)
.map((vids) => this. fileterY(vids));
}
});
}
fileterX(vids) {
return vids.filter((vid) => vids.views>100;);
}
fileterY(vids) {
return vids.filter((vid) => vids.views<20;);
}
It looks like you want to evaluate the latest value of isPersonalized$ within the map function, i'd do that via withLatestFrom (Example: The first one toggles true/false every 5s, the second emits an increasing number every 1s):
const isPersonalized$ = Rx.Observable.interval(5000)
.map(value => value % 2 === 0);
const getPlayerSearchResults$ = Rx.Observable.interval(1000)
.withLatestFrom(isPersonalized$)
.map(bothValues => {
const searchResult = bothValues[0];
const isPersonalized = bothValues[1];
...
});

Wp7 & ReactiveOauth

I´m having trouble populating a list with the result from an API using ReactiveOauth for wp7.
It works when i´m populating the listbox directly. But I can´t figure out how to add the object to a list instead. The list is always empty when doing it like this .Subscribe(a => lista.Add(new Device ...
Any suggestions is very appreciated.
var token = TelldusWrapper.Security.GetToken();
var lista = new List<Device>();
var client = new OAuthClient(ConsumerKey, ConsumerSecret, token)
{
Url = "http://api.telldus.com/xml/devices/list",
Parameters = { { "supportedMethods", "TELLSTICK_TURNON" } }
};
client.GetResponseText().Select(s => XElement.Parse(s))
.SelectMany(x => x.Descendants("device"))
.Select(x => new
{
Text = x.Attribute("id").Value,
Name = x.Attribute("name").Value
})
.ObserveOnDispatcher()
.Subscribe(a => listbox.Items.Add(new Device { Id = a.Text, Name = a.Name }), ex => MessageBox.Show(ex.ToString()));
//.Subscribe(a => lista.Add(new Device { Id = a.Text, Name = a.Name }), ex => MessageBox.Show(ex.ToString()));

Resources