// Ġ ĠP urchases Ċ // Ċ // Ġ ĠCopyright ĠÂ© Ġ 2 0 1 9 ĠRe venue Cat , ĠInc . ĠAll Ġrights Ġreserved . Ċ // Ċ Ċ package Ġcom . re venue cat . p urchases . common Ċ Ċ import Ġandroid . net . Uri Ċ import Ġandroidx . test . ext . junit . runners . Android JUnit 4 Ċ import Ġcom . re venue cat . p urchases . P urch aser Info Ċ import Ġcom . re venue cat . p urchases . P urchases Error Ċ import Ġcom . re venue cat . p urchases . P urchases ErrorCode Ċ import Ġcom . re venue cat . p urchases . common . at tribution . At tribution Network Ċ import Ġcom . re venue cat . p urchases . models . Product Details Ċ import Ġcom . re venue cat . p urchases . utils . Responses Ċ import Ġcom . re venue cat . p urchases . utils . get Nullable String Ċ import Ġio . mock k . Called Ċ import Ġio . mock k . every Ċ import Ġio . mock k . mock k Ċ import Ġio . mock k . mock k Static Ċ import Ġio . mock k . slot Ċ import Ġio . mock k . verify Ċ import Ġorg . assertj . core . api . Assertions . assertThat Ċ import Ġorg . assertj . core . api . Fail . fail Ċ import Ġorg . json . JSONObject Ċ import Ġorg . junit . Before Ċ import Ġorg . junit . Test Ċ import Ġorg . junit . runner . RunWith Ċ import Ġorg . ro bo lectric . annotation . Config Ċ import Ġjava . io . IOException Ċ import Ġjava . lang . Thread . sleep Ċ import Ġjava . util . HashMap Ċ import Ġjava . util . concurrent . Count DownLatch Ċ import Ġjava . util . concurrent . Linked BlockingQueue Ċ import Ġjava . util . concurrent . Thread Local Random Ċ import Ġjava . util . concurrent . ThreadPoolExecutor Ċ import Ġjava . util . concurrent . TimeUnit Ċ Ċ private Ġconst Ġval ĠAPI _ KEY Ġ= Ġ" TEST _ API _ KEY " Ċ Ċ @ RunWith ( Android JUnit 4 :: class ) Ċ @ Config ( manifest Ġ= ĠConfig . NONE ) Ċ class ĠBackend Test Ġ{ ĊĊĠĠĠ Ġ@ Before ĊĠĠĠ Ġfun Ġsetup () Ġ= Ġmock k Static (" com . re venue cat . p urchases . common . Factories Kt ") ĊĊĠĠĠ Ġprivate Ġvar Ġmock Client : ĠHTTP Client Ġ= Ġmock k ( relax ed Ġ= Ġtrue ) ĊĠĠĠ Ġprivate Ġval Ġdispatcher Ġ= ĠSync Dispatcher () ĊĠĠĠ Ġprivate Ġvar Ġbackend : ĠBackend Ġ= ĠBackend ( ĊĠĠĠĠĠĠĠ ĠAPI _ KEY , ĊĠĠĠĠĠĠĠ Ġdispatcher , ĊĠĠĠĠĠĠĠ Ġmock Client ĊĠĠĠ Ġ) ĊĠĠĠ Ġprivate Ġvar Ġasync Backend : ĠBackend Ġ= ĠBackend ( ĊĠĠĠĠĠĠĠ ĠAPI _ KEY , ĊĠĠĠĠĠĠĠ ĠDispatcher ( ĊĠĠĠĠĠĠĠĠĠĠĠ ĠThreadPool Executor ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ 1 , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ 2 , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ 0 , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠTimeUnit . MILLISECONDS , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠLinked BlockingQueue () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ), ĊĠĠĠĠĠĠĠ Ġmock Client ĊĠĠĠ Ġ) ĊĠĠĠ Ġprivate Ġval Ġapp UserID Ġ= Ġ" j erry " ĊĊĠĠĠ Ġprivate Ġvar Ġreceived P urch aser Info : ĠP urch aser Info ? Ġ= Ġnull ĊĠĠĠ Ġprivate Ġvar Ġreceived Created : ĠBoolean ? Ġ= Ġnull ĊĠĠĠ Ġprivate Ġvar Ġreceived Offer ings JSON : ĠJSONObject ? Ġ= Ġnull ĊĠĠĠ Ġprivate Ġvar Ġreceived Error : ĠP urchases Error ? Ġ= Ġnull ĊĊĠĠĠ Ġprivate Ġval Ġon Receive P urch aser Info Success Handler : Ġ( P urch aser Info ) Ġ-> ĠUnit Ġ= Ġ{ Ġinfo Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠ Ġthis @ Backend Test . received P urch aser Info Ġ= Ġinfo ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġval Ġon Receive Post Receipt Success Handler : Ġ( P urch aser Info , ĠJSONObject ?) Ġ-> ĠUnit Ġ= ĊĠĠĠĠĠĠĠ Ġ{ Ġinfo , Ġ_ Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠ Ġthis @ Backend Test . received P urch aser Info Ġ= Ġinfo ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġval Ġpost Receipt Error Callback : Ġ( P urchases Error , ĠBoolean , ĠJSONObject ?) Ġ-> ĠUnit Ġ= ĊĠĠĠĠĠĠĠ Ġ{ Ġerror , Ġ_, Ġ_ Ġ-> ĊĠĠĠĠĠĠĠ Ġthis @ Backend Test . received Error Ġ= Ġerror ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġval Ġon Receive P urch aser Info ErrorHandler : Ġ( P urchases Error ) Ġ-> ĠUnit Ġ= Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġthis @ Backend Test . received Error Ġ= Ġit ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġval Ġon Receive Offer ings Response Success Handler : Ġ( JSONObject ) Ġ-> ĠUnit Ġ= Ġ{ Ġoffer ings JSON Ġ-> ĊĠĠĠĠĠĠĠ Ġthis @ Backend Test . received Offer ings JSON Ġ= Ġoffer ings JSON ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġval Ġon Receive Offer ings ErrorHandler : Ġ( P urchases Error ) Ġ-> ĠUnit Ġ= Ġ{ ĊĠĠĠĠĠĠĠ Ġthis @ Backend Test . received Error Ġ= Ġit ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġval Ġon Login Success Handler : Ġ( P urch aser Info , ĠBoolean ) Ġ-> ĠUnit Ġ= Ġ{ Ġpurch aser Info , Ġcreated Ġ-> ĊĠĠĠĠĠĠĠ Ġthis @ Backend Test . received P urch aser Info Ġ= Ġpurch aser Info ĊĠĠĠĠĠĠĠ Ġthis @ Backend Test . received Created Ġ= Ġcreated ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġval Ġon Receive Login ErrorHandler : Ġ( P urchases Error ) Ġ-> ĠUnit Ġ= Ġ{ ĊĠĠĠĠĠĠĠ Ġthis @ Backend Test . received Error Ġ= Ġit ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġcan Be Created () Ġ{ ĊĠĠĠĠĠĠĠ ĠassertThat ( backend ). isNotNull ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġmock Response ( ĊĠĠĠĠĠĠĠ Ġpath : ĠString , ĊĠĠĠĠĠĠĠ Ġbody : ĠMap < String , ĠAny ?> ?, ĊĠĠĠĠĠĠĠ Ġresponse Code : ĠInt , ĊĠĠĠĠĠĠĠ Ġclient Exception : ĠException ?, ĊĠĠĠĠĠĠĠ Ġresult Body : ĠString ?, ĊĠĠĠĠĠĠĠ Ġdelayed : ĠBoolean Ġ= Ġfalse , ĊĠĠĠĠĠĠĠ Ġshould Mock P urch aser Info : ĠBoolean Ġ= Ġtrue ĊĠĠĠ Ġ): ĠP urch aser Info Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġinfo : ĠP urch aser Info Ġ= Ġmock k () ĊĊĠĠĠĠĠĠĠ Ġval Ġresult Ġ= ĠHTTP Client . Result ( response Code , ĠJSONObject ( result Body Ġ?: Ġ"{} ")) ĊĊĠĠĠĠĠĠĠ Ġval Ġheaders Ġ= ĠHashMap < String , ĠString >() ĊĠĠĠĠĠĠĠ Ġheaders [" Authorization "] Ġ= Ġ" Bearer Ġ$ API _ KEY " ĊĊĠĠĠĠĠĠĠ Ġif Ġ( should Mock P urch aser Info ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġevery Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġresult . body . build P urch aser Info () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġreturns Ġinfo ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġval Ġevery Mock ed Call Ġ= Ġevery Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client . perform Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġeq ( path ), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ( if Ġ( body Ġ== Ġnull ) Ġany () Ġelse Ġeq ( body )), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġeq < Map < String , ĠString >>( headers ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġif Ġ( client Exception Ġ== Ġnull ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġevery Mock ed Call Ġanswers Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( delayed ) Ġsleep ( 2 0 0 ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġresult ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} Ġelse Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġevery Mock ed Call Ġthrows Ġclient Exception ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġreturn Ġinfo ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġpost Receipt ( ĊĠĠĠĠĠĠĠ Ġresponse Code : ĠInt , ĊĠĠĠĠĠĠĠ Ġis Restore : ĠBoolean , ĊĠĠĠĠĠĠĠ Ġclient Exception : ĠException ?, ĊĠĠĠĠĠĠĠ Ġresult Body : ĠString ?, ĊĠĠĠĠĠĠĠ Ġobserver Mode : ĠBoolean , ĊĠĠĠĠĠĠĠ Ġreceipt Info : ĠRe ceipt Info , ĊĠĠĠĠĠĠĠ Ġstore App UserID : ĠString ? ĊĠĠĠ Ġ): ĠP urch aser Info Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġ( fetch Token , Ġinfo ) Ġ= Ġmock Post Receipt Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġresult Body , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġobserver Mode , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġstore App UserID ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġbackend . post Receipt Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpurchase Token Ġ= Ġfetch Token , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID Ġ= Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġis Restore , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġobserver Mode , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsubscriber Attributes Ġ= Ġempty Map (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġstore App UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġon Receive Post Receipt Success Handler , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġpost Receipt Error Callback ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġreturn Ġinfo ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġval Ġproduct ID Ġ= Ġ" product _ id " ĊĊĠĠĠ Ġprivate Ġfun Ġmock Post Receipt Response ( ĊĠĠĠĠĠĠĠ Ġis Restore : ĠBoolean , ĊĠĠĠĠĠĠĠ Ġresponse Code : ĠInt , ĊĠĠĠĠĠĠĠ Ġclient Exception : ĠException ?, ĊĠĠĠĠĠĠĠ Ġresult Body : ĠString ?, ĊĠĠĠĠĠĠĠ Ġdelayed : ĠBoolean Ġ= Ġfalse , ĊĠĠĠĠĠĠĠ Ġobserver Mode : ĠBoolean , ĊĠĠĠĠĠĠĠ Ġreceipt Info : ĠRe ceipt Info , ĊĠĠĠĠĠĠĠ Ġstore App UserID : ĠString ? ĊĠĠĠ Ġ): ĠPair < String , ĠP urch aser Info > Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġfetch Token Ġ= Ġ" fetch _ token " ĊĠĠĠĠĠĠĠ Ġval Ġbody Ġ= Ġmap Of ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" fetch _ token " Ġto Ġfetch Token , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" app _ user _ id " Ġto Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" product _ id " Ġto Ġreceipt Info . product ID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" is _ restore " Ġto Ġis Restore , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" present ed _ offer ing _ identifier " Ġto Ġreceipt Info . offer ing Identifier , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" observer _ mode " Ġto Ġobserver Mode , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" price " Ġto Ġreceipt Info . price , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" currency " Ġto Ġreceipt Info . currency , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" normal _ duration " Ġto Ġreceipt Info . duration , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" intro _ duration " Ġto Ġreceipt Info . intro Duration , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" trial _ duration " Ġto Ġreceipt Info . trial Duration , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" store _ user _ id " Ġto Ġstore App UserID ĊĠĠĠĠĠĠĠ Ġ). map NotNull Ġ{ Ġentry : ĠMap . Entry < String , ĠAny ?> Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠ Ġentry . value ?. let Ġ{ Ġentry . key Ġto Ġit Ġ} ĊĠĠĠĠĠĠĠ Ġ}. toMap () ĊĊĠĠĠĠĠĠĠ Ġval Ġinfo Ġ= Ġmock Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ receipt s ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġbody , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġreturn Ġfetch Token Ġto Ġinfo ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġget P urch aser Info ( ĊĠĠĠĠĠĠĠ Ġresponse Code : ĠInt , ĊĠĠĠĠĠĠĠ Ġclient Exception : ĠException ?, ĊĠĠĠĠĠĠĠ Ġresult Body : ĠString ?, ĊĠĠĠĠĠĠĠ Ġapp In Background : ĠBoolean Ġ= Ġfalse ĊĠĠĠ Ġ): ĠP urch aser Info Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġinfo Ġ= ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Response ("/ subscribers /$ app UserID ", Ġnull , Ġresponse Code , Ġclient Exception , Ġresult Body ) ĊĊĠĠĠĠĠĠĠ Ġbackend . getP urch aser Info ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp In Background , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġon Receive P urch aser Info Success Handler , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġon Receive P urch aser Info ErrorHandler ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġreturn Ġinfo ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġget Subscriber Info Calls Pro per URL () Ġ{ ĊĊĠĠĠĠĠĠĠ Ġval Ġinfo Ġ= Ġget P urch aser Info ( 2 0 0 , Ġnull , Ġnull ) ĊĊĠĠĠĠĠĠĠ ĠassertThat ( received P urch aser Info ). isNotNull ĊĠĠĠĠĠĠĠ ĠassertThat ( received P urch aser Info ). isEqualTo ( info ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġget Subscriber Info Fails IfNot 2 0 X () Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġfailure Code Ġ= ĠThread Local Random . current (). nextInt ( 3 0 0 , Ġ 5 0 0 Ġ+ Ġ 1 ) ĊĊĠĠĠĠĠĠĠ Ġget P urch aser Info ( failure Code , Ġnull , Ġnull ) ĊĊĠĠĠĠĠĠĠ ĠassertThat ( received P urch aser Info ). isNull () ĊĠĠĠĠĠĠĠ ĠassertThat ( received Error ). ` as ` (" Received Ġerror Ġis Ġnot Ġnull "). isNotNull ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġclient Error Calls ErrorHandler () Ġ{ ĊĠĠĠĠĠĠĠ Ġget P urch aser Info ( 2 0 0 , ĠIOException (), Ġnull ) ĊĊĠĠĠĠĠĠĠ ĠassertThat ( received P urch aser Info ). isNull () ĊĠĠĠĠĠĠĠ ĠassertThat ( received Error ). ` as ` (" Received Ġerror Ġis Ġnot Ġnull "). isNotNull ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġattempts To Parse ErrorMessage From Server () Ġ{ ĊĠĠĠĠĠĠĠ Ġget P urch aser Info ( 4 0 4 , Ġnull , Ġ"{ ' code ': Ġ 7 2 2 5 , Ġ' message ': Ġ' D ude Ġnot Ġfound '} ") ĊĊĠĠĠĠĠĠĠ ĠassertThat ( received Error ). ` as ` (" Received Ġerror Ġis Ġnot Ġnull "). isNotNull ĊĠĠĠĠĠĠĠ ĠassertThat ( received Error !!. underlying ErrorMessage ). ` as ` (" Received Ġunderlying Ġmessage Ġis Ġnot Ġnull "). isNotNull () ĊĠĠĠĠĠĠĠ ĠassertThat ( received Error !!. underlying ErrorMessage !! ). contains (" D ude Ġnot Ġfound ") ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġhandles Missing Message In Error Body () Ġ{ ĊĠĠĠĠĠĠĠ Ġget P urch aser Info ( 4 0 4 , Ġnull , Ġ"{ ' no _ message ': Ġ' D ude Ġnot Ġfound '} ") ĊĠĠĠĠĠĠĠ ĠassertThat ( received Error ). ` as ` (" Received Ġerror Ġis Ġnot Ġnull "). isNotNull ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġpost Receipt Calls Pro per URL () Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġinfo Ġ= Ġpost Receipt ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= ĠRe ceipt Info ( product ID ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ ĠassertThat ( received P urch aser Info ). ` as ` (" Received Ġinfo Ġis Ġnot Ġnull "). isNotNull ĊĠĠĠĠĠĠĠ ĠassertThat ( info ). isEqualTo ( received P urch aser Info ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġpost Receipt Calls Fails For 4 XX () Ġ{ ĊĠĠĠĠĠĠĠ Ġpost Receipt ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 4 0 1 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= ĠRe ceipt Info ( product ID ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ ĠassertThat ( received P urch aser Info ). ` as ` (" Received Ġinfo Ġis Ġnull "). isNull () ĊĠĠĠĠĠĠĠ ĠassertThat ( received Error ). ` as ` (" Received Ġerror Ġis Ġnot Ġnull "). isNotNull ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġval Ġno Offer ings Response Ġ= Ġ"{ ' offer ings ': Ġ[], Ġ' current _ offer ing _ id ': Ġnull }" ĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` given Ġa Ġno Ġoffer ings Ġresponse `() Ġ{ ĊĊĠĠĠĠĠĠĠ Ġmock Response ("/ subscribers /$ app UserID / offer ings ", Ġnull , Ġ 2 0 0 , Ġnull , Ġno Offer ings Response ) ĊĊĠĠĠĠĠĠĠ Ġbackend . get Offer ings ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp In Background Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġon Receive Offer ings Response Success Handler , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġon Receive Offer ings ErrorHandler ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ ĠassertThat ( received Offer ings JSON ). ` as ` (" Received Ġoffer ings Ġresponse Ġis Ġnot Ġnull "). isNotNull ĊĠĠĠĠĠĠĠ ĠassertThat ( received Offer ings JSON !!. get JSONArray (" offer ings "). length ()). is Zero () ĊĠĠĠĠĠĠĠ ĠassertThat ( received Offer ings JSON !!. get Nullable String (" current _ offer ing _ id ")). isNull () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġcan Post Basic At tribution Data () Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġpath Ġ= Ġ"/ subscribers /$ app UserID / at tribution " ĊĊĠĠĠĠĠĠĠ Ġval Ġ` object ` Ġ= ĠJSONObject () ĊĠĠĠĠĠĠĠ Ġ` object `. put (" string ", Ġ" value ") ĊĊĠĠĠĠĠĠĠ Ġval Ġexpected Body Ġ= ĠJSONObject () ĊĠĠĠĠĠĠĠ Ġexpected Body . put (" network ", ĠAt tribution Network . APPS FL Y ER ) ĊĠĠĠĠĠĠĠ Ġexpected Body . put (" data ", Ġ` object `) ĊĊĠĠĠĠĠĠĠ Ġbackend . post At tribution Data ( app UserID , ĠAt tribution Network . APPS FL Y ER , Ġ` object `) Ġ{ ĊĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġval Ġheaders Ġ= ĠHashMap < String , ĠString >() ĊĠĠĠĠĠĠĠ Ġheaders [" Authorization "] Ġ= Ġ" Bearer Ġ$ API _ KEY " ĊĠĠĠĠĠĠĠ Ġval Ġslot Ġ= Ġslot < Map < String , ĠAny ? >>() ĊĠĠĠĠĠĠĠ Ġverify Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client . perform Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġeq ( path ), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcapture ( slot ), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġeq ( headers ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġval Ġcaptured Ġ= Ġslot . captured ĊĠĠĠĠĠĠĠ ĠassertThat ( captured . containsKey (" network ") Ġ&& Ġcaptured . containsKey (" data ") Ġ&& ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcaptured [" network "] Ġ== ĠAt tribution Network . APPS FL Y ER . server Value ). isTrue () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġdoesnt Post Empty At tribution Data () Ġ{ ĊĠĠĠĠĠĠĠ Ġbackend . post At tribution Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠAt tribution Network . APPS FL Y ER , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠJSONObject () ĊĠĠĠĠĠĠĠ Ġ) Ġ{} ĊĠĠĠĠĠĠĠ Ġverify Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client Ġwas Not ĠCalled ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġen codes App UserId () Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġencode able UserID Ġ= Ġ" userid Ġwith Ġspaces " ĊĊĠĠĠĠĠĠĠ Ġval Ġencoded UserID Ġ= Ġ" userid % 2 0 with % 2 0 spaces " ĊĠĠĠĠĠĠĠ Ġval Ġpath Ġ= Ġ"/ subscribers /$ encoded UserID / at tribution " ĊĊĠĠĠĠĠĠĠ Ġval Ġ` object ` Ġ= ĠJSONObject () ĊĠĠĠĠĠĠĠ Ġ` object `. put (" string ", Ġ" value ") ĊĊĠĠĠĠĠĠĠ Ġbackend . post At tribution Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġencode able UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠAt tribution Network . APPS FL Y ER , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ` object ` ĊĠĠĠĠĠĠĠ Ġ) Ġ{ Ġ} ĊĊĠĠĠĠĠĠĠ Ġverify Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client . perform Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġeq ( path ), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany (), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġdoesnt Dispatch If Closed () Ġ{ ĊĠĠĠĠĠĠĠ Ġbackend . post At tribution Data (" id ", ĠAt tribution Network . APPS FL Y ER , ĠJSONObject ()) Ġ{ Ġ} ĊĊĠĠĠĠĠĠĠ Ġbackend . close () ĊĊĠĠĠĠĠĠĠ Ġbackend . post At tribution Data (" id ", ĠAt tribution Network . APPS FL Y ER , ĠJSONObject ()) Ġ{ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` given Ġan Ġalias Ġtoken , Ġalias Ġcalls Ġproperly `() Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġbody Ġ= Ġmap Of ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" new _ app _ user _ id " Ġto Ġ" new Id " ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġmock Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ subscribers /$ app UserID / alias ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġbody , ĊĠĠĠĠĠĠĠĠĠĠĠĠ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġval ĠonSuccess Ġ= Ġmock k < () Ġ-> ĠUnit >( relax ed Ġ= Ġtrue ) ĊĠĠĠĠĠĠĠ Ġbackend . create Alias ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" new Id ", ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġfail < String >(" Should Ġhave Ġcalled Ġsuccess ") ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġverify Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess . invoke () ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` given Ġmultiple Ġget Ġcalls Ġfor Ġsame Ġsubscriber , Ġonly Ġone Ġis Ġtriggered `() Ġ{ ĊĠĠĠĠĠĠĠ Ġmock Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ subscribers /$ app UserID ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠĠ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtrue ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġlock Ġ= ĠCount DownLatch ( 2 ) ĊĠĠĠĠĠĠĠ Ġasync Backend . getP urch aser Info ( app UserID , Ġapp In Background Ġ= Ġfalse , ĠonSuccess Ġ= Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠ Ġ}, ĠonError Ġ= Ġon Receive P urch aser Info ErrorHandler ) ĊĠĠĠĠĠĠĠ Ġasync Backend . getP urch aser Info ( app UserID , Ġapp In Background Ġ= Ġfalse , ĠonSuccess Ġ= Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠ Ġ}, ĠonError Ġ= Ġon Receive P urch aser Info ErrorHandler ) ĊĠĠĠĠĠĠĠ Ġlock . await ( 2 0 0 0 , ĠTimeUnit . MILLISECONDS ) ĊĠĠĠĠĠĠĠ ĠassertThat ( lock . count ). isEqualTo ( 0 ) ĊĠĠĠĠĠĠĠ Ġverify ( exact ly Ġ= Ġ 1 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client . perform Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ subscribers /" Ġ+ ĠUri . encode ( app UserID ), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` given Ġmultiple Ġpost Ġcalls Ġfor Ġsame Ġsubscriber , Ġonly Ġone Ġis Ġtriggered `() Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġreceipt Info Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID Ġ= Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoffering Identifier Ġ= Ġ" offer ing _ a " ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġ( fetch Token , Ġ_) Ġ= Ġmock Post Receipt Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġoffering Identifier Ġ= Ġ" offer ing _ a " ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġlock Ġ= ĠCount DownLatch ( 2 ) ĊĊĠĠĠĠĠĠĠ Ġasync Backend . post Receipt Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpurchase Token Ġ= Ġfetch Token , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID Ġ= Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsubscriber Attributes Ġ= Ġempty Map (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġ{ Ġ_, Ġ_ Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġpost Receipt Error Callback ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġproduct Info 1 Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID Ġ= Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoffering Identifier Ġ= Ġ" offer ing _ a " ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġasync Backend . post Receipt Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpurchase Token Ġ= Ġfetch Token , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID Ġ= Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsubscriber Attributes Ġ= Ġempty Map (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġproduct Info 1 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġ{ Ġ_, Ġ_ Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġpost Receipt Error Callback ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġlock . await ( 2 0 0 0 , ĠTimeUnit . MILLISECONDS ) ĊĠĠĠĠĠĠĠ ĠassertThat ( lock . count ). isEqualTo ( 0 ) ĊĠĠĠĠĠĠĠ Ġverify ( exact ly Ġ= Ġ 1 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client . perform Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ receipt s ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany (), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` given Ġmultiple Ġget Ġoffer ings Ġcalls Ġfor Ġsame Ġuser , Ġonly Ġone Ġis Ġtriggered `() Ġ{ ĊĠĠĠĠĠĠĠ Ġmock Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ subscribers /$ app UserID / offer ings ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠĠ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġno Offer ings Response , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtrue ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġlock Ġ= ĠCount DownLatch ( 2 ) ĊĠĠĠĠĠĠĠ Ġasync Backend . get Offer ings ( app UserID , Ġapp In Background Ġ= Ġfalse , ĠonSuccess Ġ= Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠ Ġ}, ĠonError Ġ= Ġon Receive Offer ings ErrorHandler ) ĊĠĠĠĠĠĠĠ Ġasync Backend . get Offer ings ( app UserID , Ġapp In Background Ġ= Ġfalse , ĠonSuccess Ġ= Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠ Ġ}, ĠonError Ġ= Ġon Receive Offer ings ErrorHandler ) ĊĠĠĠĠĠĠĠ Ġlock . await ( 2 0 0 0 , ĠTimeUnit . MILLISECONDS ) ĊĠĠĠĠĠĠĠ ĠassertThat ( lock . count ). isEqualTo ( 0 ) ĊĠĠĠĠĠĠĠ Ġverify ( exact ly Ġ= Ġ 1 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client . perform Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ subscribers /$ app UserID / offer ings ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` given Ġmultiple Ġoffer ings Ġget Ġcalls Ġfor Ġdifferent Ġuser , Ġboth Ġare Ġtriggered `() Ġ{ ĊĠĠĠĠĠĠĠ Ġmock Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ subscribers /$ app UserID / offer ings ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠĠ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġno Offer ings Response , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtrue ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġlock Ġ= ĠCount DownLatch ( 2 ) ĊĠĠĠĠĠĠĠ Ġasync Backend . get Offer ings ( app UserID , Ġapp In Background Ġ= Ġfalse , ĠonSuccess Ġ= Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠ Ġ}, ĠonError Ġ= Ġon Receive Offer ings ErrorHandler ) ĊĠĠĠĠĠĠĠ Ġasync Backend . get Offer ings (" another User ", Ġapp In Background Ġ= Ġfalse , ĠonSuccess Ġ= Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠ Ġ}, ĠonError Ġ= Ġon Receive Offer ings ErrorHandler ) ĊĠĠĠĠĠĠĠ Ġlock . await ( 2 0 0 0 , ĠTimeUnit . MILLISECONDS ) ĊĠĠĠĠĠĠĠ ĠassertThat ( lock . count ). isEqualTo ( 0 ) ĊĠĠĠĠĠĠĠ Ġverify ( exact ly Ġ= Ġ 1 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client . perform Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ subscribers /$ app UserID / offer ings ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġverify ( exact ly Ġ= Ġ 1 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client . perform Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ subscribers / another User / offer ings ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` given Ġmultiple Ġpost Ġcalls Ġfor Ġsame Ġsubscriber Ġdifferent Ġoffering , Ġboth Ġare Ġtriggered `() Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġ( fetch Token , Ġ_) Ġ= Ġmock Post Receipt Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġoffering Identifier Ġ= Ġ" offer ing _ a " ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġlock Ġ= ĠCount DownLatch ( 2 ) ĊĠĠĠĠĠĠĠ Ġval Ġreceipt Info Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID Ġ= Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoffering Identifier Ġ= Ġ" offer ing _ a " ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġasync Backend . post Receipt Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpurchase Token Ġ= Ġfetch Token , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID Ġ= Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsubscriber Attributes Ġ= Ġempty Map (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġ{ Ġ_, Ġ_ Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġpost Receipt Error Callback ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġ( fetch Token 1 , Ġ_) Ġ= Ġmock Post Receipt Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġoffering Identifier Ġ= Ġ" offer ing _ b " ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġproduct Info 1 Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID Ġ= Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoffering Identifier Ġ= Ġ" offer ing _ b " ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġasync Backend . post Receipt Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpurchase Token Ġ= Ġfetch Token 1 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID Ġ= Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsubscriber Attributes Ġ= Ġempty Map (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġproduct Info 1 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġ{ Ġ_, Ġ_ Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġpost Receipt Error Callback ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġlock . await () ĊĠĠĠĠĠĠĠ ĠassertThat ( lock . count ). isEqualTo ( 0 ) ĊĠĠĠĠĠĠĠ Ġverify ( exact ly Ġ= Ġ 2 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client . perform Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ receipt s ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany (), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġpost Receipt Observer Mode () Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġinfo Ġ= Ġpost Receipt ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= ĠRe ceipt Info ( product ID ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ ĠassertThat ( received P urch aser Info ). ` as ` (" Received Ġinfo Ġis Ġnot Ġnull "). isNotNull ĊĠĠĠĠĠĠĠ ĠassertThat ( info ). isEqualTo ( received P urch aser Info ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` post Receipt Ġpasses Ġprice Ġand Ġcurrency `() Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġproduct Details Ġ= Ġmock Product Details () ĊĊĠĠĠĠĠĠĠ Ġval Ġinfo Ġ= Ġpost Receipt ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġproduct Details Ġ= Ġproduct Details ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ ĠassertThat ( received P urch aser Info ). ` as ` (" Received Ġinfo Ġis Ġnot Ġnull "). isNotNull ĊĠĠĠĠĠĠĠ ĠassertThat ( info ). isEqualTo ( received P urch aser Info ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` given Ġmultiple Ġpost Ġcalls Ġfor Ġsame Ġsubscriber Ġdifferent Ġprice , Ġboth Ġare Ġtriggered `() Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġ( fetch Token , Ġ_) Ġ= Ġmock Post Receipt Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġoffering Identifier Ġ= Ġ" offer ing _ a " ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġval Ġproduct Details Ġ= Ġmock Product Details () ĊĠĠĠĠĠĠĠ Ġval Ġproduct Details 1 Ġ= Ġmock Product Details ( price Ġ= Ġ 3 5 0 0 0 0 ) ĊĠĠĠĠĠĠĠ Ġval Ġreceipt Info Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoffering Identifier Ġ= Ġ" offer ing _ a ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct Details Ġ= Ġproduct Details ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġproduct Info 1 Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID Ġ= Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoffering Identifier Ġ= Ġ" offer ing _ a ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct Details Ġ= Ġproduct Details 1 ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġmock Post Receipt Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġmock Post Receipt Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġproduct Info 1 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġlock Ġ= ĠCount DownLatch ( 2 ) ĊĠĠĠĠĠĠĠ Ġasync Backend . post Receipt Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpurchase Token Ġ= Ġfetch Token , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID Ġ= Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsubscriber Attributes Ġ= Ġempty Map (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġ{ Ġ_, Ġ_ Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġpost Receipt Error Callback ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġasync Backend . post Receipt Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpurchase Token Ġ= Ġfetch Token , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID Ġ= Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsubscriber Attributes Ġ= Ġempty Map (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġproduct Info 1 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġ{ Ġ_, Ġ_ Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġpost Receipt Error Callback ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġlock . await ( 2 0 0 0 , ĠTimeUnit . MILLISECONDS ) ĊĠĠĠĠĠĠĠ ĠassertThat ( lock . count ). isEqualTo ( 0 ) ĊĠĠĠĠĠĠĠ Ġverify ( exact ly Ġ= Ġ 2 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client . perform Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ receipt s ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () Ġas ĠMap < String , ĠAny ? >, ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` given Ġmultiple Ġpost Ġcalls Ġfor Ġsame Ġsubscriber Ġdifferent Ġdur ations , Ġboth Ġare Ġtriggered `() Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġ( fetch Token , Ġ_) Ġ= Ġmock Post Receipt Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġoffering Identifier Ġ= Ġ" offer ing _ a " ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġval Ġproduct Details Ġ= Ġmock Product Details () ĊĠĠĠĠĠĠĠ Ġval Ġproduct Details 1 Ġ= Ġmock Product Details ( duration Ġ= Ġ" P 2 M ") ĊĊĠĠĠĠĠĠĠ Ġval Ġreceipt Info Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID Ġ= Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoffering Identifier Ġ= Ġ" offer ing _ a ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct Details Ġ= Ġproduct Details ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġproduct Info 1 Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoffering Identifier Ġ= Ġ" offer ing _ a ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct Details Ġ= Ġproduct Details 1 ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġmock Post Receipt Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġmock Post Receipt Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġproduct Info 1 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġlock Ġ= ĠCount DownLatch ( 2 ) ĊĠĠĠĠĠĠĠ Ġasync Backend . post Receipt Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpurchase Token Ġ= Ġfetch Token , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID Ġ= Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsubscriber Attributes Ġ= Ġempty Map (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġ{ Ġ_, Ġ_ Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġpost Receipt Error Callback ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġasync Backend . post Receipt Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpurchase Token Ġ= Ġfetch Token , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID Ġ= Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsubscriber Attributes Ġ= Ġempty Map (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġproduct Info 1 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġ{ Ġ_, Ġ_ Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġpost Receipt Error Callback ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġlock . await () ĊĠĠĠĠĠĠĠ ĠassertThat ( lock . count ). isEqualTo ( 0 ) ĊĠĠĠĠĠĠĠ Ġverify ( exact ly Ġ= Ġ 2 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client . perform Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ receipt s ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () Ġas ĠMap < String , ĠAny ? >, ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` given Ġmultiple Ġpost Ġcalls Ġfor Ġsame Ġsubscriber Ġsame Ġdur ations , Ġonly Ġone Ġis Ġtriggered `() Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġproduct Details Ġ= Ġmock Product Details () ĊĊĠĠĠĠĠĠĠ Ġval Ġreceipt Info Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoffering Identifier Ġ= Ġ" offer ing _ a ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct Details Ġ= Ġproduct Details ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġ( fetch Token , Ġ_) Ġ= Ġmock Post Receipt Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġval Ġlock Ġ= ĠCount DownLatch ( 2 ) ĊĠĠĠĠĠĠĠ Ġasync Backend . post Receipt Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpurchase Token Ġ= Ġfetch Token , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID Ġ= Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsubscriber Attributes Ġ= Ġempty Map (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġ{ Ġ_, Ġ_ Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġpost Receipt Error Callback ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġasync Backend . post Receipt Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpurchase Token Ġ= Ġfetch Token , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID Ġ= Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsubscriber Attributes Ġ= Ġempty Map (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġ{ Ġ_, Ġ_ Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġpost Receipt Error Callback ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġlock . await () ĊĠĠĠĠĠĠĠ ĠassertThat ( lock . count ). isEqualTo ( 0 ) ĊĠĠĠĠĠĠĠ Ġverify ( exact ly Ġ= Ġ 1 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client . perform Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ receipt s ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () Ġas ĠMap < String , ĠAny ? >, ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` post Receipt Ġpasses Ġdur ations `() Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġproduct Details Ġ= Ġmock Product Details ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġduration Ġ= Ġ" P 1 M ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġintro Duration Ġ= Ġ" P 2 M ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtrial Duration Ġ= Ġ" P 3 M " ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġval Ġinfo Ġ= Ġpost Receipt ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID Ġ= Ġproduct ID , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġproduct Details Ġ= Ġproduct Details ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull , ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ ĠassertThat ( received P urch aser Info ). ` as ` (" Received Ġpurch aser Ġinfo Ġis Ġnot Ġnull "). isNotNull ĊĠĠĠĠĠĠĠ ĠassertThat ( info ). isEqualTo ( received P urch aser Info ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` offer ings Ġcall Ġis Ġen queued Ġwith Ġdelay Ġif Ġon Ġbackground `() Ġ{ ĊĠĠĠĠĠĠĠ Ġmock Response ("/ subscribers /$ app UserID / offer ings ", Ġnull , Ġ 2 0 0 , Ġnull , Ġno Offer ings Response ) ĊĠĠĠĠĠĠĠ Ġdispatcher . calledWith Random Delay Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġbackend . get Offer ings ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp In Background Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġon Receive Offer ings Response Success Handler , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġon Receive Offer ings ErrorHandler ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġval Ġcalled With Random Delay : ĠBoolean ? Ġ= Ġdispatcher . calledWith Random Delay ĊĠĠĠĠĠĠĠ ĠassertThat ( calledWith Random Delay ). isNotNull () ĊĠĠĠĠĠĠĠ ĠassertThat ( calledWith Random Delay ). isTrue () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` p urch aser Ġinfo Ġcall Ġis Ġen queued Ġwith Ġdelay Ġif Ġon Ġbackground `() Ġ{ ĊĠĠĠĠĠĠĠ Ġdispatcher . calledWith Random Delay Ġ= Ġnull ĊĊĠĠĠĠĠĠĠ Ġget P urch aser Info ( 2 0 0 , Ġclient Exception Ġ= Ġnull , Ġresult Body Ġ= Ġnull , Ġapp In Background Ġ= Ġtrue ) ĊĊĠĠĠĠĠĠĠ Ġval Ġcalled With Random Delay : ĠBoolean ? Ġ= Ġdispatcher . calledWith Random Delay ĊĠĠĠĠĠĠĠ ĠassertThat ( calledWith Random Delay ). isNotNull () ĊĠĠĠĠĠĠĠ ĠassertThat ( calledWith Random Delay ). isTrue () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` log In Ġmakes Ġthe Ġright Ġhttp Ġcall `() Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġnew App UserID Ġ= Ġ" new Id " ĊĠĠĠĠĠĠĠ Ġval Ġbody Ġ= Ġmap Of ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" new _ app _ user _ id " Ġto Ġnew App UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" app _ user _ id " Ġto Ġapp UserID ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġmock Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ subscribers / identify ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġbody , ĊĠĠĠĠĠĠĠĠĠĠĠĠ 2 0 1 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠResponse s . valid Full P urch aser Response ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġbackend . log In ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnew App UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġon Login Success Handler , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġfail (" Should Ġhave Ġcalled Ġsuccess ") ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġverify ( exact ly Ġ= Ġ 1 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client . perform Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ subscribers / identify ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġbody , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` log In Ġcorrectly Ġparses Ġpurch aser Info `() Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġnew App UserID Ġ= Ġ" new Id " ĊĠĠĠĠĠĠĠ Ġval Ġrequest Body Ġ= Ġmap Of ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" new _ app _ user _ id " Ġto Ġnew App UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" app _ user _ id " Ġto Ġapp UserID ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġresult Body Ġ= ĠResponse s . valid Full P urch aser Response ĊĠĠĠĠĠĠĠ Ġmock Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ subscribers / identify ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġrequest Body , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 1 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġresult Body , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġshould Mock P urch aser Info Ġ= Ġfalse ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġexpected P urch aser Info Ġ= ĠJSONObject ( result Body ). build P urch aser Info () ĊĊĠĠĠĠĠĠĠ Ġbackend . log In ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnew App UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġon Login Success Handler , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġfail (" Should Ġhave Ġcalled Ġsuccess ") ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ ĠassertThat ( received P urch aser Info ). isEqualTo ( expected P urch aser Info ) ĊĠĠĠĠĠĠĠ ĠassertThat ( received Created ). isEqualTo ( true ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` log In Ġcalls ĠOn Error Ġif Ġpurch aser Info Ġcan 't Ġbe Ġparsed `() Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġnew App UserID Ġ= Ġ" new Id " ĊĠĠĠĠĠĠĠ Ġval Ġrequest Body Ġ= Ġmap Of ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" new _ app _ user _ id " Ġto Ġnew App UserID ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġresult Body Ġ= Ġ"{} " ĊĠĠĠĠĠĠĠ Ġmock Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ subscribers /$ app UserID / identify ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġrequest Body , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 1 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġresult Body , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġshould Mock P urch aser Info Ġ= Ġfalse ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġbackend . log In ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnew App UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ{ Ġ_, Ġ_ Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġfail (" Should Ġhave Ġcalled Ġsuccess ") ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠ Ġon Receive Login ErrorHandler ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ ĠassertThat ( received Error ). isNotNull ĊĠĠĠĠĠĠĠ ĠassertThat ( received Error ?. code ). isEqualTo ( P urchases ErrorCode . Unknown Error ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` log In Ġreturns Ġcreated Ġtrue Ġif Ġstatus Ġis Ġ 2 0 1 `() Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġnew App UserID Ġ= Ġ" new Id " ĊĠĠĠĠĠĠĠ Ġval Ġrequest Body Ġ= Ġmap Of ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" new _ app _ user _ id " Ġto Ġnew App UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" app _ user _ id " Ġto Ġapp UserID ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġresult Body Ġ= ĠResponse s . valid Full P urch aser Response ĊĠĠĠĠĠĠĠ Ġmock Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ subscribers / identify ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġrequest Body , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 1 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġresult Body ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġbackend . log In ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnew App UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġon Login Success Handler , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġfail (" Should Ġhave Ġcalled Ġsuccess ") ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ ĠassertThat ( received Created ). isTrue () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` log In Ġreturns Ġcreated Ġfalse Ġif Ġstatus Ġisn 't Ġ 2 0 1 `() Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġnew App UserID Ġ= Ġ" new Id " ĊĠĠĠĠĠĠĠ Ġval Ġrequest Body Ġ= Ġmap Of ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" new _ app _ user _ id " Ġto Ġnew App UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" app _ user _ id " Ġto Ġapp UserID ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġresult Body Ġ= ĠResponse s . valid Full P urch aser Response ĊĠĠĠĠĠĠĠ Ġmock Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ subscribers / identify ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġrequest Body , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġresult Body ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġbackend . log In ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnew App UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġon Login Success Handler , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġfail (" Should Ġhave Ġcalled Ġsuccess ") ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ ĠassertThat ( received Created ). isFalse ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` given Ġmultiple Ġpost Ġcalls Ġfor Ġsame Ġsubscriber Ġdifferent Ġstore Ġuser ĠID , Ġboth Ġare Ġtriggered `() Ġ{ ĊĊĠĠĠĠĠĠĠ Ġval Ġlock Ġ= ĠCount DownLatch ( 2 ) ĊĠĠĠĠĠĠĠ Ġval Ġreceipt Info Ġ= ĠRe ceipt Info ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġproduct ID Ġ= Ġproduct ID ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġ( fetch Token , Ġ_) Ġ= Ġmock Post Receipt Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġasync Backend . post Receipt Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpurchase Token Ġ= Ġfetch Token , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID Ġ= Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsubscriber Attributes Ġ= Ġempty Map (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġ{ Ġ_, Ġ_ Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġpost Receipt Error Callback ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġ( fetch Token 1 , Ġ_) Ġ= Ġmock Post Receipt Response ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Code Ġ= Ġ 2 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġclient Exception Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresult Body Ġ= Ġnull , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelayed Ġ= Ġtrue , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġ" store _ app _ user _ id " ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġasync Backend . post Receipt Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpurchase Token Ġ= Ġfetch Token 1 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp UserID Ġ= Ġapp UserID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Restore Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġobserver Mode Ġ= Ġfalse , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsubscriber Attributes Ġ= Ġempty Map (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreceipt Info Ġ= Ġreceipt Info , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstore App UserID Ġ= Ġ" store _ app _ user _ id ", ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonSuccess Ġ= Ġ{ Ġ_, Ġ_ Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlock . count Down () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠ ĠonError Ġ= Ġpost Receipt Error Callback ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġlock . await () ĊĠĠĠĠĠĠĠ ĠassertThat ( lock . count ). isEqualTo ( 0 ) ĊĠĠĠĠĠĠĠ Ġverify ( exact ly Ġ= Ġ 2 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmock Client . perform Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ"/ receipt s ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany (), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġany () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġmock Product Details ( ĊĠĠĠĠĠĠĠ Ġprice : ĠLong Ġ= Ġ 2 5 0 0 0 0 0 0 , ĊĠĠĠĠĠĠĠ Ġduration : ĠString Ġ= Ġ" P 1 M ", ĊĠĠĠĠĠĠĠ Ġintro Duration : ĠString Ġ= Ġ" P 1 M ", ĊĠĠĠĠĠĠĠ Ġtrial Duration : ĠString Ġ= Ġ" P 1 M " ĊĠĠĠ Ġ): ĠProduct Details Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġproduct Details Ġ= Ġmock k < Product Details >() ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġproduct Details . price Amount Micro s Ġ} Ġreturns Ġprice ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġproduct Details . price Currency Code Ġ} Ġreturns Ġ" USD " ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġproduct Details . subscription Period Ġ} Ġreturns Ġduration ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġproduct Details . intro duct ory Price Period Ġ} Ġreturns Ġintro Duration ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġproduct Details . free Trial Period Ġ} Ġreturns Ġtrial Duration ĊĠĠĠĠĠĠĠ Ġreturn Ġproduct Details ĊĠĠĠ Ġ} Ċ Ċ } Ċ