@@ -589,6 +589,222 @@ void main() {
589
589
});
590
590
});
591
591
592
+ group ('PerAccountStore edit-message methods' , () {
593
+ late PerAccountStore store;
594
+ late FakeApiConnection connection;
595
+ late StreamMessage message;
596
+
597
+ Future <void > prepare () async {
598
+ store = eg.store ();
599
+ connection = store.connection as FakeApiConnection ;
600
+ message = eg.streamMessage ();
601
+ await store.addMessage (message);
602
+ }
603
+
604
+ test ('smoke' , () => awaitFakeAsync ((async ) async {
605
+ await prepare ();
606
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
607
+
608
+ connection.prepare (
609
+ json: UpdateMessageResult ().toJson (), delay: Duration (seconds: 1 ));
610
+ store.editMessage (messageId: message.id, content: 'new content' );
611
+ check (connection.takeRequests ()).single.isA< http.Request > ()
612
+ ..method.equals ('PATCH' )
613
+ ..url.path.equals ('/api/v1/messages/${message .id }' )
614
+ ..bodyFields.deepEquals ({
615
+ 'content' : 'new content' ,
616
+ });
617
+
618
+ // Mid-request
619
+ async .elapse (Duration (milliseconds: 500 ));
620
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isFalse ();
621
+
622
+ // Request succeeded; event hasn't arrived
623
+ async .elapse (Duration (milliseconds: 500 ));
624
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isFalse ();
625
+
626
+ await store.handleEvent (eg.updateMessageEditEvent (message));
627
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
628
+ }));
629
+
630
+ test ('request fails' , () => awaitFakeAsync ((async ) async {
631
+ await prepare ();
632
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
633
+
634
+ connection.prepare (apiException: eg.apiBadRequest (), delay: Duration (seconds: 1 ));
635
+ store.editMessage (messageId: message.id, content: 'new content' );
636
+ async .elapse (Duration (seconds: 1 ));
637
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isTrue ();
638
+ }));
639
+
640
+ test ('request fails; take failed edit' , () => awaitFakeAsync ((async ) async {
641
+ await prepare ();
642
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
643
+
644
+ connection.prepare (apiException: eg.apiBadRequest (), delay: Duration (seconds: 1 ));
645
+ store.editMessage (messageId: message.id, content: 'new content' );
646
+ async .elapse (Duration (seconds: 1 ));
647
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isTrue ();
648
+
649
+ check (store.takeFailedMessageEdit (message.id)).equals ('new content' );
650
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
651
+ }));
652
+
653
+ test ('request failure after event arrival' , () => awaitFakeAsync ((async ) async {
654
+ // This can happen with network issues.
655
+
656
+ await prepare ();
657
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
658
+
659
+ connection.prepare (
660
+ httpException: const SocketException ('failed' ), delay: Duration (seconds: 1 ));
661
+ store.editMessage (messageId: message.id, content: 'new content' );
662
+
663
+ async .elapse (Duration (milliseconds: 500 ));
664
+ await store.handleEvent (eg.updateMessageEditEvent (message));
665
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
666
+
667
+ async .elapse (Duration (milliseconds: 500 ));
668
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
669
+ }));
670
+
671
+ test ('request failure before event arrival' , () => awaitFakeAsync ((async ) async {
672
+ // This can happen with network issues.
673
+
674
+ await prepare ();
675
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
676
+
677
+ connection.prepare (
678
+ httpException: const SocketException ('failed' ), delay: Duration (seconds: 1 ));
679
+ store.editMessage (messageId: message.id, content: 'new content' );
680
+
681
+ async .elapse (Duration (seconds: 1 ));
682
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isTrue ();
683
+
684
+ await store.handleEvent (eg.updateMessageEditEvent (message));
685
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
686
+ }));
687
+
688
+ test ('request failure before event arrival; take failed edit in between' , () => awaitFakeAsync ((async ) async {
689
+ // This can happen with network issues.
690
+
691
+ await prepare ();
692
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
693
+
694
+ connection.prepare (
695
+ httpException: const SocketException ('failed' ), delay: Duration (seconds: 1 ));
696
+ store.editMessage (messageId: message.id, content: 'new content' );
697
+
698
+ async .elapse (Duration (seconds: 1 ));
699
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isTrue ();
700
+ check (store.takeFailedMessageEdit (message.id)).equals ('new content' );
701
+
702
+ await store.handleEvent (eg.updateMessageEditEvent (message));
703
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
704
+ }));
705
+
706
+ // TODO tests with delete-message event
707
+
708
+ group ('takeFailedMessageEdit throws StateError' , () {
709
+ test ('if called before request starts' , () => awaitFakeAsync ((async ) async {
710
+ await prepare ();
711
+ check (() => store.takeFailedMessageEdit (message.id)).throws <StateError >();
712
+ }));
713
+
714
+ test ('if called before request failure' , () => awaitFakeAsync ((async ) async {
715
+ await prepare ();
716
+ connection.prepare (
717
+ apiException: eg.apiBadRequest (), delay: Duration (seconds: 1 ));
718
+ store.editMessage (messageId: message.id, content: 'new content' );
719
+
720
+ async .elapse (Duration (milliseconds: 500 ));
721
+ check (() => store.takeFailedMessageEdit (message.id)).throws <StateError >();
722
+ async .flushTimers ();
723
+ }));
724
+
725
+ test ('if called after request success' , () => awaitFakeAsync ((async ) async {
726
+ await prepare ();
727
+ connection.prepare (
728
+ json: UpdateMessageResult ().toJson (), delay: Duration (seconds: 1 ));
729
+ store.editMessage (messageId: message.id, content: 'new content' );
730
+
731
+ async .elapse (Duration (seconds: 1 ));
732
+ check (() => store.takeFailedMessageEdit (message.id)).throws <StateError >();
733
+ }));
734
+
735
+ test ('if called after request success and event arrival' , () => awaitFakeAsync ((async ) async {
736
+ await prepare ();
737
+ connection.prepare (
738
+ json: UpdateMessageResult ().toJson (), delay: Duration (seconds: 1 ));
739
+ store.editMessage (messageId: message.id, content: 'new content' );
740
+
741
+ async .elapse (Duration (seconds: 1 ));
742
+ await store.handleEvent (eg.updateMessageEditEvent (message));
743
+ check (() => store.takeFailedMessageEdit (message.id)).throws <StateError >();
744
+ }));
745
+
746
+ test ('if called after event arrival and request failure' , () => awaitFakeAsync ((async ) async {
747
+ // This can happen with network issues.
748
+ await prepare ();
749
+ connection.prepare (
750
+ httpException: const SocketException ('failed' ), delay: Duration (seconds: 1 ));
751
+ store.editMessage (messageId: message.id, content: 'new content' );
752
+
753
+ async .elapse (Duration (seconds: 1 ));
754
+ await store.handleEvent (eg.updateMessageEditEvent (message));
755
+ check (() => store.takeFailedMessageEdit (message.id)).throws <StateError >();
756
+ }));
757
+
758
+ test ('if called after request failure and event arrival' , () => awaitFakeAsync ((async ) async {
759
+ // This can happen with network issues.
760
+ await prepare ();
761
+ connection.prepare (
762
+ httpException: const SocketException ('failed' ), delay: Duration (seconds: 1 ));
763
+ store.editMessage (messageId: message.id, content: 'new content' );
764
+
765
+ async .elapse (Duration (milliseconds: 500 ));
766
+ await store.handleEvent (eg.updateMessageEditEvent (message));
767
+
768
+ async .elapse (Duration (milliseconds: 500 ));
769
+ check (() => store.takeFailedMessageEdit (message.id)).throws <StateError >();
770
+ }));
771
+
772
+ test ('if called after message-delete event and request failure' , () => awaitFakeAsync ((async ) async {
773
+ await prepare ();
774
+ connection.prepare (
775
+ httpException: const SocketException ('failed' ), delay: Duration (seconds: 1 ));
776
+ store.editMessage (messageId: message.id, content: 'new content' );
777
+
778
+ async .elapse (Duration (seconds: 1 ));
779
+ await store.handleEvent (eg.deleteMessageEvent ([message]));
780
+ check (() => store.takeFailedMessageEdit (message.id)).throws <StateError >();
781
+ }));
782
+
783
+ test ('if called after request failure and message-delete event' , () => awaitFakeAsync ((async ) async {
784
+ await prepare ();
785
+ connection.prepare (
786
+ httpException: const SocketException ('failed' ), delay: Duration (seconds: 1 ));
787
+ store.editMessage (messageId: message.id, content: 'new content' );
788
+
789
+ async .elapse (Duration (milliseconds: 500 ));
790
+ await store.handleEvent (eg.deleteMessageEvent ([message]));
791
+
792
+ async .elapse (Duration (milliseconds: 500 ));
793
+ check (() => store.takeFailedMessageEdit (message.id)).throws <StateError >();
794
+ }));
795
+
796
+ test ('if called a second time for the same failed request' , () => awaitFakeAsync ((async ) async {
797
+ await prepare ();
798
+ connection.prepare (
799
+ apiException: eg.apiBadRequest (), delay: Duration (seconds: 1 ));
800
+ store.editMessage (messageId: message.id, content: 'new content' );
801
+ async .elapse (Duration (seconds: 1 ));
802
+ check (store.takeFailedMessageEdit (message.id)).equals ('new content' );
803
+ check (() => store.takeFailedMessageEdit (message.id)).throws <StateError >();
804
+ }));
805
+ });
806
+ });
807
+
592
808
group ('UpdateMachine.load' , () {
593
809
late TestGlobalStore globalStore;
594
810
late FakeApiConnection connection;
0 commit comments