diff --git a/app/src/androidTest/assets/inAppPaymentsTests/configuration.json b/app/src/androidTest/assets/inAppPaymentsTests/configuration.json index f84f6c2c67..4e72ea06d7 100644 --- a/app/src/androidTest/assets/inAppPaymentsTests/configuration.json +++ b/app/src/androidTest/assets/inAppPaymentsTests/configuration.json @@ -20,7 +20,9 @@ "1000": 10000, "2000": 20000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -45,7 +47,9 @@ "1000": 40000, "2000": 80000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -70,7 +74,9 @@ "1000": 20, "2000": 40 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -95,7 +101,9 @@ "1000": 15, "2000": 30 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -120,7 +128,9 @@ "1000": 150, "2000": 300 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -145,7 +155,9 @@ "1000": 80, "2000": 160 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -170,7 +182,9 @@ "1000": 150, "2000": 300 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -195,7 +209,9 @@ "1000": 350, "2000": 700 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -220,7 +236,9 @@ "1000": 40, "2000": 80 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -245,7 +263,9 @@ "1000": 20, "2000": 30 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -271,7 +291,9 @@ "1000": 25, "2000": 50 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -296,7 +318,9 @@ "1000": 50, "2000": 100 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -322,7 +346,9 @@ "1000": 20, "2000": 40 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -347,7 +373,9 @@ "1000": 200, "2000": 400 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -373,7 +401,9 @@ "1000": 200, "2000": 400 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -398,7 +428,9 @@ "1000": 125, "2000": 250 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -423,7 +455,9 @@ "1000": 20000, "2000": 40000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -448,7 +482,9 @@ "1000": 20, "2000": 40 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -473,7 +509,9 @@ "1000": 75, "2000": 150 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -498,7 +536,9 @@ "1000": 8000, "2000": 15000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -523,7 +563,9 @@ "1000": 250, "2000": 500 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -548,7 +590,9 @@ "1000": 35000, "2000": 70000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -573,7 +617,9 @@ "1000": 150, "2000": 300 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -598,7 +644,9 @@ "1000": 10, "2000": 20 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -623,7 +671,9 @@ "1000": 500, "2000": 1000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -648,7 +698,9 @@ "1000": 300, "2000": 600 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -674,7 +726,9 @@ "1000": 1000, "2000": 2000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -699,7 +753,9 @@ "1000": 500, "2000": 1000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -724,7 +780,9 @@ "1000": 4000, "2000": 8000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -749,7 +807,9 @@ "1000": 40, "2000": 80 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -774,7 +834,9 @@ "1000": 10, "2000": 20 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -799,7 +861,9 @@ "1000": 5000, "2000": 10000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -824,7 +888,9 @@ "1000": 30, "2000": 60 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -849,7 +915,9 @@ "1000": 400, "2000": 800 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -874,7 +942,9 @@ "1000": 100, "2000": 200 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -899,7 +969,9 @@ "1000": 1000, "2000": 2000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -924,7 +996,9 @@ "1000": 15, "2000": 30 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -949,7 +1023,9 @@ "1000": 20, "2000": 40 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -974,7 +1050,9 @@ "1000": 35, "2000": 70 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -999,7 +1077,9 @@ "1000": 1000, "2000": 2000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1024,7 +1104,9 @@ "1000": 100, "2000": 200 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -1050,7 +1132,9 @@ "1000": 300, "2000": 600 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1075,7 +1159,9 @@ "1000": 100000, "2000": 200000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1100,7 +1186,9 @@ "1000": 600, "2000": 1200 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1125,7 +1213,9 @@ "1000": 1000, "2000": 2000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1150,7 +1240,9 @@ "1000": 40, "2000": 80 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1175,7 +1267,9 @@ "1000": 60, "2000": 125 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1200,7 +1294,9 @@ "1000": 300, "2000": 700 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -1226,7 +1322,9 @@ "1000": 100000, "2000": 200000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1251,7 +1349,9 @@ "1000": 1000, "2000": 2000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1276,7 +1376,9 @@ "1000": 1000, "2000": 2000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1301,7 +1403,9 @@ "1000": 10, "2000": 20 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1326,7 +1430,9 @@ "1000": 500, "2000": 1000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -1352,7 +1458,9 @@ "1000": 600, "2000": 1200 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -1378,7 +1486,9 @@ "1000": 60000, "2000": 125000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1403,7 +1513,9 @@ "1000": 1500, "2000": 3000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1428,7 +1540,9 @@ "1000": 1500, "2000": 3000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1453,7 +1567,9 @@ "1000": 40000, "2000": 80000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1478,7 +1594,9 @@ "1000": 500, "2000": 1000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1503,7 +1621,9 @@ "1000": 10, "2000": 20 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -1529,7 +1649,9 @@ "1000": 1250, "2000": 2500 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1554,7 +1676,9 @@ "1000": 10, "2000": 20 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1579,7 +1703,9 @@ "1000": 15, "2000": 30 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -1605,7 +1731,9 @@ "1000": 400, "2000": 800 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1630,7 +1758,9 @@ "1000": 800, "2000": 1600 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1655,7 +1785,9 @@ "1000": 5000, "2000": 10000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1680,7 +1812,9 @@ "1000": 1000, "2000": 2000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1705,7 +1839,9 @@ "1000": 100000, "2000": 200000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1730,7 +1866,9 @@ "1000": 15, "2000": 30 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1755,7 +1893,9 @@ "1000": 5000, "2000": 10000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1780,7 +1920,9 @@ "1000": 1500, "2000": 3000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1805,7 +1947,9 @@ "1000": 1000, "2000": 2000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1830,7 +1974,9 @@ "1000": 10, "2000": 20 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -1856,7 +2002,9 @@ "1000": 1500, "2000": 3000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1881,7 +2029,9 @@ "1000": 150, "2000": 300 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1906,7 +2056,9 @@ "1000": 20000, "2000": 40000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1931,7 +2083,9 @@ "1000": 200000, "2000": 400000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -1956,7 +2110,9 @@ "1000": 15, "2000": 30 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -1982,7 +2138,9 @@ "1000": 40, "2000": 80 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -2008,7 +2166,9 @@ "1000": 2000, "2000": 4000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2033,7 +2193,9 @@ "1000": 60, "2000": 125 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2058,7 +2220,9 @@ "1000": 40000, "2000": 80000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2083,7 +2247,9 @@ "1000": 200, "2000": 400 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2108,7 +2274,9 @@ "1000": 125000, "2000": 250000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2133,7 +2301,9 @@ "1000": 10, "2000": 20 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2158,7 +2328,9 @@ "1000": 5000, "2000": 10000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2183,7 +2355,9 @@ "1000": 100, "2000": 200 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2208,7 +2382,9 @@ "1000": 7, "2000": 15 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2233,7 +2409,9 @@ "1000": 150, "2000": 300 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2258,7 +2436,9 @@ "1000": 15000, "2000": 30000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2283,7 +2463,9 @@ "1000": 100, "2000": 200 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2308,7 +2490,9 @@ "1000": 75, "2000": 150 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -2334,7 +2518,9 @@ "1000": 10000, "2000": 20000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2359,7 +2545,9 @@ "1000": 40, "2000": 80 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2384,7 +2572,9 @@ "1000": 10, "2000": 20 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL", @@ -2412,7 +2602,9 @@ "1000": 150, "2000": 300 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2437,7 +2629,9 @@ "1000": 75, "2000": 150 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -2463,7 +2657,9 @@ "1000": 15, "2000": 30 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -2489,7 +2685,9 @@ "1000": 15, "2000": 30 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2514,7 +2712,9 @@ "1000": 20000, "2000": 40000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2539,7 +2739,9 @@ "1000": 300, "2000": 800 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2564,7 +2766,9 @@ "1000": 100, "2000": 200 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -2590,7 +2794,9 @@ "1000": 10, "2000": 20 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2615,7 +2821,9 @@ "1000": 50, "2000": 100 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2640,7 +2848,9 @@ "1000": 3000, "2000": 5000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2665,7 +2875,9 @@ "1000": 4000, "2000": 8000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2690,7 +2902,9 @@ "1000": 6000, "2000": 12000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2715,7 +2929,9 @@ "1000": 250, "2000": 500 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -2741,7 +2957,9 @@ "1000": 2000, "2000": 4000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2766,7 +2984,9 @@ "1000": 25, "2000": 50 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2791,7 +3011,9 @@ "1000": 1000, "2000": 2000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2816,7 +3038,9 @@ "1000": 20, "2000": 40 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2841,7 +3065,9 @@ "1000": 4000, "2000": 8000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2866,7 +3092,9 @@ "1000": 200, "2000": 400 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2891,7 +3119,9 @@ "1000": 150, "2000": 300 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2916,7 +3146,9 @@ "1000": 40, "2000": 80 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2941,7 +3173,9 @@ "1000": 60, "2000": 125 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2966,7 +3200,9 @@ "1000": 2500, "2000": 5000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -2991,7 +3227,9 @@ "1000": 900, "2000": 1800 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -3016,7 +3254,9 @@ "1000": 150, "2000": 300 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -3041,7 +3281,9 @@ "1000": 750, "2000": 1500 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -3066,7 +3308,9 @@ "1000": 20, "2000": 40 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -3091,7 +3335,9 @@ "1000": 10000, "2000": 20000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -3116,7 +3362,9 @@ "1000": 1200, "2000": 2400 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -3141,7 +3389,9 @@ "1000": 4000, "2000": 8000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -3166,7 +3416,9 @@ "1000": 1000, "2000": 2000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -3192,7 +3444,9 @@ "1000": 25000, "2000": 50000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -3217,7 +3471,9 @@ "1000": 40, "2000": 80 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -3243,7 +3499,9 @@ "1000": 10, "2000": 20 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -3269,7 +3527,9 @@ "1000": 75, "2000": 150 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] @@ -3294,7 +3554,9 @@ "1000": 4000, "2000": 8000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD", "PAYPAL" @@ -3320,7 +3582,9 @@ "1000": 20000, "2000": 40000 }, - "backupSubscription": {}, + "backupSubscription": { + "201": 1000 + }, "supportedPaymentMethods": [ "CARD" ] diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsCheckoutActivityTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsCheckoutActivityTest.kt index 3ca93c1080..f9e9f96c9c 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsCheckoutActivityTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsCheckoutActivityTest.kt @@ -20,9 +20,12 @@ import assertk.assertions.isEqualTo import assertk.assertions.isNull import io.mockk.coEvery import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkObject import io.mockk.mockkStatic import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.StandardTestDispatcher import org.junit.Before import org.junit.Rule import org.junit.Test @@ -38,9 +41,12 @@ import org.thoughtcrime.securesms.database.InAppPaymentTable import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.net.SignalNetwork +import org.thoughtcrime.securesms.testing.CoroutineDispatcherRule import org.thoughtcrime.securesms.testing.InAppPaymentsRule import org.thoughtcrime.securesms.testing.SignalActivityRule import org.thoughtcrime.securesms.util.RemoteConfig +import org.whispersystems.signalservice.api.NetworkResult import java.math.BigDecimal import java.util.Currency @@ -53,6 +59,10 @@ class MessageBackupsCheckoutActivityTest { @get:Rule val composeTestRule = createEmptyComposeRule() + private val testDispatcher = StandardTestDispatcher() + + @get:Rule val coroutineDispatcherRule = CoroutineDispatcherRule(testDispatcher) + private val purchaseResults = MutableSharedFlow() @Before @@ -61,6 +71,11 @@ class MessageBackupsCheckoutActivityTest { coEvery { AppDependencies.billingApi.queryProduct() } returns BillingProduct(price = FiatMoney(BigDecimal.ONE, Currency.getInstance("USD"))) coEvery { AppDependencies.billingApi.launchBillingFlow(any()) } returns Unit + mockkObject(SignalNetwork) + every { SignalNetwork.archive } returns mockk { + every { triggerBackupIdReservation(any(), any(), any()) } returns NetworkResult.Success(Unit) + } + mockkStatic(RemoteConfig::class) every { RemoteConfig.messageBackups } returns true } @@ -79,6 +94,8 @@ class MessageBackupsCheckoutActivityTest { composeTestRule.onNodeWithText(context.getString(R.string.MessageBackupsTypeSelectionScreen__next)).performClick() composeTestRule.waitForIdle() + testDispatcher.scheduler.advanceUntilIdle() + runBlocking { purchaseResults.emit( BillingPurchaseResult.Success( @@ -94,6 +111,8 @@ class MessageBackupsCheckoutActivityTest { composeTestRule.waitForIdle() composeTestRule.onNodeWithTag("dialog-circular-progress-indicator").assertIsDisplayed() + testDispatcher.scheduler.advanceUntilIdle() + val iap = SignalDatabase.inAppPayments.getLatestInAppPaymentByType(InAppPaymentType.RECURRING_BACKUP) assertThat(iap?.state).isEqualTo(InAppPaymentTable.State.PENDING) @@ -162,8 +181,11 @@ class MessageBackupsCheckoutActivityTest { } private fun launchCheckoutFlow(tier: MessageBackupTier? = null): ActivityScenario { - return ActivityScenario.launch( + val scenario = ActivityScenario.launch( MessageBackupsCheckoutActivity.Contract().createIntent(InstrumentationRegistry.getInstrumentation().targetContext, tier) ) + + testDispatcher.scheduler.advanceUntilIdle() + return scenario } } diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/CoroutineDispatcherRule.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/CoroutineDispatcherRule.kt new file mode 100644 index 0000000000..aff8c083b1 --- /dev/null +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/CoroutineDispatcherRule.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.testing + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.test.TestDispatcher +import org.junit.rules.ExternalResource +import org.signal.core.util.concurrent.SignalDispatchers + +/** + * Rule that allows for injection of test dispatchers when operating with ViewModels. + */ +class CoroutineDispatcherRule( + defaultDispatcher: TestDispatcher, + mainDispatcher: TestDispatcher = defaultDispatcher, + ioDispatcher: TestDispatcher = defaultDispatcher, + unconfinedDispatcher: TestDispatcher = defaultDispatcher +) : ExternalResource() { + + private val testDispatcherProvider = TestDispatcherProvider( + main = mainDispatcher, + io = ioDispatcher, + default = defaultDispatcher, + unconfined = unconfinedDispatcher + ) + + override fun before() { + SignalDispatchers.setDispatcherProvider(testDispatcherProvider) + } + + override fun after() { + SignalDispatchers.setDispatcherProvider() + } + + private class TestDispatcherProvider( + override val main: CoroutineDispatcher, + override val io: CoroutineDispatcher, + override val default: CoroutineDispatcher, + override val unconfined: CoroutineDispatcher + ) : SignalDispatchers.DispatcherProvider +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index 078649b7ee..e655a34cb4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -10,8 +10,6 @@ import android.os.Environment import android.os.StatFs import androidx.annotation.Discouraged import androidx.annotation.WorkerThread -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import okio.ByteString import okio.ByteString.Companion.toByteString import org.greenrobot.eventbus.EventBus @@ -305,9 +303,7 @@ object BackupRepository { } val paidType = try { - withContext(Dispatchers.IO) { - getPaidType() - } + getPaidType() } catch (e: IOException) { Log.w(TAG, "Failed to retrieve paid type.", e) return false @@ -1415,7 +1411,8 @@ object BackupRepository { } } - private suspend fun getFreeType(): MessageBackupsType.Free { + @WorkerThread + private fun getFreeType(): MessageBackupsType.Free { val config = getSubscriptionsConfiguration() return MessageBackupsType.Free( @@ -1426,6 +1423,7 @@ object BackupRepository { private suspend fun getPaidType(): MessageBackupsType.Paid? { val config = getSubscriptionsConfiguration() val product = AppDependencies.billingApi.queryProduct() ?: return null + val backupLevelConfiguration = config.backupConfiguration.backupLevelConfigurationMap[SubscriptionsConfiguration.BACKUPS_LEVEL] ?: return null return MessageBackupsType.Paid( @@ -1435,12 +1433,11 @@ object BackupRepository { ) } - private suspend fun getSubscriptionsConfiguration(): SubscriptionsConfiguration { - val serviceResponse = withContext(Dispatchers.IO) { - AppDependencies - .donationsService - .getDonationsConfiguration(Locale.getDefault()) - } + @WorkerThread + private fun getSubscriptionsConfiguration(): SubscriptionsConfiguration { + val serviceResponse = AppDependencies + .donationsService + .getDonationsConfiguration(Locale.getDefault()) if (serviceResponse.result.isEmpty) { if (serviceResponse.applicationError.isPresent) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt index f6057c6840..b0b785140b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt @@ -9,7 +9,9 @@ import androidx.fragment.app.FragmentManager import androidx.lifecycle.Lifecycle import androidx.lifecycle.coroutineScope import androidx.lifecycle.repeatOnLifecycle +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.thoughtcrime.securesms.backup.v2.BackupRepository import org.thoughtcrime.securesms.keyvalue.SignalStore @@ -25,7 +27,7 @@ object BackupAlertDelegate { BackupAlertBottomSheet.create(BackupAlert.BackupFailed).show(fragmentManager, null) } else if (BackupRepository.shouldDisplayCouldNotCompleteBackupSheet()) { BackupAlertBottomSheet.create(BackupAlert.CouldNotCompleteBackup(daysSinceLastBackup = SignalStore.backup.daysSinceLastBackup)).show(fragmentManager, null) - } else if (BackupRepository.shouldDisplayYourMediaWillBeDeletedTodaySheet()) { + } else if (withContext(Dispatchers.IO) { BackupRepository.shouldDisplayYourMediaWillBeDeletedTodaySheet() }) { BackupAlertBottomSheet.create(BackupAlert.MediaWillBeDeletedToday).show(fragmentManager, null) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt index d07bf280ec..ad3a0b50af 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt @@ -8,7 +8,6 @@ package org.thoughtcrime.securesms.backup.v2.ui.subscription import androidx.annotation.WorkerThread import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.flow.MutableStateFlow @@ -23,6 +22,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.reactive.asFlow import kotlinx.coroutines.withContext import org.signal.core.util.billing.BillingPurchaseResult +import org.signal.core.util.concurrent.SignalDispatchers import org.signal.core.util.logging.Log import org.signal.donations.InAppPaymentType import org.signal.donations.PaymentSourceType @@ -70,7 +70,7 @@ class MessageBackupsFlowViewModel( check(SignalStore.backup.backupTier != MessageBackupTier.PAID) { "This screen does not support cancellation or downgrades." } viewModelScope.launch { - val result = withContext(Dispatchers.IO) { + val result = withContext(SignalDispatchers.IO) { BackupRepository.triggerBackupIdReservation() } @@ -79,20 +79,22 @@ class MessageBackupsFlowViewModel( internalStateFlow.update { it.copy(paymentReadyState = MessageBackupsFlowState.PaymentReadyState.READY) } } - result.runOnStatusCodeError { - Log.d(TAG, "Failed to trigger backup id reservation. ($it)") + result.runOnStatusCodeError { code -> + Log.d(TAG, "Failed to trigger backup id reservation. ($code)") internalStateFlow.update { it.copy(paymentReadyState = MessageBackupsFlowState.PaymentReadyState.FAILED) } } } viewModelScope.launch { - internalStateFlow.update { - it.copy( - availableBackupTypes = BackupRepository.getAvailableBackupsTypes( - if (!RemoteConfig.messageBackups) emptyList() else listOf(MessageBackupTier.FREE, MessageBackupTier.PAID) - ) + val availableBackupTypes = withContext(SignalDispatchers.IO) { + BackupRepository.getAvailableBackupsTypes( + if (!RemoteConfig.messageBackups) emptyList() else listOf(MessageBackupTier.FREE, MessageBackupTier.PAID) ) } + + internalStateFlow.update { + it.copy(availableBackupTypes = availableBackupTypes) + } } viewModelScope.launch { @@ -218,7 +220,7 @@ class MessageBackupsFlowViewModel( check(state.selectedMessageBackupTier == MessageBackupTier.PAID) check(state.availableBackupTypes.any { it.tier == state.selectedMessageBackupTier }) - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(SignalDispatchers.IO) { internalStateFlow.update { it.copy(inAppPayment = null) } val paidFiat = AppDependencies.billingApi.queryProduct()!!.price @@ -265,7 +267,7 @@ class MessageBackupsFlowViewModel( */ @OptIn(FlowPreview::class) private suspend fun handleSuccess(result: BillingPurchaseResult.Success, inAppPaymentId: InAppPaymentTable.InAppPaymentId) { - withContext(Dispatchers.IO) { + withContext(SignalDispatchers.IO) { Log.d(TAG, "Setting purchase token data on InAppPayment and InAppPaymentSubscriber.") ensureSubscriberIdForBackups(IAPSubscriptionId.GooglePlayBillingPurchaseToken(result.purchaseToken)) @@ -287,7 +289,7 @@ class MessageBackupsFlowViewModel( InAppPaymentPurchaseTokenJob.createJobChain(inAppPayment).enqueue() } - val terminalInAppPayment = withContext(Dispatchers.IO) { + val terminalInAppPayment = withContext(SignalDispatchers.IO) { Log.d(TAG, "Awaiting completion of job chain for up to 10 seconds.") InAppPaymentsRepository.observeUpdates(inAppPaymentId).asFlow() .filter { it.state == InAppPaymentTable.State.END } diff --git a/core-util/src/main/java/org/signal/core/util/concurrent/SignalDispatchers.kt b/core-util/src/main/java/org/signal/core/util/concurrent/SignalDispatchers.kt new file mode 100644 index 0000000000..f3d315ce30 --- /dev/null +++ b/core-util/src/main/java/org/signal/core/util/concurrent/SignalDispatchers.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.core.util.concurrent + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + +/** + * [Dispatchers] wrapper to allow tests to inject test dispatchers. + */ +object SignalDispatchers { + + private var dispatcherProvider: DispatcherProvider = DefaultDispatcherProvider + + fun setDispatcherProvider(dispatcherProvider: DispatcherProvider = DefaultDispatcherProvider) { + this.dispatcherProvider = dispatcherProvider + } + + val Main get() = dispatcherProvider.main + val IO get() = dispatcherProvider.io + val Default get() = dispatcherProvider.default + val Unconfined get() = dispatcherProvider.unconfined + + interface DispatcherProvider { + val main: CoroutineDispatcher + val io: CoroutineDispatcher + val default: CoroutineDispatcher + val unconfined: CoroutineDispatcher + } + + private object DefaultDispatcherProvider : DispatcherProvider { + override val main: CoroutineDispatcher = Dispatchers.Main + override val io: CoroutineDispatcher = Dispatchers.IO + override val default: CoroutineDispatcher = Dispatchers.Default + override val unconfined: CoroutineDispatcher = Dispatchers.Unconfined + } +}