package Ġde . r ki . cor on aw arn app . app config . sources . local Ċ Ċ import Ġandroid . content . Context Ċ import Ġde . r ki . cor on aw arn app . app config . internal . Internal Config Data Ċ import Ġde . r ki . cor on aw arn app . util . TimeStamp er Ċ import Ġde . r ki . cor on aw arn app . util . serialization . Serialization Module Ċ import Ġio . kot est . matchers . shouldBe Ċ import Ġio . mock k . Mock K Annotations Ċ import Ġio . mock k . clear All Mocks Ċ import Ġio . mock k . every Ċ import Ġio . mock k . impl . annotations . Mock K Ċ import Ġio . mock k . mock k Ċ import Ġkotlinx . coroutines . test . run Blocking Test Ċ import Ġok io . ByteString . Companion . decode Hex Ċ import Ġok io . ByteString . Companion . to ByteString Ċ import Ġorg . joda . time . Duration Ċ import Ġorg . joda . time . Instant Ċ import Ġorg . junit . jupiter . api . After Each Ċ import Ġorg . junit . jupiter . api . BeforeEach Ċ import Ġorg . junit . jupiter . api . Test Ċ import Ġtest helpers . Base IO Test Ċ import Ġtest helpers . extensions . to Comparable Json Ċ import Ġjava . io . File Ċ Ċ class ĠApp Config Storage Test Ġ: ĠBase IO Test () Ġ{ ĊĊĠĠĠ Ġ@ Mock K Ġprivate Ġlateinit Ġvar Ġcontext : ĠContext ĊĠĠĠ Ġ@ Mock K Ġprivate Ġlateinit Ġvar Ġtime Stamp er : ĠTime Stamp er ĊĊĠĠĠ Ġprivate Ġval Ġtest Dir Ġ= ĠFile ( IO _ TEST _ BASE DIR , Ġthis :: class . java . simple Name ) ĊĠĠĠ Ġprivate Ġval Ġprivate Files Ġ= ĠFile ( test Dir , Ġ" files ") ĊĠĠĠ Ġprivate Ġval Ġstorage Dir Ġ= ĠFile ( private Files , Ġ" app config _ storage ") ĊĠĠĠ Ġprivate Ġval Ġlegacy ConfigPath Ġ= ĠFile ( storage Dir , Ġ" app config ") ĊĠĠĠ Ġprivate Ġval Ġconfig Path Ġ= ĠFile ( storage Dir , Ġ" app config . json ") ĊĊĠĠĠ Ġprivate Ġval Ġtest Config Download Ġ= ĠInternal Config Data ( ĊĠĠĠĠĠĠĠ Ġraw Data Ġ= ĠAPP CONFIG _ RAW , ĊĠĠĠĠĠĠĠ Ġserver Time Ġ= ĠInstant . parse (" 2 0 2 0 - 1 1 - 0 3 T 0 5 : 3 5 : 1 6 . 0 0 0 Z "), ĊĠĠĠĠĠĠĠ Ġlocal Offset Ġ= ĠDuration . standard Hours ( 1 ), ĊĠĠĠĠĠĠĠ Ġet ag Ġ= Ġ" I Ġam Ġan ĠE Tag Ġ:) !", ĊĠĠĠĠĠĠĠ Ġcache Validity Ġ= ĠDuration . standard Seconds ( 1 2 3 ) ĊĠĠĠ Ġ) ĊĊĠĠĠ Ġ@ BeforeEach ĊĠĠĠ Ġfun Ġsetup () Ġ{ ĊĠĠĠĠĠĠĠ ĠMock K Annotations . init ( this ) ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġcontext . files Dir Ġ} Ġreturns Ġprivate Files ĊĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġtime Stamp er . now UTC Ġ} Ġreturns ĠInstant . of Epoch Milli ( 1 2 3 4 ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ After Each ĊĠĠĠ Ġfun Ġteardown () Ġ{ ĊĠĠĠĠĠĠĠ Ġclear All Mocks () ĊĠĠĠĠĠĠĠ Ġtest Dir . delete Re cursively () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġcreate Storage () Ġ= ĠApp Config Storage ( ĊĠĠĠĠĠĠĠ Ġcontext Ġ= Ġcontext , ĊĠĠĠĠĠĠĠ Ġtime Stamp er Ġ= Ġtime Stamp er , ĊĠĠĠĠĠĠĠ Ġbase Gson Ġ= ĠSerialization Module (). base Gson () ĊĠĠĠ Ġ) ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` simple Ġread Ġand Ġwrite Ġconfig `() Ġ= Ġrun Blocking Test Ġ{ ĊĠĠĠĠĠĠĠ Ġconfig Path . exists () ĠshouldBe Ġfalse ĊĠĠĠĠĠĠĠ Ġval Ġstorage Ġ= Ġcreate Storage () ĊĠĠĠĠĠĠĠ Ġconfig Path . exists () ĠshouldBe Ġfalse ĊĊĠĠĠĠĠĠĠ Ġstorage . set Stored Config ( test Config Download ) ĊĊĠĠĠĠĠĠĠ Ġconfig Path . exists () ĠshouldBe Ġtrue ĊĠĠĠĠĠĠĠ Ġconfig Path . read Text (). to Comparable Json () ĠshouldBe Ġ""" ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" raw Data ": Ġ"$ APP CONFIG _ BASE 6 4 ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" etag ": Ġ" I Ġam Ġan ĠE Tag Ġ:) !", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" server Time ": Ġ 1 6 0 4 3 8 1 7 1 6 0 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" local Offset ": Ġ 3 6 0 0 0 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" cache Validity ": Ġ 1 2 3 0 0 0 ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ""" . to Comparable Json () ĊĊĠĠĠĠĠĠĠ Ġstorage . get Stored Config () ĠshouldBe Ġtest Config Download ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` rest oring Ġfrom Ġstorage `() Ġ= Ġrun Blocking Test Ġ{ ĊĠĠĠĠĠĠĠ Ġconfig Path . parent File !!. mkdir s () ĊĠĠĠĠĠĠĠ Ġconfig Path . write Text ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ""" ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" raw Data ": Ġ"$ APP CONFIG _ BASE 6 4 ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" etag ": Ġ" I Ġam Ġan ĠE Tag Ġ:) !", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" server Time ": Ġ 1 6 0 4 3 8 1 7 1 6 0 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" local Offset ": Ġ 3 6 0 0 0 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" cache Validity ": Ġ 1 2 3 0 0 0 ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ""" . trim Indent () ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġstorage Ġ= Ġcreate Storage () ĊĠĠĠĠĠĠĠ Ġstorage . get Stored Config () ĠshouldBe Ġtest Config Download ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` null ing Ġand Ġover writing `() Ġ= Ġrun Blocking Test Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġstorage Ġ= Ġcreate Storage () ĊĠĠĠĠĠĠĠ Ġconfig Path . exists () ĠshouldBe Ġfalse ĊĊĠĠĠĠĠĠĠ Ġstorage . get Stored Config () ĠshouldBe Ġnull ĊĠĠĠĠĠĠĠ Ġstorage . set Stored Config ( null ) ĊĠĠĠĠĠĠĠ Ġconfig Path . exists () ĠshouldBe Ġfalse ĊĊĠĠĠĠĠĠĠ Ġstorage . get Stored Config () ĠshouldBe Ġnull ĊĠĠĠĠĠĠĠ Ġstorage . set Stored Config ( test Config Download ) ĊĠĠĠĠĠĠĠ Ġstorage . get Stored Config () ĠshouldBe Ġtest Config Download ĊĊĠĠĠĠĠĠĠ Ġconfig Path . exists () ĠshouldBe Ġtrue ĊĠĠĠĠĠĠĠ Ġconfig Path . read Text (). to Comparable Json () ĠshouldBe Ġ""" ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" raw Data ": Ġ"$ APP CONFIG _ BASE 6 4 ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" etag ": Ġ" I Ġam Ġan ĠE Tag Ġ:) !", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" server Time ": Ġ 1 6 0 4 3 8 1 7 1 6 0 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" local Offset ": Ġ 3 6 0 0 0 0 0 , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" cache Validity ": Ġ 1 2 3 0 0 0 ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ""" . to Comparable Json () ĊĊĠĠĠĠĠĠĠ Ġstorage . set Stored Config ( null ) ĊĠĠĠĠĠĠĠ Ġstorage . get Stored Config () ĠshouldBe Ġnull ĊĠĠĠĠĠĠĠ Ġconfig Path . exists () ĠshouldBe Ġfalse ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` if Ġno Ġfallback Ġexists , Ġbut Ġwe Ġhave Ġa Ġlegacy Ġconfig , Ġuse Ġthat `() Ġ= Ġrun Blocking Test Ġ{ ĊĠĠĠĠĠĠĠ Ġconfig Path . exists () ĠshouldBe Ġfalse ĊĠĠĠĠĠĠĠ Ġlegacy ConfigPath . exists () ĠshouldBe Ġfalse ĊĊĠĠĠĠĠĠĠ Ġlegacy ConfigPath . parent File !!. mkdir s () ĊĠĠĠĠĠĠĠ Ġlegacy ConfigPath . write Bytes ( APP CONFIG _ RAW ) ĊĊĠĠĠĠĠĠĠ Ġval Ġstorage Ġ= Ġcreate Storage () ĊĊĠĠĠĠĠĠĠ Ġstorage . get Stored Config () ĠshouldBe ĠInternal Config Data ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġraw Data Ġ= ĠAPP CONFIG _ RAW , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġserver Time Ġ= ĠInstant . of Epoch Milli ( 1 2 3 4 ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlocal Offset Ġ= ĠDuration . ZERO , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġet ag Ġ= Ġ" I Ġam Ġan ĠE Tag Ġ:) !", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġcache Validity Ġ= ĠDuration . standard Minutes ( 5 ) ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` writing Ġa Ġnew Ġconfig Ġdeletes Ġany Ġlegacy Ġconfigs config `() Ġ= Ġrun Blocking Test Ġ{ ĊĠĠĠĠĠĠĠ Ġlegacy ConfigPath . parent File !!. mkdir s () ĊĠĠĠĠĠĠĠ Ġlegacy ConfigPath . write Bytes ( APP CONFIG _ RAW ) ĊĠĠĠĠĠĠĠ Ġconfig Path . exists () ĠshouldBe Ġfalse ĊĊĠĠĠĠĠĠĠ Ġval Ġstorage Ġ= Ġcreate Storage () ĊĠĠĠĠĠĠĠ Ġstorage . set Stored Config ( test Config Download ) ĊĊĠĠĠĠĠĠĠ Ġlegacy ConfigPath . exists () ĠshouldBe Ġfalse ĊĠĠĠĠĠĠĠ Ġconfig Path . exists () ĠshouldBe Ġtrue ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` return Ġnull Ġon Ġerrors `() Ġ= Ġrun Blocking Test Ġ{ ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġtime Stamp er . now UTC Ġ} Ġthrows ĠException () ĊĊĠĠĠĠĠĠĠ Ġval Ġstorage Ġ= Ġcreate Storage () ĊĠĠĠĠĠĠĠ Ġstorage . get Stored Config () ĠshouldBe Ġnull ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` return Ġnull Ġon Ġinvalid Ġjson Ġand Ġdelete Ġconfig Ġfile `() Ġ= Ġrun Blocking Test Ġ{ ĊĠĠĠĠĠĠĠ Ġconfig Path . parent File !!. mkdir s () ĊĠĠĠĠĠĠĠ Ġconfig Path . write Text ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ""" ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ""" . trim Indent () ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġstorage Ġ= Ġcreate Storage () ĊĠĠĠĠĠĠĠ Ġstorage . get Stored Config () ĠshouldBe Ġnull ĊĊĠĠĠĠĠĠĠ Ġconfig Path . exists () ĠshouldBe Ġfalse ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` return Ġnull Ġon Ġempty Ġfile Ġand Ġdelete Ġconfig Ġfile `() Ġ{ ĊĠĠĠĠĠĠĠ Ġconfig Path . parent File !!. mkdir s () ĊĠĠĠĠĠĠĠ Ġconfig Path . createNew File () ĊĊĠĠĠĠĠĠĠ Ġval Ġstorage Ġ= Ġcreate Storage () ĊĊĠĠĠĠĠĠĠ Ġrun Blocking Test Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstorage . get Stored Config () ĠshouldBe Ġnull ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġconfig Path . exists () ĠshouldBe Ġfalse ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` catch Ġerrors Ġwhen Ġtrying Ġto Ġsave Ġthe Ġconfig `() Ġ{ ĊĠĠĠĠĠĠĠ Ġconfig Path . parent File !!. mkdir s () ĊĠĠĠĠĠĠĠ Ġconfig Path . createNew File () ĊĊĠĠĠĠĠĠĠ Ġval Ġstorage Ġ= Ġcreate Storage () ĊĊĠĠĠĠĠĠĠ Ġrun Blocking Test Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstorage . set Stored Config ( mock k ()) ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġconfig Path . exists () ĠshouldBe Ġtrue ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġcompanion Ġobject Ġ{ ĊĠĠĠĠĠĠĠ Ġprivate Ġval ĠAPP CONFIG _ RAW Ġ= Ġ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ" 0 8 0 b 1 2 4 d 0 a 2 3 0 a 0 3 4 c 4 f 5 7 1 8 0 f 2 2 1 a 6 8 7 4 7 4 7 0 7 3 3 a 2 f 2 f 7 7 7 7 7 7 " Ġ+ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" 2 e 6 3 6 f 7 2 6 f 6 e 6 1 7 7 6 1 7 2 6 e 2 e 6 1 7 0 7 0 0 a 2 6 0 a 0 4 4 8 4 9 4 7 4 8 1 0 0 f 1 8 4 8 2 2 1 a 6 8 7 4 7 4 7 0 7 3 3 a 2 f 2 f 7 7 7 7 7 7 2 e 6 3 6 f 7 " Ġ+ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" 2 6 f 6 e 6 1 7 7 6 1 7 2 6 e 2 e 6 1 7 0 7 0 1 a 6 4 0 a 1 0 0 8 0 1 1 0 0 2 1 8 0 3 2 0 0 4 2 8 0 5 3 0 0 6 3 8 0 7 4 0 0 8 1 1 0 0 0 0 0 0 0 0 0 0 0 0 4 9 4 0 1 a 0 a 2 0 " Ġ+ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" 0 1 2 8 0 1 3 0 0 1 3 8 0 1 4 0 0 1 2 1 0 0 0 0 0 0 0 0 0 0 0 0 4 9 4 0 2 a 1 0 0 8 0 5 1 0 0 5 1 8 0 5 2 0 0 5 2 8 0 5 3 0 0 5 3 8 0 5 4 0 0 5 3 1 0 0 0 0 0 0 0 0 0 0 0 0 3 " Ġ+ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" 4 4 0 3 a 0 e 1 0 0 1 1 8 0 1 2 0 0 1 2 8 0 1 3 0 0 1 3 8 0 1 4 0 0 1 4 1 0 0 0 0 0 0 0 0 0 0 0 0 4 9 4 0 2 2 1 c 0 a 0 4 0 8 3 7 1 0 3 f 1 2 1 2 0 9 0 0 0 0 0 0 0 0 0 0 0 0 " Ġ+ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" f 0 3 f 1 1 0 0 0 0 0 0 0 0 0 0 0 0 e 0 3 f 2 0 1 9 2 a 1 a 0 a 0 a 0 a 0 4 1 0 0 8 1 8 0 2 1 2 0 2 1 0 0 5 1 2 0 c 0 a 0 4 0 8 0 1 1 8 0 4 1 2 0 4 0 8 0 1 1 8 0 4 " ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ). decode Hex (). toByteArray () ĊĊĠĠĠĠĠĠĠ Ġprivate Ġval ĠAPP CONFIG _ BASE 6 4 Ġ= ĠAPP CONFIG _ RAW . to ByteString (). base 6 4 () ĊĠĠĠ Ġ} Ċ } Ċ