@@ -2958,6 +2958,181 @@ func assertNoBlockHeaders[BlockHeader any](t *testing.T, blockHeadersChan <-chan
29582958 }
29592959}
29602960
2961+ func TestClient_SubscribeBlockDigest (t * testing.T ) {
2962+ blockHeaders := test .BlockHeaderGenerator ()
2963+
2964+ generateBlockDigestResponses := func (count uint64 ) []* access.SubscribeBlockDigestsResponse {
2965+ var resBlockDigests []* access.SubscribeBlockDigestsResponse
2966+
2967+ for i := uint64 (0 ); i < count ; i ++ {
2968+ blockHeader := blockHeaders .New ()
2969+
2970+ digest := flow.BlockDigest {
2971+ BlockID : blockHeader .ID ,
2972+ Height : blockHeader .Height ,
2973+ Timestamp : blockHeader .Timestamp ,
2974+ }
2975+
2976+ resBlockDigests = append (resBlockDigests , convert .BlockDigestToMessage (digest ))
2977+ }
2978+
2979+ return resBlockDigests
2980+ }
2981+
2982+ t .Run ("Happy Path - from start height" , clientTest (func (t * testing.T , ctx context.Context , rpc * mocks.MockRPCClient , c * BaseClient ) {
2983+ startHeight := uint64 (1 )
2984+ responseCount := uint64 (100 )
2985+
2986+ ctx , cancel := context .WithCancel (ctx )
2987+ stream := & mockBlockDigestClientStream [access.SubscribeBlockDigestsResponse ]{
2988+ ctx : ctx ,
2989+ responses : generateBlockDigestResponses (responseCount ),
2990+ }
2991+
2992+ rpc .
2993+ On ("SubscribeBlockDigestsFromStartHeight" , ctx , mock .Anything ).
2994+ Return (stream , nil )
2995+
2996+ blockDigestsCh , errCh , err := c .SubscribeBlockDigestsFromStartHeight (ctx , startHeight , flow .BlockStatusSealed )
2997+ require .NoError (t , err )
2998+
2999+ wg := sync.WaitGroup {}
3000+ wg .Add (1 )
3001+ go assertNoErrors (t , errCh , wg .Done )
3002+
3003+ for i := uint64 (0 ); i < responseCount ; i ++ {
3004+ actualDigest := <- blockDigestsCh
3005+ expectedDigest := convert .MessageToBlockDigest (stream .responses [i ])
3006+ require .Equal (t , expectedDigest , actualDigest )
3007+ }
3008+ cancel ()
3009+
3010+ wg .Wait ()
3011+ }))
3012+
3013+ t .Run ("Happy Path - from start block id" , clientTest (func (t * testing.T , ctx context.Context , rpc * mocks.MockRPCClient , c * BaseClient ) {
3014+ responseCount := uint64 (100 )
3015+
3016+ ctx , cancel := context .WithCancel (ctx )
3017+ stream := & mockBlockDigestClientStream [access.SubscribeBlockDigestsResponse ]{
3018+ ctx : ctx ,
3019+ responses : generateBlockDigestResponses (responseCount ),
3020+ }
3021+
3022+ rpc .
3023+ On ("SubscribeBlockDigestsFromStartBlockID" , ctx , mock .Anything ).
3024+ Return (stream , nil )
3025+
3026+ startBlockID := convert .MessageToIdentifier (stream .responses [0 ].BlockId )
3027+ blockDigestsCh , errCh , err := c .SubscribeBlockDigestsFromStartBlockID (ctx , startBlockID , flow .BlockStatusSealed )
3028+ require .NoError (t , err )
3029+
3030+ wg := sync.WaitGroup {}
3031+ wg .Add (1 )
3032+ go assertNoErrors (t , errCh , wg .Done )
3033+
3034+ for i := uint64 (0 ); i < responseCount ; i ++ {
3035+ actualDigest := <- blockDigestsCh
3036+ expectedDigest := convert .MessageToBlockDigest (stream .responses [i ])
3037+ require .Equal (t , expectedDigest , actualDigest )
3038+ }
3039+ cancel ()
3040+
3041+ wg .Wait ()
3042+ }))
3043+
3044+ t .Run ("Happy Path - from latest" , clientTest (func (t * testing.T , ctx context.Context , rpc * mocks.MockRPCClient , c * BaseClient ) {
3045+ responseCount := uint64 (100 )
3046+
3047+ ctx , cancel := context .WithCancel (ctx )
3048+ stream := & mockBlockDigestClientStream [access.SubscribeBlockDigestsResponse ]{
3049+ ctx : ctx ,
3050+ responses : generateBlockDigestResponses (responseCount ),
3051+ }
3052+
3053+ rpc .
3054+ On ("SubscribeBlockDigestsFromLatest" , ctx , mock .Anything ).
3055+ Return (stream , nil )
3056+
3057+ blockDigestsCh , errCh , err := c .SubscribeBlockDigestsFromLatest (ctx , flow .BlockStatusSealed )
3058+ require .NoError (t , err )
3059+
3060+ wg := sync.WaitGroup {}
3061+ wg .Add (1 )
3062+ go assertNoErrors (t , errCh , wg .Done )
3063+
3064+ for i := uint64 (0 ); i < responseCount ; i ++ {
3065+ actualDigest := <- blockDigestsCh
3066+ expectedDigest := convert .MessageToBlockDigest (stream .responses [i ])
3067+ require .Equal (t , expectedDigest , actualDigest )
3068+ }
3069+ cancel ()
3070+
3071+ wg .Wait ()
3072+ }))
3073+
3074+ t .Run ("Stream returns error" , clientTest (func (t * testing.T , ctx context.Context , rpc * mocks.MockRPCClient , c * BaseClient ) {
3075+ ctx , cancel := context .WithCancel (ctx )
3076+ stream := & mockBlockDigestClientStream [access.SubscribeBlockDigestsResponse ]{
3077+ ctx : ctx ,
3078+ err : status .Error (codes .Internal , "internal error" ),
3079+ }
3080+
3081+ rpc .
3082+ On ("SubscribeBlockDigestsFromLatest" , ctx , mock .Anything ).
3083+ Return (stream , nil )
3084+
3085+ blockDigestsCh , errCh , err := c .SubscribeBlockDigestsFromLatest (ctx , flow .BlockStatusSealed )
3086+ require .NoError (t , err )
3087+
3088+ wg := sync.WaitGroup {}
3089+ wg .Add (1 )
3090+ go assertNoBlockDigests (t , blockDigestsCh , wg .Done )
3091+
3092+ errorCount := 0
3093+ for e := range errCh {
3094+ require .Error (t , e )
3095+ require .ErrorIs (t , e , stream .err )
3096+ errorCount += 1
3097+ }
3098+ cancel ()
3099+
3100+ require .Equalf (t , 1 , errorCount , "only 1 error is expected" )
3101+
3102+ wg .Wait ()
3103+ }))
3104+ }
3105+
3106+ type mockBlockDigestClientStream [SubscribeBlockDigestsResponse any ] struct {
3107+ grpc.ClientStream
3108+
3109+ ctx context.Context
3110+ err error
3111+ offset int
3112+ responses []* SubscribeBlockDigestsResponse
3113+ }
3114+
3115+ func (s * mockBlockDigestClientStream [SubscribeBlockDigestsResponse ]) Recv () (* SubscribeBlockDigestsResponse , error ) {
3116+ if s .err != nil {
3117+ return nil , s .err
3118+ }
3119+
3120+ if s .offset >= len (s .responses ) {
3121+ <- s .ctx .Done ()
3122+ return nil , io .EOF
3123+ }
3124+ defer func () { s .offset ++ }()
3125+
3126+ return s .responses [s .offset ], nil
3127+ }
3128+
3129+ func assertNoBlockDigests [BlockDigest any ](t * testing.T , blockDigestsChan <- chan BlockDigest , done func ()) {
3130+ defer done ()
3131+ for range blockDigestsChan {
3132+ require .FailNow (t , "should not receive block digests" )
3133+ }
3134+ }
3135+
29613136type mockAccountStatutesClientStream struct {
29623137 grpc.ClientStream
29633138
0 commit comments