public void Load_with_missing_due_card() { var es = new InMemoryEventstore(); es.Record(new Event[] { new NewCardEncountered { Question = "q3", Answer = "a3", Tags = "t3", Id = "3" }, new CardMovedTo { CardId = "3", BinIndex = 3 }, new DueCardSelected { CardId = "3" }, new CardFoundMissing { CardId = "3" }, }); var sut = new DueCardContextManager(es); var result = sut.Load(new DueCardQuery()).Ctx as DueCardContextModel; result.DueCard.Should().BeNull(); }
public void Load_with_empty_bin() { var es = new InMemoryEventstore(); var sut = new SelectDueCardContextManager(es); es.Record(new Event[] { new CardMovedTo { CardId = "1", BinIndex = 1 }, new CardMovedTo { CardId = "3", BinIndex = 3 }, }); var result = sut.Load(new SelectDueCardCommand()).Ctx as SelectDueCardContextModel; result.Should().BeEquivalentTo(new SelectDueCardContextModel { Bins = new[] { new string[0], new[] { "1" }, new string[0], new[] { "3" } }, DueBinIndex = -1, Config = null }); }
public void Load() { var es = new InMemoryEventstore(); es.Record(new Event[] { new CardMovedTo { CardId = "1", BinIndex = 0 }, new CardMovedTo { CardId = "x", BinIndex = 1 }, new CardMovedTo { CardId = "2", BinIndex = 2 }, new CardMovedTo { CardId = "1", BinIndex = 1 }, new CardFoundMissing { CardId = "x" } }); var sut = new RegisterAnswerContextManager(es); var result = sut.Load(new RegisterAnswerCommand()).Ctx as RegisterAnswerContextModel; result.CardsInBins.Should().BeEquivalentTo(new Dictionary <string, int> { { "1", 1 }, { "2", 2 } }); }
public void Load_with_no_due_card_because_it_was_moved() { var es = new InMemoryEventstore(); es.Record(new Event[] { new NewCardEncountered { Question = "q1", Answer = "a1", Tags = "t1", Id = "1" }, new CardMovedTo { CardId = "1", BinIndex = 1 }, new DueCardSelected { CardId = "1" }, new CardMovedTo { CardId = "1", BinIndex = 2 }, }); var sut = new DueCardContextManager(es); var result = sut.Load(new DueCardQuery()).Ctx as DueCardContextModel; result.DueCard.Should().BeNull(); }
public void Load() { var es = new InMemoryEventstore(); es.Record(new Event[] { new NewCardEncountered { Question = "q1", Answer = "a1", Tags = "t1", Id = "1" }, new CardMovedTo { CardId = "1", BinIndex = 1 }, new DueCardSelected { CardId = "1" }, new NewCardEncountered { Question = "q2", Answer = "a2", Tags = "t2", Id = "2" }, new CardMovedTo { CardId = "2", BinIndex = 2 }, new DueCardSelected { CardId = "2" }, new NewCardEncountered { Question = "q3", Answer = "a3", Tags = "t3", Id = "3" }, new CardMovedTo { CardId = "3", BinIndex = 3 }, new DueCardSelected { CardId = "3" }, new CardFoundMissing { CardId = "3" }, new DueCardSelected { CardId = "2" }, new CardWasChanged { CardId = "2", Question = "q2v2", Answer = "a2v2", Tags = "t2v2" }, }); var sut = new DueCardContextManager(es); var result = sut.Load(new DueCardQuery()).Ctx as DueCardContextModel; result.DueCard.Should().BeEquivalentTo(new DueCardFoundQueryResult { CardId = "2", Question = "q2v2", Answer = "a2v2", Tags = "t2v2", BinIndex = 2 }); }
public static void Main(string[] args) { var es = new InMemoryEventstore(); es.Record(new A()); es.Record(new C()); es.Record(new B()); nsimpleeventstore.adapters.EventArchive.Write("myarchive.json", es.Replay().Events); var events = EventArchive.Read("myarchive.json"); var es2 = new InMemoryEventstore(events); Console.WriteLine(es2.Replay().Events.Length); }
public void InMemoryEventstore_acceptance_test() { using (var sut = new InMemoryEventstore()) { Event e0 = new TodoAdded("do dishes"); sut.Record(e0); sut.Record(new TodoAdded("walk dog")); Event e2 = new TodoAdded("write report"); sut.Record(new Event[] { e2, new TodoCategorized(e2.Id, "work") }); sut.Record(new TodoDone(e0.Id)); var result = sut.Replay(); var todos = result.Events.Aggregate(new Dictionary <string, ToDoItem>(), Map); Assert.Equal(2, todos.Count); Assert.Equal("write report", todos[e2.Id].Subject); Assert.Contains("work", todos[e2.Id].Categories); Dictionary <string, ToDoItem> Map(Dictionary <string, ToDoItem> items, Event e) { switch (e) { case TodoAdded a: items[a.Id] = new ToDoItem { Id = a.Id, Subject = a.Subject }; break; case TodoDone d: items.Remove(d.EntityId); break; case TodoCategorized c: foreach (var cat in c.Categories) { items[c.EntityId].Categories.Add(cat); } break; } return(items); } } }
public void Initialize_record_and_replay() { var sut = new InMemoryEventstore(new[] { new TestEvent { Text = "foo" }, new TestEvent { Text = "bar" } }); sut.Record(new TestEvent { Text = "baz" }); var result = sut.Replay().Events.Select(e => (e as TestEvent).Text); Assert.Equal(new[] { "foo", "bar", "baz" }, result); }
public void Load_without_cards() { var es = new InMemoryEventstore(); var sut = new SelectDueCardContextManager(es); es.Record(new Event[] { }); var result = sut.Load(new SelectDueCardCommand()).Ctx as SelectDueCardContextModel; result.Should().BeEquivalentTo(new SelectDueCardContextModel { Bins = new[] { new string[0], // bin 0 and new string[0] // bin 1 always get created }, DueBinIndex = -1, Config = null }); }
public void Load_message_context() { var es = new InMemoryEventstore(); es.Record(new Event[] { new BoxConfigured { Bins = new[] { new BoxConfigured.Bin { LowerDueThreshold = 1, UpperDueThreshold = 2 } } }, new NewCardEncountered { Question = "q1", Answer = "a1", Tags = "t1", Id = "1" }, new CardMovedTo { CardId = "1", BinIndex = 0 }, new NewCardEncountered { Question = "q2", Answer = "a2", Tags = "t2,t3", Id = "2" }, new CardMovedTo { CardId = "2", BinIndex = 3 }, new NewCardEncountered { Question = "q3", Answer = "a3", Tags = "", Id = "3" }, new CardWasChanged { Question = "q1v2", Answer = "a1v2", Tags = "t1", CardId = "1" }, new CardMovedTo { CardId = "1", BinIndex = 2 }, new CardFoundMissing { CardId = "3" }, new BoxConfigured { Bins = new[] { new BoxConfigured.Bin { LowerDueThreshold = 10, UpperDueThreshold = 20 } } } }); var sut = new SyncContextManagement(es); var result = sut.Load(new SyncCommand()); var ctxModel = result.Ctx as SyncContextModel; ctxModel.Config.Should().BeEquivalentTo(new FlashcardboxConfig { Bins = new[] { new FlashcardboxConfig.Bin { LowerDueThreshold = 10, UpperDueThreshold = 20 } } }); ctxModel.Flashcards.Should().BeEquivalentTo(new Dictionary <string, (string binIndex, string hash)> { { "1", ("2", FlashcardHash.Calculate("q1v2", "a1v2", "t1")) }, { "2", ("3", FlashcardHash.Calculate("q2", "a2", "t2,t3")) } });
public void Load() { var es = new InMemoryEventstore(); var sut = new SelectDueCardContextManager(es); es.Record(new Event[] { new BoxConfigured { Bins = new[] { new BoxConfigured.Bin { LowerDueThreshold = 10, UpperDueThreshold = 20 }, } }, new CardMovedTo { CardId = "1", BinIndex = 1 }, new CardMovedTo { CardId = "2.1", BinIndex = 2 }, new CardMovedTo { CardId = "2.2", BinIndex = 1 }, new DueCardSelected { CardId = "2.2", BinIndex = 1 }, new CardMovedTo { CardId = "3.1", BinIndex = 3 }, new CardMovedTo { CardId = "3.2", BinIndex = 3 }, new CardMovedTo { CardId = "3.3", BinIndex = 1 }, new CardMovedTo { CardId = "3.x", BinIndex = 3 }, new CardMovedTo { CardId = "2.2", BinIndex = 2 }, new CardFoundMissing { CardId = "3.x" }, new CardMovedTo { CardId = "3.3", BinIndex = 3 }, new DueCardSelected { CardId = "3.3", BinIndex = 3 } }); var result = sut.Load(new SelectDueCardCommand()).Ctx as SelectDueCardContextModel; result.Should().BeEquivalentTo(new SelectDueCardContextModel { Bins = new[] { new string[0], new[] { "1" }, new[] { "2.1", "2.2" }, new[] { "3.1", "3.2", "3.3" } }, DueBinIndex = 3, Config = new FlashcardboxConfig { Bins = new [] { new FlashcardboxConfig.Bin { LowerDueThreshold = 10, UpperDueThreshold = 20 } } } }); }
public void Some_cards_are_not_memorized_right_away() { const string DBPATH = "sampledb_acceptance"; File.Copy(Path.Combine(DBPATH, "flashcards v1.csv"), Path.Combine(DBPATH, "flashcards.csv"), true); var db = new FlashcardboxDb(DBPATH); var es = new InMemoryEventstore(); var mh = new MessageHandling(es, db); // box initialized var status = mh.Handle(new SyncCommand()); // 0:a12bcde3fg,1:,2:,3: Memorized("a"); //0:cde3fg,1:12b*,2:a,3: Memorized("1"); //0:cde3fg,1:2b*,2:a1,3: Forgotten("2"); //0:cde3fg,1:b2*,2:a1,3: Memorized("b"); //0:cde3fg,1:2*,2:a1b,3: Memorized("2"); //0:cde3fg,1:2*,2:a1b,3: -> 0:3fg,1:2cde*,2:a1b,3: -> 0:3fg,1:cde*,2:a1b2,3: Forgotten("c"); //0:3fg,1:dec*,2:a1b2,3: Memorized("d"); //0:3fg,1:ec*,2:a1b2d,3: Memorized("e"); //0:3fg,1:c*,2:a1b2de,3: Memorized("a"); //0:3fg,1:c*,2:1b2de*,3:a Forgotten("1"); //0:3fg,1:c1,2:b2de*,3:a Forgotten("b"); //0:3fg,1:c1b,2:2de*,3:a Memorized("2"); //0:3fg,1:c1b,2:de*,3:a2 Memorized("c"); //0:3fg,1:c1b,2:de*,3:a2 -> 0:fg,1:c1b3,2:de*,3:a2 -> 0:fg,1:1b3*,2:dec,3:a2 Memorized("1"); //0:fg,1:b3*,2:dec1,3:a2 Memorized("b"); //0:fg,1:3*,2:dec1b,3:a2 Forgotten("d"); //0:fg,1:3d,2:ec1b*,3:a2 Memorized("e"); //0:fg,1:3d,2:c1b*,3:a2e Memorized("c"); //0:fg,1:3d,2:1b*,3:a2ec Memorized("3"); //0:,1:dfg*,2:1b3,3:a2ec Memorized("d"); //0:,1:fg*,2:1b3d,3:a2ec Memorized("f"); //0:,1:g*,2:1b3df,3:a2ec Forgotten("1"); //0:,1:g1,2:b3df*,3:a2ec Memorized("b"); //0:,1:g1,2:3df*,3:a2ecb Memorized("3"); //0:,1:g1,2:df*,3:a2ecb3 Memorized("g"); //0:,1:1*,2:dfg,3:a2ecb3 Memorized("1"); //0:,1:*,2:dfg1,3:a2ecb3 Memorized("d"); //0:,1:,2:fg1*,3:a2ecb3d Forgotten("f"); //0:,1:f,2:g1*,3:a2ecb3d Memorized("f"); //0:,1:*,2:g1f*,3:a2ecb3d Memorized("g"); //0:,1:*,2:1f*,3:a2ecb3dg Memorized("1"); //0:,1:*,2:f*,3:a2ecb3dg1 Memorized("f"); //0:,1:*,2:*,3:a2ecb3dg1f mh.Handle(new SelectDueCardCommand()).Should().BeOfType <Failure>(); void Memorized(string question) { mh.Handle(new SelectDueCardCommand()); var card = mh.Handle(new DueCardQuery()) as DueCardFoundQueryResult; mh.Handle(new RegisterAnswerCommand { CardId = card.CardId, CorrectlyAnswered = true }); card.Question.Should().Be(question); } void Forgotten(string question) { mh.Handle(new SelectDueCardCommand()); var card = mh.Handle(new DueCardQuery()) as DueCardFoundQueryResult; mh.Handle(new RegisterAnswerCommand { CardId = card.CardId, CorrectlyAnswered = false }); card.Question.Should().Be(question); } }
public void Cards_only_move_forward() { const string DBPATH = "sampledb_acceptance"; File.Copy(Path.Combine(DBPATH, "flashcards v1.csv"), Path.Combine(DBPATH, "flashcards.csv"), true); var db = new FlashcardboxDb(DBPATH); var es = new InMemoryEventstore(); var mh = new MessageHandling(es, db); // box not yet initialized var progress = mh.Handle(new ProgressQuery()); progress.Should().BeEquivalentTo(new ProgressQueryResult { Bins = new[] { new ProgressQueryResult.Bin(), new ProgressQueryResult.Bin() } }); // box initialized var status = mh.Handle(new SyncCommand()); status.Should().BeOfType <SyncSuccess>(); progress = mh.Handle(new ProgressQuery()); progress.Should().BeEquivalentTo(new ProgressQueryResult { Bins = new[] { new ProgressQueryResult.Bin { Count = 10 }, new ProgressQueryResult.Bin { LowerDueThreshold = 2, UpperDueThreshold = 4 }, new ProgressQueryResult.Bin { LowerDueThreshold = 3, UpperDueThreshold = 5 }, new ProgressQueryResult.Bin() } }); // learn first card status = mh.Handle(new SelectDueCardCommand()); status.Should().BeOfType <Success>(); progress = mh.Handle(new ProgressQuery()); progress.Should().BeEquivalentTo(new ProgressQueryResult { Bins = new[] { new ProgressQueryResult.Bin { Count = 6 }, new ProgressQueryResult.Bin { Count = 4, LowerDueThreshold = 2, UpperDueThreshold = 4, IsDue = true }, new ProgressQueryResult.Bin { LowerDueThreshold = 3, UpperDueThreshold = 5 }, new ProgressQueryResult.Bin() } }); var card = mh.Handle(new DueCardQuery()) as DueCardFoundQueryResult; card.Question.Should().Be("a"); status = mh.Handle(new RegisterAnswerCommand { CardId = card.CardId, CorrectlyAnswered = true }); status.Should().BeOfType <Success>(); progress = mh.Handle(new ProgressQuery()); progress.Should().BeEquivalentTo(new ProgressQueryResult { Bins = new[] { new ProgressQueryResult.Bin { Count = 6 }, new ProgressQueryResult.Bin { Count = 3, LowerDueThreshold = 2, UpperDueThreshold = 4, IsDue = true }, new ProgressQueryResult.Bin { Count = 1, LowerDueThreshold = 3, UpperDueThreshold = 5 }, new ProgressQueryResult.Bin() } }); // learn second card mh.Handle(new SelectDueCardCommand()); card = mh.Handle(new DueCardQuery()) as DueCardFoundQueryResult; card.Question.Should().Be("1"); mh.Handle(new RegisterAnswerCommand { CardId = card.CardId, CorrectlyAnswered = true }); progress = mh.Handle(new ProgressQuery()); progress.Should().BeEquivalentTo(new ProgressQueryResult { Bins = new[] { new ProgressQueryResult.Bin { Count = 6 }, new ProgressQueryResult.Bin { Count = 2, LowerDueThreshold = 2, UpperDueThreshold = 4, IsDue = true }, new ProgressQueryResult.Bin { Count = 2, LowerDueThreshold = 3, UpperDueThreshold = 5 }, new ProgressQueryResult.Bin() } }); // learn card 3 mh.Handle(new SelectDueCardCommand()); card = mh.Handle(new DueCardQuery()) as DueCardFoundQueryResult; card.Question.Should().Be("2"); mh.Handle(new RegisterAnswerCommand { CardId = card.CardId, CorrectlyAnswered = true }); progress = mh.Handle(new ProgressQuery()); progress.Should().BeEquivalentTo(new ProgressQueryResult { Bins = new[] { new ProgressQueryResult.Bin { Count = 6 }, new ProgressQueryResult.Bin { Count = 1, LowerDueThreshold = 2, UpperDueThreshold = 4, IsDue = true }, new ProgressQueryResult.Bin { Count = 3, LowerDueThreshold = 3, UpperDueThreshold = 5 }, new ProgressQueryResult.Bin() } }); // learning card 4 first leads to a refill of bin 1 mh.Handle(new SelectDueCardCommand()); card = mh.Handle(new DueCardQuery()) as DueCardFoundQueryResult; card.Question.Should().Be("b"); mh.Handle(new RegisterAnswerCommand { CardId = card.CardId, CorrectlyAnswered = true }); progress = mh.Handle(new ProgressQuery()); progress.Should().BeEquivalentTo(new ProgressQueryResult { Bins = new[] { new ProgressQueryResult.Bin { Count = 3 }, new ProgressQueryResult.Bin { Count = 3, LowerDueThreshold = 2, UpperDueThreshold = 4, IsDue = true }, new ProgressQueryResult.Bin { Count = 4, LowerDueThreshold = 3, UpperDueThreshold = 5 }, new ProgressQueryResult.Bin() } }); // learning card 5 fills up bin 2... mh.Handle(new SelectDueCardCommand()); card = mh.Handle(new DueCardQuery()) as DueCardFoundQueryResult; card.Question.Should().Be("c"); mh.Handle(new RegisterAnswerCommand { CardId = card.CardId, CorrectlyAnswered = true }); // ...but bin 1 stays due because its lower threshold hasn't been reached mh.Handle(new SelectDueCardCommand()); card = mh.Handle(new DueCardQuery()) as DueCardFoundQueryResult; card.Question.Should().Be("d"); mh.Handle(new RegisterAnswerCommand { CardId = card.CardId, CorrectlyAnswered = true }); progress = mh.Handle(new ProgressQuery()); progress.Should().BeEquivalentTo(new ProgressQueryResult { Bins = new[] { new ProgressQueryResult.Bin { Count = 3 }, new ProgressQueryResult.Bin { Count = 1, LowerDueThreshold = 2, UpperDueThreshold = 4, IsDue = true }, new ProgressQueryResult.Bin { Count = 6, LowerDueThreshold = 3, UpperDueThreshold = 5 }, new ProgressQueryResult.Bin() } }); // this only changes now! mh.Handle(new SelectDueCardCommand()); card = mh.Handle(new DueCardQuery()) as DueCardFoundQueryResult; card.Question.Should().Be("a"); mh.Handle(new RegisterAnswerCommand { CardId = card.CardId, CorrectlyAnswered = true }); progress = mh.Handle(new ProgressQuery()); progress.Should().BeEquivalentTo(new ProgressQueryResult { Bins = new[] { new ProgressQueryResult.Bin { Count = 3 }, new ProgressQueryResult.Bin { Count = 1, LowerDueThreshold = 2, UpperDueThreshold = 4, }, new ProgressQueryResult.Bin { Count = 5, LowerDueThreshold = 3, UpperDueThreshold = 5, IsDue = true }, new ProgressQueryResult.Bin { Count = 1 } } }); // learn all cards: // 1(3,1,4,2), 2(3,1,3,3), b(3,1,2,4), e(0,3,4,3), 3(0,2,5,3), f(0,1-g,5-cde3f,4-a12b) // c(0,1-g,5-de3f,4-a12bc), d(0,1-g,3-e3f,5-a12bcd), e(0,1-g,2-3f,6-a12bcde) // g(0,0,3-3fg,7-a12bcde), 3(0,0,2-fg,8-a12bcde3), f(0,0,1-g,9-a12bcde3f), g(0,0,0,10-a12bcde3fg) foreach (var q in "12be3fcdeg3fg".ToCharArray()) { mh.Handle(new SelectDueCardCommand()); card = mh.Handle(new DueCardQuery()) as DueCardFoundQueryResult; mh.Handle(new RegisterAnswerCommand { CardId = card.CardId, CorrectlyAnswered = true }); card.Question.Should().Be(q.ToString()); } mh.Handle(new SelectDueCardCommand()).Should().BeOfType <Failure>(); }
public void Fluent() { var es = new InMemoryEventstore(); var sut = new MessagePump(es); sut.On <AddTodoCmd>().Use(new AddTodoCmdCtxModelManager()).Do(new AddTodoCmdProcessor()); sut.On <AllTodosQuery>().Use(new AllTodosQueryCtxModelManager()).Do(new AllTodosQueryProcessor()); var checkToDoCtxModelManager = new CheckTodoCmdCtxModelManager(); sut.On <CheckTodoCmd>().Load(checkToDoCtxModelManager).Finally(checkToDoCtxModelManager).Do(new CheckTodoCmdProcessor()); // add a couple of tasks var response = sut.Handle(new AddTodoCmd { Subject = "foo" }); Assert.IsType <Success>(response.Msg); Assert.Empty(response.Notifications); response = sut.Handle(new AddTodoCmd { Subject = "bar" }); Assert.IsType <Success>(response.Msg); Assert.Empty(response.Notifications); response = sut.Handle(new AddTodoCmd { Subject = "baz" }); Assert.IsType <Success>(response.Msg); Assert.Empty(response.Notifications); // what are the currently active tasks? response = sut.Handle(new AllTodosQuery()); Assert.Empty(response.Notifications); var result = response.Msg as AllTodosQueryResult; Assert.Equal(new[] { "foo", "bar", "baz" }, result.Todos.Select(x => x.Description)); // check a task as done response = sut.Handle(new CheckTodoCmd { Id = result.Todos[0].Id }); Assert.IsType <Success>(response.Msg); Assert.Empty(response.Notifications); // ...and it still gets listed response = sut.Handle(new AllTodosQuery()); Assert.Empty(response.Notifications); result = response.Msg as AllTodosQueryResult; Assert.Equal(new[] { "foo, done", "bar", "baz" }, result.Todos.Select(x => x.Description)); // ...unless done tasks are explicitly excluded response = sut.Handle(new AllTodosQuery { ActiveOnly = true }); Assert.Empty(response.Notifications); result = response.Msg as AllTodosQueryResult; Assert.Equal(new[] { "bar", "baz" }, result.Todos.Select(x => x.Description)); // check all remaining tasks - and a notification gets created, once all are done sut.Handle(new CheckTodoCmd { Id = result.Todos[0].Id }); response = sut.Handle(new CheckTodoCmd { Id = result.Todos[1].Id }); Assert.IsType <Success>(response.Msg); Assert.Single(response.Notifications); Assert.IsType <FreeCapacityAvailableNotification>(response.Notifications[0]); }