5252 async {
5353 let execute = || async {
5454 let receipt = receipt. ok_or ( IndexerServiceError :: ReceiptNotFound ) ?;
55-
5655 // Verify the receipt and store it in the database
5756 tap_manager
5857 . verify_and_store_receipt ( & ctx. unwrap_or_default ( ) , receipt)
7372
7473#[ cfg( test) ]
7574mod tests {
75+
76+ use core:: panic;
77+ use rstest:: * ;
7678 use std:: { sync:: Arc , time:: Duration } ;
79+ use tokio:: time:: sleep;
80+ use tower:: { Service , ServiceBuilder , ServiceExt } ;
7781
7882 use alloy:: primitives:: { address, Address } ;
7983 use axum:: {
@@ -92,7 +96,6 @@ mod tests {
9296 } ,
9397 } ;
9498 use test_assets:: { create_signed_receipt, TAP_EIP712_DOMAIN } ;
95- use tower:: { Service , ServiceBuilder , ServiceExt } ;
9699 use tower_http:: auth:: AsyncRequireAuthorizationLayer ;
97100
98101 use crate :: {
@@ -105,20 +108,28 @@ mod tests {
105108
106109 const ALLOCATION_ID : Address = address ! ( "deadbeefcafebabedeadbeefcafebabedeadbeef" ) ;
107110
108- async fn handle ( _: Request < Body > ) -> anyhow:: Result < Response < Body > > {
109- Ok ( Response :: new ( Body :: default ( ) ) )
111+ #[ fixture]
112+ fn metric ( ) -> & ' static prometheus:: CounterVec {
113+ let registry = prometheus:: Registry :: new ( ) ;
114+ let metric = Box :: leak ( Box :: new (
115+ prometheus:: register_counter_vec_with_registry!(
116+ "tap_middleware_test" ,
117+ "Failed queries to handler" ,
118+ & [ "deployment" ] ,
119+ registry,
120+ )
121+ . unwrap ( ) ,
122+ ) ) ;
123+ metric
110124 }
111125
112- struct TestLabel ;
113- impl MetricLabelProvider for TestLabel {
114- fn get_labels ( & self ) -> Vec < & str > {
115- vec ! [ "label1" ]
116- }
117- }
126+ const FAILED_NONCE : u64 = 99 ;
118127
119- #[ sqlx:: test( migrations = "../../migrations" ) ]
120- async fn test_tap_middleware ( pgpool : PgPool ) {
121- let context = IndexerTapContext :: new ( pgpool. clone ( ) , TAP_EIP712_DOMAIN . clone ( ) ) . await ;
128+ async fn service (
129+ metric : & ' static prometheus:: CounterVec ,
130+ pgpool : PgPool ,
131+ ) -> impl Service < Request < Body > , Response = Response < Body > , Error = impl std:: fmt:: Debug > {
132+ let context = IndexerTapContext :: new ( pgpool, TAP_EIP712_DOMAIN . clone ( ) ) . await ;
122133
123134 struct MyCheck ;
124135 #[ async_trait:: async_trait]
@@ -128,77 +139,115 @@ mod tests {
128139 _: & tap_core:: receipt:: Context ,
129140 receipt : & ReceiptWithState < Checking > ,
130141 ) -> CheckResult {
131- if receipt. signed_receipt ( ) . message . nonce == 99 {
142+ if receipt. signed_receipt ( ) . message . nonce == FAILED_NONCE {
132143 Err ( CheckError :: Failed ( anyhow:: anyhow!( "Failed" ) ) )
133144 } else {
134145 Ok ( ( ) )
135146 }
136147 }
137148 }
138149
139- let tap_manager = Box :: leak ( Box :: new ( Manager :: new (
150+ let manager = Box :: leak ( Box :: new ( Manager :: new (
140151 TAP_EIP712_DOMAIN . clone ( ) ,
141152 context,
142153 CheckList :: new ( vec ! [ Arc :: new( MyCheck ) ] ) ,
143154 ) ) ) ;
144- let metric = Box :: leak ( Box :: new (
145- prometheus:: register_counter_vec!(
146- "tap_middleware_test" ,
147- "Failed queries to handler" ,
148- & [ "deployment" ]
149- )
150- . unwrap ( ) ,
151- ) ) ;
152-
153- let tap_auth = tap_receipt_authorize ( tap_manager, metric) ;
154-
155+ let tap_auth = tap_receipt_authorize ( manager, metric) ;
155156 let authorization_middleware = AsyncRequireAuthorizationLayer :: new ( tap_auth) ;
156157
157158 let mut service = ServiceBuilder :: new ( )
158159 . layer ( authorization_middleware)
159- . service_fn ( handle) ;
160+ . service_fn ( |_: Request < Body > | async {
161+ Ok :: < _ , anyhow:: Error > ( Response :: new ( Body :: default ( ) ) )
162+ } ) ;
160163
161- let handle = service. ready ( ) . await . unwrap ( ) ;
164+ service. ready ( ) . await . unwrap ( ) ;
165+ service
166+ }
167+
168+ #[ rstest]
169+ #[ sqlx:: test( migrations = "../../migrations" ) ]
170+ async fn test_tap_valid_receipt (
171+ metric : & ' static prometheus:: CounterVec ,
172+ #[ ignore] pgpool : PgPool ,
173+ ) {
174+ let mut service = service ( metric, pgpool. clone ( ) ) . await ;
162175
163176 let receipt = create_signed_receipt ( ALLOCATION_ID , 1 , 1 , 1 ) . await ;
164177
165178 // check with receipt
166- let mut req = Request :: new ( Default :: default ( ) ) ;
179+ let mut req = Request :: new ( Body :: default ( ) ) ;
167180 req. extensions_mut ( ) . insert ( receipt) ;
168- let res = handle . call ( req) . await . unwrap ( ) ;
181+ let res = service . call ( req) . await . unwrap ( ) ;
169182 assert_eq ! ( res. status( ) , StatusCode :: OK ) ;
170183
171- // todo make this sleep better
172- tokio:: time:: sleep ( Duration :: from_millis ( 100 ) ) . await ;
173-
174184 // verify receipts
175- let result = sqlx:: query!( "SELECT * FROM scalar_tap_receipts" )
176- . fetch_all ( & pgpool)
177- . await
178- . unwrap ( ) ;
179- assert_eq ! ( result. len( ) , 1 ) ;
185+ if tokio:: time:: timeout ( Duration :: from_secs ( 1 ) , async {
186+ loop {
187+ let result = sqlx:: query!( "SELECT * FROM scalar_tap_receipts" )
188+ . fetch_all ( & pgpool)
189+ . await
190+ . unwrap ( ) ;
191+
192+ if result. is_empty ( ) {
193+ sleep ( Duration :: from_millis ( 50 ) ) . await ;
194+ } else {
195+ break ;
196+ }
197+ }
198+ } )
199+ . await
200+ . is_err ( )
201+ {
202+ panic ! ( "Timeout assertion" ) ;
203+ }
204+ }
205+
206+ #[ rstest]
207+ #[ sqlx:: test( migrations = "../../migrations" ) ]
208+ async fn test_invalid_receipt_with_failed_metric (
209+ metric : & ' static prometheus:: CounterVec ,
210+ #[ ignore] pgpool : PgPool ,
211+ ) {
212+ let mut service = service ( metric, pgpool. clone ( ) ) . await ;
180213 // if it fails tap receipt, should return failed to process payment + tap message
181214
182215 assert_eq ! ( metric. collect( ) . first( ) . unwrap( ) . get_metric( ) . len( ) , 0 ) ;
183216
217+ struct TestLabel ;
218+ impl MetricLabelProvider for TestLabel {
219+ fn get_labels ( & self ) -> Vec < & str > {
220+ vec ! [ "label1" ]
221+ }
222+ }
223+
184224 // default labels, all empty
185225 let labels: MetricLabels = Arc :: new ( TestLabel ) ;
186226
187227 let mut receipt = create_signed_receipt ( ALLOCATION_ID , 1 , 1 , 1 ) . await ;
188228 // change the nonce to make the receipt invalid
189- receipt. message . nonce = 99 ;
190- let mut req = Request :: new ( Default :: default ( ) ) ;
229+ receipt. message . nonce = FAILED_NONCE ;
230+ let mut req = Request :: new ( Body :: default ( ) ) ;
191231 req. extensions_mut ( ) . insert ( receipt) ;
192232 req. extensions_mut ( ) . insert ( labels) ;
193- let res = handle. call ( req) . await . unwrap ( ) ;
194- assert_eq ! ( res. status( ) , StatusCode :: BAD_REQUEST ) ;
233+ let response = service. call ( req) ;
234+
235+ assert_eq ! ( response. await . unwrap( ) . status( ) , StatusCode :: BAD_REQUEST ) ;
195236
196237 assert_eq ! ( metric. collect( ) . first( ) . unwrap( ) . get_metric( ) . len( ) , 1 ) ;
238+ }
197239
240+ #[ rstest]
241+ #[ sqlx:: test( migrations = "../../migrations" ) ]
242+ async fn test_tap_missing_signed_receipt (
243+ metric : & ' static prometheus:: CounterVec ,
244+ #[ ignore] pgpool : PgPool ,
245+ ) {
246+ let mut service = service ( metric, pgpool. clone ( ) ) . await ;
198247 // if it doesnt contain the signed receipt
199248 // should return payment required
200- let req = Request :: new ( Default :: default ( ) ) ;
201- let res = handle . call ( req) . await . unwrap ( ) ;
249+ let req = Request :: new ( Body :: default ( ) ) ;
250+ let res = service . call ( req) . await . unwrap ( ) ;
202251 assert_eq ! ( res. status( ) , StatusCode :: PAYMENT_REQUIRED ) ;
203252 }
204253}
0 commit comments