mirror of
https://github.com/ProtonMail/protoncore_android.git
synced 2026-06-14 09:54:49 +00:00
test(coreexample): UI tests for Google In App Purchases.
This commit is contained in:
+1
-1
@@ -167,7 +167,7 @@ internal class SignupViewModel @Inject constructor(
|
||||
}.catch { error ->
|
||||
emit(State.Error.Message(error.message))
|
||||
}.onEach {
|
||||
_state.tryEmit(it)
|
||||
_state.emit(it)
|
||||
}.launchIn(viewModelScope)
|
||||
|
||||
fun register(caller: ActivityResultCaller) {
|
||||
|
||||
@@ -141,6 +141,21 @@ fun setupFlavors(testedExtension: TestedExtension) {
|
||||
buildConfigField("String", buildConfigFieldKeys.HV3_HOST, hv3Host.toBuildConfigValue())
|
||||
buildConfigField("String", buildConfigFieldKeys.QUARK_HOST, quarkHost.toBuildConfigValue())
|
||||
}
|
||||
productFlavors.register("mock") {
|
||||
buildConfigField("String", buildConfigFieldKeys.API_HOST, "api.mock".toBuildConfigValue())
|
||||
buildConfigField("String", buildConfigFieldKeys.HV3_HOST, "verify.mock".toBuildConfigValue())
|
||||
buildConfigField("String", buildConfigFieldKeys.QUARK_HOST, "quark.mock".toBuildConfigValue())
|
||||
buildConfigField("Boolean", buildConfigFieldKeys.USE_DEFAULT_PINS, false.toBuildConfigValue())
|
||||
|
||||
dimension = flavorDimensions.env
|
||||
testInstrumentationRunner = "me.proton.core.test.android.ProtonTestRunner"
|
||||
testInstrumentationRunnerArguments["clearPackageData"] = "true"
|
||||
|
||||
testOptions {
|
||||
animationsDisabled = true
|
||||
execution = "ANDROIDX_TEST_ORCHESTRATOR"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,12 +249,22 @@ dependencies {
|
||||
`hilt-android-compiler`
|
||||
)
|
||||
|
||||
kaptAndroidTest(
|
||||
`hilt-android-compiler`
|
||||
)
|
||||
|
||||
androidTestImplementation(
|
||||
project(Module.androidInstrumentedTest),
|
||||
`android-test-runner`,
|
||||
`hilt-android-testing`,
|
||||
`kotlin-test-junit`
|
||||
`kotlin-test-junit`,
|
||||
`mockk-android`,
|
||||
mockWebServer,
|
||||
uiautomator
|
||||
)
|
||||
|
||||
androidTestUtil(`androidx-test-orchestrator`)
|
||||
|
||||
// Lint - off temporary
|
||||
// lintChecks(project(Module.lint))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.test.android
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import androidx.test.runner.AndroidJUnitRunner
|
||||
import dagger.hilt.android.testing.HiltTestApplication
|
||||
|
||||
class ProtonTestRunner : AndroidJUnitRunner() {
|
||||
override fun newApplication(cl: ClassLoader, className: String, context: Context): Application =
|
||||
super.newApplication(cl, HiltTestApplication::class.java.name, context)
|
||||
}
|
||||
+4
-1
@@ -158,7 +158,10 @@ class SelectPlanTests : BaseTest() {
|
||||
selectPlanRobot?.let {
|
||||
it.toggleExpandPlan(Dev)
|
||||
.selectPlan<GoogleIAPRobot>(Dev)
|
||||
.verify { googleIAPElementsDisplayed() }
|
||||
.verify {
|
||||
googleIAPElementsDisplayed()
|
||||
nextPaymentProviderButtonDisplayed()
|
||||
}
|
||||
|
||||
GoogleIAPRobot()
|
||||
.close<SelectPlanRobot>()
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Addresses": [
|
||||
{
|
||||
"ID": "Aj_ugxEsw8G-5twSvHmaaC-R75FBj7szznD7ib4DbBa7cuJxrIITuXMuzUHLMcQQrBglx0jacVwzDq-wGKMphQ==",
|
||||
"DomainID": "l8vWAXHBQmv0u7OVtPbcqMa4iwQaBqowINSQjPrxAr-Da8fVPKUkUcqAq30_BCxj1X0nW70HQRmAa-rIvzmKUA==",
|
||||
"Email": "test-mock-936@proton.black",
|
||||
"Status": 1,
|
||||
"Type": 1,
|
||||
"Receive": 0,
|
||||
"Send": 0,
|
||||
"DisplayName": "",
|
||||
"Signature": "",
|
||||
"Order": 1,
|
||||
"Priority": 1,
|
||||
"ConfirmationState": 1,
|
||||
"HasKeys": 0,
|
||||
"SignedKeyList": null,
|
||||
"Keys": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Addresses": [
|
||||
{
|
||||
"ID": "Aj_ugxEsw8G-5twSvHmaaC-R75FBj7szznD7ib4DbBa7cuJxrIITuXMuzUHLMcQQrBglx0jacVwzDq-wGKMphQ==",
|
||||
"DomainID": "l8vWAXHBQmv0u7OVtPbcqMa4iwQaBqowINSQjPrxAr-Da8fVPKUkUcqAq30_BCxj1X0nW70HQRmAa-rIvzmKUA==",
|
||||
"Email": "test-mock-936@proton.black",
|
||||
"Status": 1,
|
||||
"Type": 1,
|
||||
"Receive": 1,
|
||||
"Send": 1,
|
||||
"DisplayName": "",
|
||||
"Signature": "",
|
||||
"Order": 1,
|
||||
"Priority": 1,
|
||||
"ConfirmationState": 1,
|
||||
"HasKeys": 1,
|
||||
"SignedKeyList": {
|
||||
"MinEpochID": null,
|
||||
"MaxEpochID": null,
|
||||
"ExpectedMinEpochID": 413,
|
||||
"Data": "[{\"Fingerprint\": \"0060f7739d803c34162963bfad3c05fca6a522f1\",\"SHA256Fingerprints\": [\"317add2a2a52233559e42c9a9b0f1cc84dc4532132b415a98b02663d3f9591b6\",\"855d512b2230abbbd058da2a5e210f35c161a2cd5d514b2062265463eee1192c\"],\"Flags\": 3,\"Primary\": 1}]",
|
||||
"Signature": "-----BEGIN PGP SIGNATURE-----\nVersion: ProtonMail\n\nwnUEABYKACcFAmNIQecJkK08BfympSLxFiEEAGD3c52APDQWKWO/rTwF/Kal\nIvEAAK3ZAQCPrqF9RPcFj/gkfM5IUxN0HUULnXWCJaMhxEinVbTu2gD+OOZI\n4yvWC3fcDpSVYhClNjV6fn6rVLzetG0MbHwmVgs=\n=yPix\n-----END PGP SIGNATURE-----\n"
|
||||
},
|
||||
"Keys": [
|
||||
{
|
||||
"ID": "gG7SLPctbQeU4GiCDYrwPdtGBOwjTo501TUvu-xanerLahsW7uShd29QbBmy8FHXomp20WmH6bWcyKZAu5aMoQ==",
|
||||
"Primary": 1,
|
||||
"Flags": 3,
|
||||
"Fingerprint": "0060f7739d803c34162963bfad3c05fca6a522f1",
|
||||
"Fingerprints": [
|
||||
"0060f7739d803c34162963bfad3c05fca6a522f1",
|
||||
"9381fee3c76b61154cfaeeca2f48c5ae1156905f"
|
||||
],
|
||||
"PublicKey": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: ProtonMail\n\nxjMEY0bwZxYJKwYBBAHaRw8BAQdA32HWq/s9pn80cTYQzntfCUObXhQdKTYL\ncWhzh5mKVCPNN3Rlc3QtbW9jay05MzZAcHJvdG9uLmJsYWNrIDx0ZXN0LW1v\nY2stOTM2QHByb3Rvbi5ibGFjaz7CjAQTFggAPgUCY0bwZwmQrTwF/KalIvEW\nIQQAYPdznYA8NBYpY7+tPAX8pqUi8QIbAwIeAQIZAQMLCQcCFQgDFgACAiIB\nAAAZlwEA162m1fpQ1Lc4BOIud94Na4PYNLOZG+1XyBn+cZaRFeYA/AmpAYsd\naTK94OOAziF3xzTmogaSOaLlAdK4amyqMUkFwqgEEBYIAFoFAmNIQecJkNTS\nGgVRp7LsFiEENhVDvw2lbaYMs9qU1NIaBVGnsuwsHFRlc3QgT3BlblBHUCBD\nQSA8dGVzdC1vcGVucGdwLWNhQHByb3Rvbi5tZT4FgwDtTgAAACbtAQDLWkDg\nWyIHnmcvKydwf3g1qGa+h8DfnqiqPHDPumtxpgD+OisPcOZocVFvTCyrumXn\nCuydKSCMmEF+dbQp6L5XfgXOOARjRvBnEgorBgEEAZdVAQUBAQdAPgaJsuED\neY1ElpnYaZHraXVHH4CiMYCOXe+BxTl88gcDAQoJwngEGBYIACoFAmNG8GcJ\nkK08BfympSLxFiEEAGD3c52APDQWKWO/rTwF/KalIvECGwwAADVyAQCG85q1\nI4fqiEpB1S+qMykiI5X0vUrxyLvf4UajjduwAQD/auK6+JyYruPq/UV+L9yw\nVxCwZV211YbS1oaQOTElhAI=\n=6Gbr\n-----END PGP PUBLIC KEY BLOCK-----\n",
|
||||
"Active": 1,
|
||||
"Version": 3,
|
||||
"Activation": null,
|
||||
"PrivateKey": "-----BEGIN PGP PRIVATE KEY BLOCK-----\nVersion: ProtonMail\n\nxYYEY0bwZxYJKwYBBAHaRw8BAQdA32HWq/s9pn80cTYQzntfCUObXhQdKTYL\ncWhzh5mKVCP+CQMIUpXLJHRxAopggKwtUzebKlK5y8EYCwho/je4Q9t9drFo\nf++RYhV1Bf/5oVO27Mw9rhVmbONCYSInx5tuJjQ+DhvmqkfqcYruP3kLMGHU\nZ803dGVzdC1tb2NrLTkzNkBwcm90b24uYmxhY2sgPHRlc3QtbW9jay05MzZA\ncHJvdG9uLmJsYWNrPsKMBBMWCAA+BQJjRvBnCZCtPAX8pqUi8RYhBABg93Od\ngDw0Filjv608BfympSLxAhsDAh4BAhkBAwsJBwIVCAMWAAICIgEAABmXAQDX\nrabV+lDUtzgE4i533g1rg9g0s5kb7VfIGf5xlpEV5gD8CakBix1pMr3g44DO\nIXfHNOaiBpI5ouUB0rhqbKoxSQXHiwRjRvBnEgorBgEEAZdVAQUBAQdAPgaJ\nsuEDeY1ElpnYaZHraXVHH4CiMYCOXe+BxTl88gcDAQoJ/gkDCN7T8/EcLqWx\nYJSp14FcyrKeISS5eF/7BtQKrPG8znPt2IJP38dqbplEgzgfS1ypbrKJHf+n\n6gKR/aEVL7TaNqQ/TS3V/Kyt9F3pc7XZ8lzCeAQYFggAKgUCY0bwZwmQrTwF\n/KalIvEWIQQAYPdznYA8NBYpY7+tPAX8pqUi8QIbDAAANXIBAIbzmrUjh+qI\nSkHVL6ozKSIjlfS9SvHIu9/hRqON27ABAP9q4rr4nJiu4+r9RX4v3LBXELBl\nXbXVhtLWhpA5MSWEAg==\n=egGh\n-----END PGP PRIVATE KEY BLOCK-----\n",
|
||||
"Token": null,
|
||||
"Signature": null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Modulus": "-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA256\n\n+6itHql7q+rDyAIm+PNYRNpzdqza5KSTQQ5KZCi1apxTgC4jzMvm46Ph7Ipb8L0LnwawV6rfDpG6/xqPqW2Wa0PMUK/AOA7mc4ElkKAyf/bMnk00foJ/jT2TDTvAceXOcUM93deJimL6Kd3T+hqcjZAYTOh4qGBdH8vLKfFgs1Xrcgo0nBZAMcxUkcbZFnWMsCGwHlPbdT8fn8Wzv1h6xyCeT5rEK7L1VIN01Zjv1gKIbHcd2uGiY63NKdYg17rBelZ50taNFZoZk9PbGv6z0xSUGVxZ3pveNyG4oaeFYbyc17bbTSfwYsi4V/8kML6kPcnQZQ/vXsAtbzRVboWqzQ==\n-----BEGIN PGP SIGNATURE-----\nVersion: ProtonMail\nComment: https://protonmail.com\n\nwl4EARYIABAFAlwB1j8JEDUFhcTpUY8mAADSJwEA5ee0/SNdABNcsKh3rCp6\n9l6lbFYukxV0i2owa5CCJ6cBANMluXRgjg0LJCYVPnBjhrWB6PCnRMgdCyF9\nHMnOiTUI\n=LOHL\n-----END PGP SIGNATURE-----\n",
|
||||
"ModulusID": "e_DzaCA0dI-vawVq4ab00WFHBMCbZ4ZbVEFBIAxEIkg_ozPFoN4QAQxTjO7vlQ5P9Y-MpUTgUAZ_CICYQz2uxA=="
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Scope": "4297913728",
|
||||
"Scopes": [
|
||||
"full",
|
||||
"locked",
|
||||
"self",
|
||||
"payments",
|
||||
"keys",
|
||||
"parent",
|
||||
"user",
|
||||
"loggedin",
|
||||
"nondelinquent",
|
||||
"mail",
|
||||
"verified"
|
||||
]
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"EventID": "CfPWFKv7hHURG0mJ5raWYYCcLXKvfCbFoaezy-06aqcxxvbkWdKTxkZUIcanEf7Tw4USrg2vZ_HUIi06YjILLQ==",
|
||||
"Refresh": 0,
|
||||
"More": 0,
|
||||
"Pushes": [],
|
||||
"Notices": []
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"EventID": "CfPWFKv7hHURG0mJ5raWYYCcLXKvfCbFoaezy-06aqcxxvbkWdKTxkZUIcanEf7Tw4USrg2vZ_HUIi06YjILLQ=="
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Features": [
|
||||
{
|
||||
"Code": "ThreadingAndroid",
|
||||
"Type": "boolean",
|
||||
"Global": true,
|
||||
"DefaultValue": true,
|
||||
"Value": true,
|
||||
"Writable": false
|
||||
},
|
||||
{
|
||||
"Code": "CanUserSendFeedback",
|
||||
"Type": "boolean",
|
||||
"Global": true,
|
||||
"DefaultValue": false,
|
||||
"Value": false,
|
||||
"Writable": false
|
||||
}
|
||||
],
|
||||
"Total": 2
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Domains": [
|
||||
"proton.black",
|
||||
"protonmail.com.proton.black"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"RecipientType": 1,
|
||||
"MIMEType": "text/html",
|
||||
"Keys": [
|
||||
{
|
||||
"Flags": 3,
|
||||
"PublicKey": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: ProtonMail\n\nxjMEY0fm4hYJKwYBBAHaRw8BAQdAcQcU17vasED/GNSVBFJL8pOsRTLv9ix6\nPVnQan43bVjNR3Rlc3QtZm4yaTM0aEBwYXltZW50cy5wcm90b24uYmxhY2sg\nPHRlc3QtZm4yaTM0aEBwYXltZW50cy5wcm90b24uYmxhY2s+wowEExYIAD4F\nAmNH5uIJkGXQGno3YG6/FiEEm2+6uaYNlaK0+/shZdAaejdgbr8CGwMCHgEC\nGQEDCwkHAhUIAxYAAgIiAQAAT4UA/1FkNRAJ9ErCsx0OiN4JeEZsDuBd0DbF\ni48ejOMocAlWAQCjtN7ylHanFip80t9Bbsd0iw2CJhKwfOddWXY02k9GD8Ko\nBBAWCABaBQJjSThiCZDU0hoFUaey7BYhBDYVQ78NpW2mDLPalNTSGgVRp7Ls\nLBxUZXN0IE9wZW5QR1AgQ0EgPHRlc3Qtb3BlbnBncC1jYUBwcm90b24ubWU+\nBYMA7U4AAACuBgD7BWTrCQToGDVRZKUckkAAgpCgRY6fnv8ZK2JxlFWQ8bAA\n/3347rLgOgtARjj9t1znCjAeoCQbJQw74ge/nSRxgekAzjgEY0fm4hIKKwYB\nBAGXVQEFAQEHQNU+tdqd9yjxaO+gW4XCWwFoCA8SqKa2N6aRZJ6l6zBaAwEK\nCcJ4BBgWCAAqBQJjR+biCZBl0Bp6N2BuvxYhBJtvurmmDZWitPv7IWXQGno3\nYG6/AhsMAADHIQEAqxfglAyZmITQxybwxvipBXQaPcvxWUOxH6h3PxLBu5IB\nANo+nxEII8hQkzdgN3plBNhS3kuD4fwUTTWv1L+Y/8gO\n=/TXu\n-----END PGP PUBLIC KEY BLOCK-----\n"
|
||||
}
|
||||
],
|
||||
"SignedKeyList": {
|
||||
"MinEpochID": null,
|
||||
"MaxEpochID": null,
|
||||
"ExpectedMinEpochID": 1,
|
||||
"Data": "[{\"Fingerprint\": \"9b6fbab9a60d95a2b4fbfb2165d01a7a37606ebf\",\"SHA256Fingerprints\": [\"aef688d21c4cfa367ff5754862498501f400f87d9ce207b88fa52ccac556c582\",\"731f8442280cf2312ea0bf9ea6fd7542fcb983ff51c1e4c97fa0371c78e7ec41\"],\"Flags\": 3,\"Primary\": 1}]",
|
||||
"Signature": "-----BEGIN PGP SIGNATURE-----\nVersion: ProtonMail\n\nwnUEABYKACcFAmNJOGIJkGXQGno3YG6/FiEEm2+6uaYNlaK0+/shZdAaejdg\nbr8AAA9PAQCLUDIEFDrqv+lppiWkukJkGsp+tJ8iwSozU7Q1/63UBgD/SfqD\n1R+fea5FwlqdfRXTzfwj6Xafoqe2Rg4tfQRTeAo=\n=vtu/\n-----END PGP SIGNATURE-----\n"
|
||||
},
|
||||
"Warnings": []
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"KeySalts": [
|
||||
{
|
||||
"ID": "ktXfDhLWmzv_urlJa4J_hjGWNLOYRwwJbY8N7vJ8iUJjsW6tET3rGLWLU64DhPXcjiXkLS-P-Cex_cnsfiB-2g==",
|
||||
"KeySalt": "KyXt2jZxVpS4Uh3AVwY2HA=="
|
||||
},
|
||||
{
|
||||
"ID": "gG7SLPctbQeU4GiCDYrwPdtGBOwjTo501TUvu-xanerLahsW7uShd29QbBmy8FHXomp20WmH6bWcyKZAu5aMoQ==",
|
||||
"KeySalt": "KyXt2jZxVpS4Uh3AVwY2HA=="
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"MailSettings": {
|
||||
"LastLoginTime": 1665742943,
|
||||
"AutoSaveContacts": 1,
|
||||
"AutoWildcardSearch": 1,
|
||||
"ComposerMode": 0,
|
||||
"FontSize": null,
|
||||
"FontFace": null,
|
||||
"MessageButtons": 0,
|
||||
"ShowImages": 2,
|
||||
"ShowMoved": 0,
|
||||
"ViewMode": 0,
|
||||
"ViewLayout": 0,
|
||||
"SwipeLeft": 3,
|
||||
"SwipeRight": 0,
|
||||
"AlsoArchive": 0,
|
||||
"Hotkeys": 0,
|
||||
"Shortcuts": 1,
|
||||
"PMSignature": 2,
|
||||
"ImageProxy": 2,
|
||||
"TLS": 0,
|
||||
"RightToLeft": 0,
|
||||
"AttachPublicKey": 0,
|
||||
"Sign": 0,
|
||||
"PGPScheme": 16,
|
||||
"PromptPin": 0,
|
||||
"KT": 0,
|
||||
"Autocrypt": 0,
|
||||
"StickyLabels": 0,
|
||||
"ExpandFolders": 0,
|
||||
"ConfirmLink": 1,
|
||||
"DelaySendSeconds": 10,
|
||||
"ThemeType": 0,
|
||||
"ThemeVersion": null,
|
||||
"Theme": "",
|
||||
"DisplayName": "",
|
||||
"Signature": "",
|
||||
"AutoResponder": {
|
||||
"StartTime": 0,
|
||||
"EndTime": 0,
|
||||
"DaysSelected": [],
|
||||
"Repeat": 0,
|
||||
"Subject": "Auto",
|
||||
"Message": "",
|
||||
"IsEnabled": false,
|
||||
"Zone": "Europe/Zurich"
|
||||
},
|
||||
"EnableFolderColor": 0,
|
||||
"InheritParentFolderColor": 1,
|
||||
"SpamAction": null,
|
||||
"BlockSenderConfirmation": null,
|
||||
"MobileSettings": {
|
||||
"MessageToolbar": {
|
||||
"IsCustom": false,
|
||||
"Actions": [
|
||||
"toggle_read",
|
||||
"trash",
|
||||
"move",
|
||||
"label"
|
||||
]
|
||||
},
|
||||
"ConversationToolbar": {
|
||||
"IsCustom": false,
|
||||
"Actions": [
|
||||
"toggle_read",
|
||||
"trash",
|
||||
"move",
|
||||
"label"
|
||||
]
|
||||
},
|
||||
"ListToolbar": {
|
||||
"IsCustom": false,
|
||||
"Actions": [
|
||||
"toggle_read",
|
||||
"trash",
|
||||
"move",
|
||||
"label"
|
||||
]
|
||||
}
|
||||
},
|
||||
"HideEmbeddedImages": 0,
|
||||
"HideRemoteImages": 0,
|
||||
"HideSenderImages": 0,
|
||||
"NumMessagePerPage": 50,
|
||||
"RecipientLimit": 100,
|
||||
"DraftMIMEType": "text/html",
|
||||
"ReceiveMIMEType": "text/html",
|
||||
"ShowMIMEType": "text/html",
|
||||
"PMSignatureReferralLink": 0,
|
||||
"SubmissionAccess": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Code": 2501,
|
||||
"Error": "You are not a member of an organization",
|
||||
"Details": {}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"PaymentMethods": []
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Plans": [
|
||||
{
|
||||
"ID": "Wb4NAqmiuqoA7kCHE28y92bBFfN8jaYQCLxHRAB96yGj-bh9SxguXC48_WSU-fRUjdAr-lx95c6rFLplgXyXYA==",
|
||||
"ParentMetaPlanID": "hUcV0_EeNwUmXA6EoyNrtO-ZTD8H8F6LvNaSjMaPxB5ecFkA7y-5kc3q38cGumJENGHjtSoUndkYFUx0_xlJeg==",
|
||||
"Type": 1,
|
||||
"Name": "mail2022",
|
||||
"Title": "Mail Plus",
|
||||
"MaxDomains": 1,
|
||||
"MaxAddresses": 10,
|
||||
"MaxCalendars": 20,
|
||||
"MaxSpace": 16106127360,
|
||||
"MaxMembers": 1,
|
||||
"MaxVPN": 0,
|
||||
"MaxTier": 0,
|
||||
"Services": 1,
|
||||
"Features": 1,
|
||||
"State": 1,
|
||||
"Pricing": {
|
||||
"1": 499,
|
||||
"12": 4788,
|
||||
"24": 8376
|
||||
},
|
||||
"PeriodEnd": {
|
||||
"1": 1668096824,
|
||||
"12": 1696954424,
|
||||
"24": 1728576824
|
||||
},
|
||||
"Currency": "CHF",
|
||||
"Quantity": 1,
|
||||
"Cycle": 1,
|
||||
"Amount": 499,
|
||||
"Vendors": {
|
||||
"Google": {
|
||||
"Plans": {
|
||||
"12": "googlemail_mail2022_12_renewing"
|
||||
},
|
||||
"CustomerID": "cus_google_3KGnMew0qrAfmwXgP0FL"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "vl-JevUsz3GJc18CC1VOs-qDKqoIWlLiUePdrzFc72-BtxBPHBDZM7ayn8CNQ59Sk4XjDbwwBVpdYrPIFtOvIw==",
|
||||
"ParentMetaPlanID": "hUcV0_EeNwUmXA6EoyNrtO-ZTD8H8F6LvNaSjMaPxB5ecFkA7y-5kc3q38cGumJENGHjtSoUndkYFUx0_xlJeg==",
|
||||
"Type": 1,
|
||||
"Name": "bundle2022",
|
||||
"Title": "Proton Unlimited",
|
||||
"MaxDomains": 3,
|
||||
"MaxAddresses": 15,
|
||||
"MaxCalendars": 20,
|
||||
"MaxSpace": 536870912000,
|
||||
"MaxMembers": 1,
|
||||
"MaxVPN": 10,
|
||||
"MaxTier": 2,
|
||||
"Services": 7,
|
||||
"Features": 1,
|
||||
"State": 1,
|
||||
"Pricing": {
|
||||
"1": 1199,
|
||||
"12": 11988,
|
||||
"24": 19176
|
||||
},
|
||||
"PeriodEnd": {
|
||||
"1": 1668096824,
|
||||
"12": 1696954424,
|
||||
"24": 1728576824
|
||||
},
|
||||
"Currency": "CHF",
|
||||
"Quantity": 1,
|
||||
"Cycle": 1,
|
||||
"Amount": 1199,
|
||||
"Vendors": {
|
||||
"Google": {
|
||||
"Plans": {
|
||||
"12": "googlemail_bundle2022_12_renewing"
|
||||
},
|
||||
"CustomerID": "cus_google_fw4epbT6TRXc0hGyDXKa"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "l8vWAXHBQmv0u7OVtPbcqMa4iwQaBqowINSQjPrxAr-Da8fVPKUkUcqAq30_BCxj1X0nW70HQRmAa-rIvzmKUA==",
|
||||
"ParentMetaPlanID": "hUcV0_EeNwUmXA6EoyNrtO-ZTD8H8F6LvNaSjMaPxB5ecFkA7y-5kc3q38cGumJENGHjtSoUndkYFUx0_xlJeg==",
|
||||
"Type": 1,
|
||||
"Name": "plus",
|
||||
"Title": "ProtonMail Plus",
|
||||
"MaxDomains": 1,
|
||||
"MaxAddresses": 5,
|
||||
"MaxCalendars": 20,
|
||||
"MaxSpace": 5368709120,
|
||||
"MaxMembers": 1,
|
||||
"MaxVPN": 0,
|
||||
"MaxTier": 0,
|
||||
"Services": 1,
|
||||
"Features": 0,
|
||||
"State": 0,
|
||||
"Pricing": {
|
||||
"1": 500,
|
||||
"12": 4800,
|
||||
"24": 7900
|
||||
},
|
||||
"PeriodEnd": {
|
||||
"1": 1668096824,
|
||||
"12": 1696954424,
|
||||
"24": 1728576824
|
||||
},
|
||||
"Currency": "CHF",
|
||||
"Quantity": 1,
|
||||
"Cycle": 1,
|
||||
"Amount": 500,
|
||||
"Vendors": {
|
||||
"Google": {
|
||||
"Plans": {
|
||||
"12": "googlemail_plus_12_renewing"
|
||||
},
|
||||
"CustomerID": "cus_google_EywMAJN-vpYitcT00qQ_"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Plans": {
|
||||
"Type": 1,
|
||||
"Name": "free",
|
||||
"Title": "Proton Free",
|
||||
"MaxDomains": 0,
|
||||
"MaxAddresses": 1,
|
||||
"MaxCalendars": 1,
|
||||
"MaxSpace": 524288000,
|
||||
"MaxRewardSpace": 1073741824,
|
||||
"MaxMembers": 1,
|
||||
"MaxVPN": 1,
|
||||
"Services": null,
|
||||
"Features": 0,
|
||||
"Cycle": null,
|
||||
"Currency": null,
|
||||
"Amount": 0,
|
||||
"Quantity": 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Card": 0,
|
||||
"Paypal": 0,
|
||||
"Bitcoin": 0,
|
||||
"InApp": 0
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Card": 0,
|
||||
"Paypal": 0,
|
||||
"Bitcoin": 0,
|
||||
"InApp": 1
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Card": 1,
|
||||
"Paypal": 1,
|
||||
"Bitcoin": 0,
|
||||
"InApp": 1
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Subscription": {
|
||||
"ID": "mfFmlFpj9JFOwsHXeN-p0bjnOppF34tgrr_mUYXJa1UCvXgssVMvkSe3gPo3vgd_zicARNzWsta3Tl8d7xdvqQ==",
|
||||
"InvoiceID": "Je7hvu3ZQBjsED0PcTYXVbIEj8lLhFzgll3I6UNnKBpajE8uIubLADCjb7fJcaXZYL-5kYq23Gckin5Mnn_czA==",
|
||||
"Cycle": 12,
|
||||
"PeriodStart": 1665418938,
|
||||
"PeriodEnd": 1696954938,
|
||||
"CreateTime": 1665418938,
|
||||
"CouponCode": null,
|
||||
"Currency": "CHF",
|
||||
"Amount": 4788,
|
||||
"Discount": 0,
|
||||
"RenewDiscount": 0,
|
||||
"RenewAmount": 4788,
|
||||
"Plans": [
|
||||
{
|
||||
"ID": "Wb4NAqmiuqoA7kCHE28y92bBFfN8jaYQCLxHRAB96yGj-bh9SxguXC48_WSU-fRUjdAr-lx95c6rFLplgXyXYA==",
|
||||
"ParentMetaPlanID": "hUcV0_EeNwUmXA6EoyNrtO-ZTD8H8F6LvNaSjMaPxB5ecFkA7y-5kc3q38cGumJENGHjtSoUndkYFUx0_xlJeg==",
|
||||
"Type": 1,
|
||||
"Name": "mail2022",
|
||||
"Title": "Mail Plus",
|
||||
"MaxDomains": 1,
|
||||
"MaxAddresses": 10,
|
||||
"MaxCalendars": 20,
|
||||
"MaxSpace": 16106127360,
|
||||
"MaxMembers": 1,
|
||||
"MaxVPN": 0,
|
||||
"MaxTier": 0,
|
||||
"Services": 1,
|
||||
"Features": 1,
|
||||
"State": 1,
|
||||
"Cycle": 12,
|
||||
"Currency": "CHF",
|
||||
"Amount": 4788,
|
||||
"Quantity": 1
|
||||
}
|
||||
],
|
||||
"Renew": 1,
|
||||
"External": 2,
|
||||
"CustomerID": "cus_google_QTqe7W-dkfX09qtIy7Mb"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Code": 22110,
|
||||
"Error": "You do not have an active subscription",
|
||||
"Details": {}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"AmountDue": 4788,
|
||||
"Amount": 4788,
|
||||
"Cycle": 12,
|
||||
"CouponDiscount": 0,
|
||||
"Gift": 0,
|
||||
"Currency": "CHF",
|
||||
"UnusedCredit": 0,
|
||||
"Credit": 0,
|
||||
"Proration": 0,
|
||||
"Coupon": null
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"User": {
|
||||
"ID": "VhC0TBoR_SHd7-rjOLtY5lYQ_Dqw9e8Lu54FqyW05ssJ3eEo6hglLat6Aq9M5MaM66EZ2Nr327QQL5zyF15SvQ==",
|
||||
"Name": "test-mock-936",
|
||||
"Currency": "CHF",
|
||||
"Credit": 0,
|
||||
"Type": 1,
|
||||
"CreateTime": 1665679842,
|
||||
"MaxSpace": 524288000,
|
||||
"MaxUpload": 26214400,
|
||||
"UsedSpace": 0,
|
||||
"Subscribed": 0,
|
||||
"Services": 1,
|
||||
"MnemonicStatus": 0,
|
||||
"Role": 0,
|
||||
"Private": 1,
|
||||
"Delinquent": 0,
|
||||
"Keys": [],
|
||||
"ToMigrate": 1,
|
||||
"Email": "test-mock-936@proton.black",
|
||||
"DisplayName": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"User": {
|
||||
"ID": "VhC0TBoR_SHd7-rjOLtY5lYQ_Dqw9e8Lu54FqyW05ssJ3eEo6hglLat6Aq9M5MaM66EZ2Nr327QQL5zyF15SvQ==",
|
||||
"Name": "test-mock-936",
|
||||
"Currency": "CHF",
|
||||
"Credit": 0,
|
||||
"Type": 1,
|
||||
"CreateTime": 1665679842,
|
||||
"MaxSpace": 16106127360,
|
||||
"MaxUpload": 26214400,
|
||||
"UsedSpace": 0,
|
||||
"Subscribed": 1,
|
||||
"Services": 1,
|
||||
"MnemonicStatus": 0,
|
||||
"Role": 2,
|
||||
"Private": 1,
|
||||
"Delinquent": 0,
|
||||
"Keys": [],
|
||||
"ToMigrate": 1,
|
||||
"Email": "test-mock-936@proton.black",
|
||||
"DisplayName": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"User": {
|
||||
"ID": "VhC0TBoR_SHd7-rjOLtY5lYQ_Dqw9e8Lu54FqyW05ssJ3eEo6hglLat6Aq9M5MaM66EZ2Nr327QQL5zyF15SvQ==",
|
||||
"Name": "test-mock-936",
|
||||
"Currency": "CHF",
|
||||
"Credit": 0,
|
||||
"Type": 1,
|
||||
"CreateTime": 1665679842,
|
||||
"MaxSpace": 524288000,
|
||||
"MaxUpload": 26214400,
|
||||
"UsedSpace": 248329,
|
||||
"Subscribed": 0,
|
||||
"Services": 1,
|
||||
"MnemonicStatus": 0,
|
||||
"Role": 0,
|
||||
"Private": 1,
|
||||
"Delinquent": 0,
|
||||
"Keys": [
|
||||
{
|
||||
"ID": "ktXfDhLWmzv_urlJa4J_hjGWNLOYRwwJbY8N7vJ8iUJjsW6tET3rGLWLU64DhPXcjiXkLS-P-Cex_cnsfiB-2g==",
|
||||
"Version": 3,
|
||||
"Primary": 1,
|
||||
"RecoverySecret": null,
|
||||
"RecoverySecretSignature": null,
|
||||
"PrivateKey": "-----BEGIN PGP PRIVATE KEY BLOCK-----\nVersion: ProtonMail\n\nxYYEY0bwZhYJKwYBBAHaRw8BAQdA1yDTR70NxF/vJVpYKVcA85nGx202WhTF\nr8B7CjTyRaj+CQMIawSpxnhvxmlgWQkR6VxIOn/FvaCnnHoEeZpBB7glqril\nSvo7VaLxVDNmTlK0iXLAdN9IDJzoTstqem3kP0eDsV1VllYogeEZXycMVLdl\ngs03dGVzdC1tb2NrLTkzNkBwcm90b24uYmxhY2sgPHRlc3QtbW9jay05MzZA\ncHJvdG9uLmJsYWNrPsKMBBMWCAA+BQJjRvBmCZCx8Eg4YZqbdxYhBPwQ6P4+\nLphcoA4eGLHwSDhhmpt3AhsDAh4BAhkBAwsJBwIVCAMWAAICIgEAAPGgAQDP\nFL4EJ4KERUaQe+CqRcN9n0eht+an0gB4DI13laL4DAD9GoT/l7rgzE/6S/mZ\nvs+pIXDa2vVe9lFeYPwAH6Q5cg3HiwRjRvBmEgorBgEEAZdVAQUBAQdAWcGA\nsFE9w5OQUIJSpltFD0NF0a7tqqCJzta33ylLfjYDAQoJ/gkDCLzXimofJ0Ov\nYMsejJrofkHZXKaBE1Jl2yxf4jMxBGGB93G/INhsjF0fNX0XJfucUKJ4Lyk+\nMaiQsvD4dMUSJjxuRqNyfOp3NR1SYa0+hSfCeAQYFggAKgUCY0bwZgmQsfBI\nOGGam3cWIQT8EOj+Pi6YXKAOHhix8Eg4YZqbdwIbDAAAKUEA/jgGKBtfNvRp\nqNn3b+1V7pOCNhabyDge22SlCFWI1Q6+AP4xt9jEIRVDzGVEnhxG44o82cyC\nTZLysQbBbsnilK4BBw==\n=pKOS\n-----END PGP PRIVATE KEY BLOCK-----\n",
|
||||
"Fingerprint": "fc10e8fe3e2e985ca00e1e18b1f04838619a9b77",
|
||||
"Active": 1
|
||||
}
|
||||
],
|
||||
"ToMigrate": 1,
|
||||
"Email": "test-mock-936@proton.black",
|
||||
"DisplayName": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"User": {
|
||||
"ID": "VhC0TBoR_SHd7-rjOLtY5lYQ_Dqw9e8Lu54FqyW05ssJ3eEo6hglLat6Aq9M5MaM66EZ2Nr327QQL5zyF15SvQ==",
|
||||
"Name": "test-mock-936",
|
||||
"Currency": "CHF",
|
||||
"Credit": 0,
|
||||
"Type": 1,
|
||||
"CreateTime": 1665679842,
|
||||
"MaxSpace": 524288000,
|
||||
"MaxUpload": 26214400,
|
||||
"UsedSpace": 248329,
|
||||
"Subscribed": 1,
|
||||
"Services": 1,
|
||||
"MnemonicStatus": 0,
|
||||
"Role": 0,
|
||||
"Private": 1,
|
||||
"Delinquent": 0,
|
||||
"Keys": [
|
||||
{
|
||||
"ID": "ktXfDhLWmzv_urlJa4J_hjGWNLOYRwwJbY8N7vJ8iUJjsW6tET3rGLWLU64DhPXcjiXkLS-P-Cex_cnsfiB-2g==",
|
||||
"Version": 3,
|
||||
"Primary": 1,
|
||||
"RecoverySecret": null,
|
||||
"RecoverySecretSignature": null,
|
||||
"PrivateKey": "-----BEGIN PGP PRIVATE KEY BLOCK-----\nVersion: ProtonMail\n\nxYYEY0bwZhYJKwYBBAHaRw8BAQdA1yDTR70NxF/vJVpYKVcA85nGx202WhTF\nr8B7CjTyRaj+CQMIawSpxnhvxmlgWQkR6VxIOn/FvaCnnHoEeZpBB7glqril\nSvo7VaLxVDNmTlK0iXLAdN9IDJzoTstqem3kP0eDsV1VllYogeEZXycMVLdl\ngs03dGVzdC1tb2NrLTkzNkBwcm90b24uYmxhY2sgPHRlc3QtbW9jay05MzZA\ncHJvdG9uLmJsYWNrPsKMBBMWCAA+BQJjRvBmCZCx8Eg4YZqbdxYhBPwQ6P4+\nLphcoA4eGLHwSDhhmpt3AhsDAh4BAhkBAwsJBwIVCAMWAAICIgEAAPGgAQDP\nFL4EJ4KERUaQe+CqRcN9n0eht+an0gB4DI13laL4DAD9GoT/l7rgzE/6S/mZ\nvs+pIXDa2vVe9lFeYPwAH6Q5cg3HiwRjRvBmEgorBgEEAZdVAQUBAQdAWcGA\nsFE9w5OQUIJSpltFD0NF0a7tqqCJzta33ylLfjYDAQoJ/gkDCLzXimofJ0Ov\nYMsejJrofkHZXKaBE1Jl2yxf4jMxBGGB93G/INhsjF0fNX0XJfucUKJ4Lyk+\nMaiQsvD4dMUSJjxuRqNyfOp3NR1SYa0+hSfCeAQYFggAKgUCY0bwZgmQsfBI\nOGGam3cWIQT8EOj+Pi6YXKAOHhix8Eg4YZqbdwIbDAAAKUEA/jgGKBtfNvRp\nqNn3b+1V7pOCNhabyDge22SlCFWI1Q6+AP4xt9jEIRVDzGVEnhxG44o82cyC\nTZLysQbBbsnilK4BBw==\n=pKOS\n-----END PGP PRIVATE KEY BLOCK-----\n",
|
||||
"Fingerprint": "fc10e8fe3e2e985ca00e1e18b1f04838619a9b77",
|
||||
"Active": 1
|
||||
}
|
||||
],
|
||||
"ToMigrate": 1,
|
||||
"Email": "test-mock-936@proton.black",
|
||||
"DisplayName": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"Code": 1000
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"ExpiresIn": 86400,
|
||||
"LocalID": 0,
|
||||
"TokenType": "Bearer",
|
||||
"AccessToken": "wt3ibg32nfmd2bp7mmwr2m6wg4pgq5vn",
|
||||
"RefreshToken": "3kknjkum6ofwricgli35fewa3vfxudl6",
|
||||
"Scopes": [
|
||||
"full",
|
||||
"self",
|
||||
"payments",
|
||||
"keys",
|
||||
"parent",
|
||||
"user",
|
||||
"loggedin",
|
||||
"nondelinquent",
|
||||
"verified"
|
||||
],
|
||||
"UID": "b6xkue4uqtq5gsfedxzbvbu5zpicoy2e",
|
||||
"UserID": "VhC0TBoR_SHd7-rjOLtY5lYQ_Dqw9e8Lu54FqyW05ssJ3eEo6hglLat6Aq9M5MaM66EZ2Nr327QQL5zyF15SvQ==",
|
||||
"EventID": "8nz6Put0cKKKinZGaC6mBrLJvn9-dvwFRcfD3LxUS-AnpJLf6GKgohuVkeSyyqqvcnip1p5guikT8DgcoZlVmQ==",
|
||||
"PasswordMode": 1,
|
||||
"ServerProof": "hD3IgA8zIRu7/0uCMns2DTNv0abl++eEaGEbSCKmgknmNL9JGk0MgaUM3ut+t7jTfi3PJKtNZCU5kMN5oKFzMDiAojN8pG6XrjOorbxDN5PmfP7XLUHgFolLlxgb93sKyZPie73J/cf22VHkmlHmFTlbMZs2gqTrUDF/D4xdewlrA5weu4myP/baanaPaLR9UzFrgOMe69k6Ei2iUbOtuntUyj4zqiK1JvuyDjVZSYD91aVwx25VNLZMc+bTAWTwYjVQO+z6J7CnyDyRBu9OXzO54YwpiMdhWKvHZIPx5xxPWJzPtA4SaFl+t57/K1C23MY4OHnxKaQvllN5nKG+rQ==",
|
||||
"Scope": "full self payments keys parent user loggedin nondelinquent verified",
|
||||
"Uid": "b6xkue4uqtq5gsfedxzbvbu5zpicoy2e",
|
||||
"TwoFactor": 0,
|
||||
"2FA": {
|
||||
"Enabled": 0,
|
||||
"FIDO2": {
|
||||
"AuthenticationOptions": null,
|
||||
"RegisteredKeys": []
|
||||
},
|
||||
"TOTP": 0
|
||||
},
|
||||
"TemporaryPassword": 0
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Modulus": "-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA256\n\n+6itHql7q+rDyAIm+PNYRNpzdqza5KSTQQ5KZCi1apxTgC4jzMvm46Ph7Ipb8L0LnwawV6rfDpG6/xqPqW2Wa0PMUK/AOA7mc4ElkKAyf/bMnk00foJ/jT2TDTvAceXOcUM93deJimL6Kd3T+hqcjZAYTOh4qGBdH8vLKfFgs1Xrcgo0nBZAMcxUkcbZFnWMsCGwHlPbdT8fn8Wzv1h6xyCeT5rEK7L1VIN01Zjv1gKIbHcd2uGiY63NKdYg17rBelZ50taNFZoZk9PbGv6z0xSUGVxZ3pveNyG4oaeFYbyc17bbTSfwYsi4V/8kML6kPcnQZQ/vXsAtbzRVboWqzQ==\n-----BEGIN PGP SIGNATURE-----\nVersion: ProtonMail\nComment: https://protonmail.com\n\nwl4EARYIABAFAlwB1j8JEDUFhcTpUY8mAADSJwEA5ee0/SNdABNcsKh3rCp6\n9l6lbFYukxV0i2owa5CCJ6cBANMluXRgjg0LJCYVPnBjhrWB6PCnRMgdCyF9\nHMnOiTUI\n=LOHL\n-----END PGP SIGNATURE-----\n",
|
||||
"ServerEphemeral": "IEvE4XmCjgbOLGcwSaE5vrrDK40Fudsc9rUkJhUqhhAdWRZWnQEI+JhFpzFrMraZX2gcMQaR1xXaYYuSjIlbWLHz0q5lctbDPkiFsBrGsYoRjKuqfbH6KjsNNZwb4rdyyLETT1yNnCCuOlUmF4c6YQ6+fIZJeqwsrrN1Pw/I1xSjxVAMwz42QxSor+C6abp3MpOlB62Tup+VCrPEz2SM+bAj/VFjcSi3pFr7vn/ZYHMTCvBUvBzrm4XbclWAgfEp3Vpm5HMSvzI1IHrKMO01uumMJru7PRVqS7GWEHaeXlxZGwX2/BT68aBhkSYS1JlCk5fWxyIQ81uK4uFE4N8FNA==",
|
||||
"Version": 4,
|
||||
"Salt": "v0qLXBUgfQBIcQ==",
|
||||
"SRPSession": "3c649026f09aa25bedc37a15b471682d"
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"User": {
|
||||
"ID": "VhC0TBoR_SHd7-rjOLtY5lYQ_Dqw9e8Lu54FqyW05ssJ3eEo6hglLat6Aq9M5MaM66EZ2Nr327QQL5zyF15SvQ==",
|
||||
"Name": "test-mock-936",
|
||||
"Currency": "CHF",
|
||||
"Credit": 0,
|
||||
"Type": 1,
|
||||
"CreateTime": 1665679842,
|
||||
"MaxSpace": 524288000,
|
||||
"MaxUpload": 26214400,
|
||||
"UsedSpace": 248329,
|
||||
"Subscribed": 0,
|
||||
"Services": 1,
|
||||
"MnemonicStatus": 0,
|
||||
"Role": 0,
|
||||
"Private": 1,
|
||||
"Delinquent": 0,
|
||||
"Keys": [
|
||||
{
|
||||
"ID": "ktXfDhLWmzv_urlJa4J_hjGWNLOYRwwJbY8N7vJ8iUJjsW6tET3rGLWLU64DhPXcjiXkLS-P-Cex_cnsfiB-2g==",
|
||||
"Version": 3,
|
||||
"Primary": 1,
|
||||
"RecoverySecret": null,
|
||||
"RecoverySecretSignature": null,
|
||||
"PrivateKey": "-----BEGIN PGP PRIVATE KEY BLOCK-----\nVersion: ProtonMail\n\nxYYEY0bwZhYJKwYBBAHaRw8BAQdA1yDTR70NxF/vJVpYKVcA85nGx202WhTF\nr8B7CjTyRaj+CQMIawSpxnhvxmlgWQkR6VxIOn/FvaCnnHoEeZpBB7glqril\nSvo7VaLxVDNmTlK0iXLAdN9IDJzoTstqem3kP0eDsV1VllYogeEZXycMVLdl\ngs03dGVzdC1tb2NrLTkzNkBwcm90b24uYmxhY2sgPHRlc3QtbW9jay05MzZA\ncHJvdG9uLmJsYWNrPsKMBBMWCAA+BQJjRvBmCZCx8Eg4YZqbdxYhBPwQ6P4+\nLphcoA4eGLHwSDhhmpt3AhsDAh4BAhkBAwsJBwIVCAMWAAICIgEAAPGgAQDP\nFL4EJ4KERUaQe+CqRcN9n0eht+an0gB4DI13laL4DAD9GoT/l7rgzE/6S/mZ\nvs+pIXDa2vVe9lFeYPwAH6Q5cg3HiwRjRvBmEgorBgEEAZdVAQUBAQdAWcGA\nsFE9w5OQUIJSpltFD0NF0a7tqqCJzta33ylLfjYDAQoJ/gkDCLzXimofJ0Ov\nYMsejJrofkHZXKaBE1Jl2yxf4jMxBGGB93G/INhsjF0fNX0XJfucUKJ4Lyk+\nMaiQsvD4dMUSJjxuRqNyfOp3NR1SYa0+hSfCeAQYFggAKgUCY0bwZgmQsfBI\nOGGam3cWIQT8EOj+Pi6YXKAOHhix8Eg4YZqbdwIbDAAAKUEA/jgGKBtfNvRp\nqNn3b+1V7pOCNhabyDge22SlCFWI1Q6+AP4xt9jEIRVDzGVEnhxG44o82cyC\nTZLysQbBbsnilK4BBw==\n=pKOS\n-----END PGP PRIVATE KEY BLOCK-----\n",
|
||||
"Fingerprint": "fc10e8fe3e2e985ca00e1e18b1f04838619a9b77",
|
||||
"Active": 1
|
||||
}
|
||||
],
|
||||
"ToMigrate": 1,
|
||||
"Email": "test-mock-936@proton.black",
|
||||
"DisplayName": ""
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Subscription": {
|
||||
"ID": "mfFmlFpj9JFOwsHXeN-p0bjnOppF34tgrr_mUYXJa1UCvXgssVMvkSe3gPo3vgd_zicARNzWsta3Tl8d7xdvqQ==",
|
||||
"InvoiceID": "Je7hvu3ZQBjsED0PcTYXVbIEj8lLhFzgll3I6UNnKBpajE8uIubLADCjb7fJcaXZYL-5kYq23Gckin5Mnn_czA==",
|
||||
"Cycle": 12,
|
||||
"PeriodStart": 1665418938,
|
||||
"PeriodEnd": 1696954938,
|
||||
"CreateTime": 1665418938,
|
||||
"CouponCode": null,
|
||||
"Currency": "CHF",
|
||||
"Amount": 4788,
|
||||
"Discount": 0,
|
||||
"RenewDiscount": 0,
|
||||
"RenewAmount": 4788,
|
||||
"Plans": [
|
||||
{
|
||||
"ID": "Wb4NAqmiuqoA7kCHE28y92bBFfN8jaYQCLxHRAB96yGj-bh9SxguXC48_WSU-fRUjdAr-lx95c6rFLplgXyXYA==",
|
||||
"ParentMetaPlanID": "hUcV0_EeNwUmXA6EoyNrtO-ZTD8H8F6LvNaSjMaPxB5ecFkA7y-5kc3q38cGumJENGHjtSoUndkYFUx0_xlJeg==",
|
||||
"Type": 1,
|
||||
"Name": "mail2022",
|
||||
"Title": "Mail Plus",
|
||||
"MaxDomains": 1,
|
||||
"MaxAddresses": 10,
|
||||
"MaxCalendars": 20,
|
||||
"MaxSpace": 16106127360,
|
||||
"MaxMembers": 1,
|
||||
"MaxVPN": 0,
|
||||
"MaxTier": 0,
|
||||
"Services": 1,
|
||||
"Features": 1,
|
||||
"State": 1,
|
||||
"Cycle": 12,
|
||||
"Currency": "CHF",
|
||||
"Amount": 4788,
|
||||
"Quantity": 1
|
||||
}
|
||||
],
|
||||
"Renew": 1,
|
||||
"External": 2,
|
||||
"CustomerID": "cus_google_QTqe7W-dkfX09qtIy7Mb"
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Subscription": {
|
||||
"ID": "zL42loPYyWGerUan59_znvVTLBz71E84dssmJHVsMscJk7ScqcIrBQdQMvGLl9C8w9wiPAh6M4FZV_Jw9WTtDw==",
|
||||
"InvoiceID": "LkFiOm3_dZjYLrp9FwvOr-k5JEW8jB0VJlWd-zfbf4KxuPSxBAm_tqsxxvoq1TNzBaDU9vlwL9i-bOAmv1A3iA==",
|
||||
"Cycle": 12,
|
||||
"PeriodStart": 1665745161,
|
||||
"PeriodEnd": 1697281161,
|
||||
"CreateTime": 1665745164,
|
||||
"CouponCode": null,
|
||||
"Currency": "CHF",
|
||||
"Amount": 4788,
|
||||
"Discount": 0,
|
||||
"RenewDiscount": 0,
|
||||
"RenewAmount": 4788,
|
||||
"Plans": [
|
||||
{
|
||||
"ID": "Wb4NAqmiuqoA7kCHE28y92bBFfN8jaYQCLxHRAB96yGj-bh9SxguXC48_WSU-fRUjdAr-lx95c6rFLplgXyXYA==",
|
||||
"ParentMetaPlanID": "hUcV0_EeNwUmXA6EoyNrtO-ZTD8H8F6LvNaSjMaPxB5ecFkA7y-5kc3q38cGumJENGHjtSoUndkYFUx0_xlJeg==",
|
||||
"Type": 1,
|
||||
"Name": "mail2022",
|
||||
"Title": "Mail Plus",
|
||||
"MaxDomains": 1,
|
||||
"MaxAddresses": 10,
|
||||
"MaxCalendars": 20,
|
||||
"MaxSpace": 16106127360,
|
||||
"MaxMembers": 1,
|
||||
"MaxVPN": 0,
|
||||
"MaxTier": 0,
|
||||
"Services": 1,
|
||||
"Features": 1,
|
||||
"State": 1,
|
||||
"Cycle": 12,
|
||||
"Currency": "CHF",
|
||||
"Amount": 4788,
|
||||
"Quantity": 1
|
||||
}
|
||||
],
|
||||
"Renew": 1,
|
||||
"External": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"AmountDue": 4788,
|
||||
"Amount": 4788,
|
||||
"Cycle": 12,
|
||||
"CouponDiscount": 0,
|
||||
"Gift": 0,
|
||||
"Currency": "CHF",
|
||||
"UnusedCredit": 0,
|
||||
"Credit": 0,
|
||||
"Proration": 0,
|
||||
"Coupon": null
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"Token": "21-Uv1NzluEO-4ZPHHjA87zU",
|
||||
"Status": 1
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"Code": 1000,
|
||||
"User": {
|
||||
"ID": "VhC0TBoR_SHd7-rjOLtY5lYQ_Dqw9e8Lu54FqyW05ssJ3eEo6hglLat6Aq9M5MaM66EZ2Nr327QQL5zyF15SvQ==",
|
||||
"Name": "test-mock-936",
|
||||
"Currency": "CHF",
|
||||
"Credit": 0,
|
||||
"Type": 1,
|
||||
"CreateTime": 1665679842,
|
||||
"MaxSpace": 524288000,
|
||||
"MaxUpload": 26214400,
|
||||
"UsedSpace": 0,
|
||||
"Subscribed": 0,
|
||||
"Services": 1,
|
||||
"MnemonicStatus": 0,
|
||||
"Role": 0,
|
||||
"Private": 1,
|
||||
"Delinquent": 0,
|
||||
"Keys": [],
|
||||
"ToMigrate": 1,
|
||||
"Email": "test-mock-936@proton.black",
|
||||
"DisplayName": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.test.android
|
||||
|
||||
import androidx.work.WorkManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.hilt.testing.TestInstallIn
|
||||
import io.mockk.mockk
|
||||
import me.proton.android.core.coreexample.di.NetworkConstantsModule
|
||||
import me.proton.android.core.coreexample.di.WorkManagerModule
|
||||
import me.proton.core.crypto.common.context.CryptoContext
|
||||
import me.proton.core.crypto.common.keystore.KeyStoreCrypto
|
||||
import me.proton.core.crypto.common.pgp.PGPCrypto
|
||||
import me.proton.core.crypto.common.srp.SrpCrypto
|
||||
import me.proton.core.crypto.dagger.CoreCryptoModule
|
||||
import me.proton.core.network.data.di.AlternativeApiPins
|
||||
import me.proton.core.network.data.di.CertificatePins
|
||||
import me.proton.core.network.data.di.DohProviderUrls
|
||||
import me.proton.core.paymentiap.dagger.CorePaymentIapBillingModule
|
||||
import me.proton.core.paymentiap.domain.BillingClientFactory
|
||||
import me.proton.core.test.android.mocks.FakeKeyStoreCrypto
|
||||
import me.proton.core.test.android.mocks.FakePGPCrypto
|
||||
import me.proton.core.test.android.mocks.FakeSrpCrypto
|
||||
import me.proton.core.test.android.mocks.FakeBillingClientFactory
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@TestInstallIn(
|
||||
components = [SingletonComponent::class],
|
||||
replaces = [
|
||||
CoreCryptoModule::class,
|
||||
CorePaymentIapBillingModule::class,
|
||||
NetworkConstantsModule::class,
|
||||
WorkManagerModule::class
|
||||
]
|
||||
)
|
||||
object TestComponent {
|
||||
// region CoreCryptoModule
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideKeyStoreCrypto(): KeyStoreCrypto = FakeKeyStoreCrypto()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCryptoContext(
|
||||
keyStoreCrypto: KeyStoreCrypto,
|
||||
srpCrypto: SrpCrypto
|
||||
): CryptoContext = object : CryptoContext {
|
||||
override val keyStoreCrypto: KeyStoreCrypto = keyStoreCrypto
|
||||
override val pgpCrypto: PGPCrypto = FakePGPCrypto()
|
||||
override val srpCrypto: SrpCrypto = srpCrypto
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSrpCrypto(): SrpCrypto = FakeSrpCrypto()
|
||||
// endregion
|
||||
|
||||
// region CorePaymentIapBillingModule
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTestBillingClientFactory(): FakeBillingClientFactory = FakeBillingClientFactory()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBillingClientProvider(factory: FakeBillingClientFactory): BillingClientFactory = factory
|
||||
// endregion
|
||||
|
||||
// region NetworkConstantsModule
|
||||
@DohProviderUrls
|
||||
@Provides
|
||||
fun provideDohProviderUrls(): Array<String> = emptyArray()
|
||||
|
||||
@CertificatePins
|
||||
@Provides
|
||||
fun provideCertificatePins() = emptyArray<String>()
|
||||
|
||||
@AlternativeApiPins
|
||||
@Provides
|
||||
fun provideAlternativeApiPins() = emptyList<String>()
|
||||
// endregion
|
||||
|
||||
// region WorkManagerModule
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWorkManager(): WorkManager = mockk(relaxed = true)
|
||||
// endregion
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.test.android
|
||||
|
||||
import android.util.Log
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import okhttp3.mockwebserver.Dispatcher
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.RecordedRequest
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
||||
/** Dispatcher for [okhttp3.mockwebserver.MockWebServer].
|
||||
* By default, it will try to find and return the contents of a file,
|
||||
* that correspond to the request path. For example, for request
|
||||
* `GET /auth/modulus`, it will load a file from assets directory: `GET/auth/modulus.json`.
|
||||
* If a file doesn't exist, it'll return 404.
|
||||
* If you don't want the default behavior, you can override the response
|
||||
* by calling [mockFromAssets] or [mock].
|
||||
*/
|
||||
class TestWebServerDispatcher : Dispatcher() {
|
||||
private val dateFormat = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'")
|
||||
private val responses = mutableMapOf<MockedEndpoint, MutableList<MockResponse>>()
|
||||
|
||||
override fun dispatch(request: RecordedRequest): MockResponse {
|
||||
val mockResponses = responses[MockedEndpoint(request.method!!, request.requestUrl?.encodedPath!!)]
|
||||
val queuedResponse = if (mockResponses != null && mockResponses.size > 1) {
|
||||
mockResponses.removeFirstOrNull()
|
||||
} else {
|
||||
mockResponses?.first()
|
||||
}
|
||||
val mockResponse = queuedResponse ?: kotlin.runCatching {
|
||||
val body = loadFromAssets("${request.method}${request.requestUrl?.encodedPath}.json")
|
||||
MockResponse().setResponseCode(200).setBody(body)
|
||||
}.getOrNull()
|
||||
|
||||
val response = mockResponse?.applyDefaultHeaders()
|
||||
Log.d("TestWebServerDispatcher", "Request $request")
|
||||
|
||||
return if (response != null) {
|
||||
Log.d("TestWebServerDispatcher", "Response $response")
|
||||
response
|
||||
} else {
|
||||
Log.e("TestWebServerDispatcher", "Response not found for request $request")
|
||||
MockResponse().setResponseCode(404)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MockResponse.applyDefaultHeaders(): MockResponse =
|
||||
addHeader("Set-Cookie", "Session-Id=test-session-id")
|
||||
.addHeader("Date", dateFormat.format(Date()))
|
||||
|
||||
/** Mocks a response for a request.
|
||||
* You can call this method multiple times, even with the same [requestMethod] and [requestPath].
|
||||
* In that case, the responses will be queued. Once they are served, they are removed,
|
||||
* so the next response for the same request will be served next.
|
||||
* @param requestMethod The HTTP method name ("GET", "POST" etc.)
|
||||
* @param requestPath The path of the incoming request, starting with `/`.
|
||||
* @param responsePath The path of the asset file which will be used as a response body.
|
||||
* @param responseCode The HTTP code for the response.
|
||||
*/
|
||||
fun mockFromAssets(
|
||||
requestMethod: String,
|
||||
requestPath: String,
|
||||
responsePath: String,
|
||||
responseCode: Int = 200
|
||||
) {
|
||||
val body = loadFromAssets(responsePath)
|
||||
val response = MockResponse().setResponseCode(responseCode).setBody(body)
|
||||
mock(
|
||||
requestMethod = requestMethod,
|
||||
requestPath = requestPath,
|
||||
mockResponse = response
|
||||
)
|
||||
}
|
||||
|
||||
fun mock(requestMethod: String, requestPath: String, mockResponse: MockResponse) {
|
||||
val key = MockedEndpoint(method = requestMethod, path = requestPath)
|
||||
val queuedResponses = responses[key] ?: mutableListOf<MockResponse>().also {
|
||||
responses[key] = it
|
||||
}
|
||||
queuedResponses.add(mockResponse)
|
||||
}
|
||||
|
||||
private fun loadFromAssets(path: String): String {
|
||||
val assetManager = InstrumentationRegistry.getInstrumentation().context.assets
|
||||
return assetManager.open(path).bufferedReader().use { it.readText() }
|
||||
}
|
||||
|
||||
private data class MockedEndpoint(
|
||||
val method: String,
|
||||
val path: String
|
||||
)
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.test.android.mocks
|
||||
|
||||
import com.android.billingclient.api.AcknowledgePurchaseResponseListener
|
||||
import com.android.billingclient.api.BillingClient
|
||||
import com.android.billingclient.api.BillingClientStateListener
|
||||
import com.android.billingclient.api.BillingResult
|
||||
import com.android.billingclient.api.ProductDetails
|
||||
import com.android.billingclient.api.ProductDetails.PricingPhase
|
||||
import com.android.billingclient.api.ProductDetailsResponseListener
|
||||
import com.android.billingclient.api.Purchase
|
||||
import com.android.billingclient.api.PurchasesResponseListener
|
||||
import com.android.billingclient.api.PurchasesUpdatedListener
|
||||
import com.android.billingclient.api.QueryPurchasesParams
|
||||
import io.mockk.every
|
||||
import io.mockk.justRun
|
||||
import io.mockk.mockk
|
||||
|
||||
internal fun BillingClient.mockBillingClientSuccess(listeners: () -> List<PurchasesUpdatedListener>) {
|
||||
val purchases = listOf(mockk<Purchase> {
|
||||
every { accountIdentifiers } returns mockk {
|
||||
every { obfuscatedAccountId } returns "obfuscated-acc-id"
|
||||
}
|
||||
every { isAcknowledged } returns true
|
||||
every { orderId } returns "order-id"
|
||||
every { packageName } returns "package-name"
|
||||
every { purchaseTime } returns 0
|
||||
every { products } returns listOf("googlemail_mail2022_12_renewing")
|
||||
every { purchaseState } returns Purchase.PurchaseState.PURCHASED
|
||||
every { purchaseToken } returns "google-purchase-token"
|
||||
})
|
||||
|
||||
mockStartConnection(BillingClient.BillingResponseCode.OK)
|
||||
mockLaunchBillingFlow(
|
||||
BillingClient.BillingResponseCode.OK,
|
||||
listeners,
|
||||
BillingClient.BillingResponseCode.OK,
|
||||
purchases
|
||||
)
|
||||
mockQueryProductDetailsAsync(
|
||||
BillingClient.BillingResponseCode.OK,
|
||||
priceAmountMicros = 99_990_000,
|
||||
priceCurrencyCode = "CHF",
|
||||
formattedPrice = "99.99",
|
||||
googleProductId = "googlemail_mail2022_12_renewing"
|
||||
)
|
||||
mockQueryPurchasesAsync(
|
||||
BillingClient.BillingResponseCode.OK,
|
||||
purchases
|
||||
)
|
||||
mockAcknowledgePurchase(BillingClient.BillingResponseCode.OK)
|
||||
}
|
||||
|
||||
internal fun BillingClient.mockAcknowledgePurchase(@BillingClient.BillingResponseCode responseCode: Int) {
|
||||
every { acknowledgePurchase(any(), any()) } answers {
|
||||
val listener = args[1] as AcknowledgePurchaseResponseListener
|
||||
listener.onAcknowledgePurchaseResponse(
|
||||
BillingResult.newBuilder().setResponseCode(responseCode).build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BillingClient.mockStartConnection(@BillingClient.BillingResponseCode responseCode: Int) {
|
||||
every { startConnection(any()) } answers {
|
||||
val listener = args[0] as BillingClientStateListener
|
||||
listener.onBillingSetupFinished(
|
||||
BillingResult.newBuilder().setResponseCode(responseCode).build()
|
||||
)
|
||||
}
|
||||
justRun { endConnection() }
|
||||
}
|
||||
|
||||
internal fun BillingClient.mockLaunchBillingFlow(
|
||||
@BillingClient.BillingResponseCode responseCode: Int,
|
||||
listeners: () -> List<PurchasesUpdatedListener>,
|
||||
@BillingClient.BillingResponseCode purchaseResponseCode: Int,
|
||||
purchases: List<Purchase>
|
||||
) {
|
||||
every { launchBillingFlow(any(), any()) } answers {
|
||||
listeners().forEach {
|
||||
it.onPurchasesUpdated(
|
||||
BillingResult.newBuilder().setResponseCode(purchaseResponseCode).build(),
|
||||
purchases
|
||||
)
|
||||
}
|
||||
BillingResult.newBuilder().setResponseCode(responseCode).build()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BillingClient.mockQueryProductDetailsAsync(
|
||||
@BillingClient.BillingResponseCode responseCode: Int,
|
||||
priceAmountMicros: Long,
|
||||
priceCurrencyCode: String,
|
||||
formattedPrice: String,
|
||||
googleProductId: String
|
||||
) {
|
||||
val pricingPhase = mockk<PricingPhase> {
|
||||
every { this@mockk.priceAmountMicros } returns priceAmountMicros
|
||||
every { this@mockk.priceCurrencyCode } returns priceCurrencyCode
|
||||
every { this@mockk.formattedPrice } returns formattedPrice
|
||||
}
|
||||
val productDetailsList = listOf(mockk<ProductDetails>(relaxed = true) {
|
||||
every { productId } returns googleProductId
|
||||
every { subscriptionOfferDetails } returns listOf(
|
||||
mockk {
|
||||
every { offerToken } returns "offer-token"
|
||||
every { pricingPhases } returns mockk {
|
||||
every { pricingPhaseList } returns listOf(pricingPhase)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
every { queryProductDetailsAsync(any(), any()) } answers {
|
||||
val listener = args[1] as ProductDetailsResponseListener
|
||||
listener.onProductDetailsResponse(
|
||||
BillingResult.newBuilder().setResponseCode(responseCode).build(),
|
||||
productDetailsList
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BillingClient.mockQueryPurchasesAsync(
|
||||
@BillingClient.BillingResponseCode responseCode: Int,
|
||||
purchases: List<Purchase>
|
||||
) {
|
||||
every { queryPurchasesAsync(any<QueryPurchasesParams>(), any()) } answers {
|
||||
val listener = args[1] as PurchasesResponseListener
|
||||
listener.onQueryPurchasesResponse(
|
||||
BillingResult.newBuilder().setResponseCode(responseCode).build(),
|
||||
purchases
|
||||
)
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package me.proton.core.test.android.mocks
|
||||
|
||||
import android.app.Activity
|
||||
import com.android.billingclient.api.AcknowledgePurchaseParams
|
||||
import com.android.billingclient.api.AcknowledgePurchaseResponseListener
|
||||
import com.android.billingclient.api.BillingClient
|
||||
import com.android.billingclient.api.BillingClientStateListener
|
||||
import com.android.billingclient.api.BillingFlowParams
|
||||
import com.android.billingclient.api.BillingResult
|
||||
import com.android.billingclient.api.ConsumeParams
|
||||
import com.android.billingclient.api.ConsumeResponseListener
|
||||
import com.android.billingclient.api.InAppMessageParams
|
||||
import com.android.billingclient.api.InAppMessageResponseListener
|
||||
import com.android.billingclient.api.PriceChangeConfirmationListener
|
||||
import com.android.billingclient.api.PriceChangeFlowParams
|
||||
import com.android.billingclient.api.ProductDetailsResponseListener
|
||||
import com.android.billingclient.api.PurchaseHistoryResponseListener
|
||||
import com.android.billingclient.api.PurchasesResponseListener
|
||||
import com.android.billingclient.api.PurchasesUpdatedListener
|
||||
import com.android.billingclient.api.QueryProductDetailsParams
|
||||
import com.android.billingclient.api.QueryPurchaseHistoryParams
|
||||
import com.android.billingclient.api.QueryPurchasesParams
|
||||
import com.android.billingclient.api.SkuDetailsParams
|
||||
import com.android.billingclient.api.SkuDetailsResponseListener
|
||||
import io.mockk.mockk
|
||||
import me.proton.core.paymentiap.domain.BillingClientFactory
|
||||
|
||||
class FakeBillingClientFactory : BillingClientFactory {
|
||||
private val _listeners = mutableListOf<PurchasesUpdatedListener>()
|
||||
|
||||
val billingClient: BillingClient = mockk()
|
||||
val listeners: List<PurchasesUpdatedListener> = _listeners
|
||||
|
||||
override fun invoke(purchasesUpdatedListener: PurchasesUpdatedListener): BillingClient {
|
||||
_listeners.add(purchasesUpdatedListener)
|
||||
return FakeBillingClient(billingClient) { _listeners.remove(purchasesUpdatedListener) }
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
private class FakeBillingClient(
|
||||
private val mock: BillingClient,
|
||||
private val onConnectionEnd: () -> Unit
|
||||
) : BillingClient() {
|
||||
override fun getConnectionState(): Int = mock.connectionState
|
||||
|
||||
override fun isFeatureSupported(feature: String): BillingResult = mock.isFeatureSupported(feature)
|
||||
|
||||
override fun launchBillingFlow(activity: Activity, params: BillingFlowParams): BillingResult =
|
||||
mock.launchBillingFlow(activity, params)
|
||||
|
||||
override fun showInAppMessages(
|
||||
activity: Activity,
|
||||
params: InAppMessageParams,
|
||||
listener: InAppMessageResponseListener
|
||||
): BillingResult = mock.showInAppMessages(activity, params, listener)
|
||||
|
||||
override fun acknowledgePurchase(params: AcknowledgePurchaseParams, listener: AcknowledgePurchaseResponseListener) =
|
||||
mock.acknowledgePurchase(params, listener)
|
||||
|
||||
override fun consumeAsync(params: ConsumeParams, listener: ConsumeResponseListener) =
|
||||
mock.consumeAsync(params, listener)
|
||||
|
||||
override fun endConnection() {
|
||||
mock.endConnection()
|
||||
onConnectionEnd()
|
||||
}
|
||||
|
||||
override fun launchPriceChangeConfirmationFlow(
|
||||
activity: Activity,
|
||||
params: PriceChangeFlowParams,
|
||||
listener: PriceChangeConfirmationListener
|
||||
) = mock.launchPriceChangeConfirmationFlow(activity, params, listener)
|
||||
|
||||
override fun queryProductDetailsAsync(params: QueryProductDetailsParams, listener: ProductDetailsResponseListener) =
|
||||
mock.queryProductDetailsAsync(params, listener)
|
||||
|
||||
override fun queryPurchaseHistoryAsync(
|
||||
params: QueryPurchaseHistoryParams,
|
||||
listener: PurchaseHistoryResponseListener
|
||||
) = mock.queryPurchaseHistoryAsync(params, listener)
|
||||
|
||||
override fun queryPurchaseHistoryAsync(skuType: String, listener: PurchaseHistoryResponseListener) =
|
||||
mock.queryPurchaseHistoryAsync(skuType, listener)
|
||||
|
||||
override fun queryPurchasesAsync(params: QueryPurchasesParams, listener: PurchasesResponseListener) =
|
||||
mock.queryPurchasesAsync(params, listener)
|
||||
|
||||
override fun queryPurchasesAsync(skuType: String, listener: PurchasesResponseListener) =
|
||||
mock.queryPurchasesAsync(skuType, listener)
|
||||
|
||||
override fun querySkuDetailsAsync(params: SkuDetailsParams, listener: SkuDetailsResponseListener) =
|
||||
mock.querySkuDetailsAsync(params, listener)
|
||||
|
||||
override fun startConnection(listener: BillingClientStateListener) = mock.startConnection(listener)
|
||||
|
||||
override fun isReady(): Boolean = mock.isReady
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.test.android.mocks
|
||||
|
||||
import me.proton.core.crypto.common.keystore.EncryptedByteArray
|
||||
import me.proton.core.crypto.common.keystore.EncryptedString
|
||||
import me.proton.core.crypto.common.keystore.KeyStoreCrypto
|
||||
import me.proton.core.crypto.common.keystore.PlainByteArray
|
||||
|
||||
class FakeKeyStoreCrypto : KeyStoreCrypto {
|
||||
override fun isUsingKeyStore(): Boolean = false
|
||||
override fun encrypt(value: String): EncryptedString = value
|
||||
override fun decrypt(value: EncryptedString): String = value
|
||||
override fun encrypt(value: PlainByteArray): EncryptedByteArray = EncryptedByteArray(value.array)
|
||||
override fun decrypt(value: EncryptedByteArray): PlainByteArray = PlainByteArray(value.array)
|
||||
}
|
||||
+367
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.test.android.mocks
|
||||
|
||||
import io.mockk.mockk
|
||||
import me.proton.core.crypto.common.pgp.Armored
|
||||
import me.proton.core.crypto.common.pgp.DataPacket
|
||||
import me.proton.core.crypto.common.pgp.DecryptedData
|
||||
import me.proton.core.crypto.common.pgp.DecryptedFile
|
||||
import me.proton.core.crypto.common.pgp.DecryptedText
|
||||
import me.proton.core.crypto.common.pgp.EncryptedFile
|
||||
import me.proton.core.crypto.common.pgp.EncryptedMessage
|
||||
import me.proton.core.crypto.common.pgp.EncryptedPacket
|
||||
import me.proton.core.crypto.common.pgp.EncryptedSignature
|
||||
import me.proton.core.crypto.common.pgp.HashKey
|
||||
import me.proton.core.crypto.common.pgp.KeyPacket
|
||||
import me.proton.core.crypto.common.pgp.PGPCrypto
|
||||
import me.proton.core.crypto.common.pgp.PGPHeader
|
||||
import me.proton.core.crypto.common.pgp.SessionKey
|
||||
import me.proton.core.crypto.common.pgp.Signature
|
||||
import me.proton.core.crypto.common.pgp.Unarmored
|
||||
import me.proton.core.crypto.common.pgp.UnlockedKey
|
||||
import me.proton.core.crypto.common.pgp.VerificationTime
|
||||
import java.io.File
|
||||
import java.util.Base64
|
||||
|
||||
class FakePGPCrypto : PGPCrypto {
|
||||
private val decoder = Base64.getDecoder()
|
||||
private val encoder = Base64.getEncoder()
|
||||
|
||||
override fun isPublicKey(key: Armored): Boolean {
|
||||
return mockk()
|
||||
}
|
||||
|
||||
override fun isPrivateKey(key: Armored): Boolean {
|
||||
return mockk()
|
||||
}
|
||||
|
||||
override fun isValidKey(key: Armored): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun lock(unlockedKey: Unarmored, passphrase: ByteArray): Armored {
|
||||
return decoder.decode(unlockedKey).decodeToString()
|
||||
}
|
||||
|
||||
override fun unlock(privateKey: Armored, passphrase: ByteArray): UnlockedKey {
|
||||
return object : UnlockedKey {
|
||||
override val value: Unarmored get() = encoder.encode(privateKey.encodeToByteArray())
|
||||
override fun close() = Unit
|
||||
}
|
||||
}
|
||||
|
||||
override fun encryptText(plainText: String, publicKey: Armored): EncryptedMessage {
|
||||
return plainText
|
||||
}
|
||||
|
||||
override fun encryptData(data: ByteArray, publicKey: Armored): EncryptedMessage {
|
||||
return encoder.encodeToString(data)
|
||||
}
|
||||
|
||||
override fun encryptData(data: ByteArray, sessionKey: SessionKey): DataPacket {
|
||||
return data
|
||||
}
|
||||
|
||||
override fun encryptFile(source: File, destination: File, sessionKey: SessionKey): EncryptedFile {
|
||||
TODO("Not yet implemented: encryptFile")
|
||||
}
|
||||
|
||||
override fun encryptAndSignText(plainText: String, publicKey: Armored, unlockedKey: Unarmored): EncryptedMessage {
|
||||
TODO("Not yet implemented: encryptAndSignText")
|
||||
}
|
||||
|
||||
override fun encryptAndSignTextWithCompression(
|
||||
plainText: String,
|
||||
publicKey: Armored,
|
||||
unlockedKey: Unarmored
|
||||
): EncryptedMessage {
|
||||
TODO("Not yet implemented: encryptAndSignTextWithCompression")
|
||||
}
|
||||
|
||||
override fun encryptAndSignData(data: ByteArray, publicKey: Armored, unlockedKey: Unarmored): EncryptedMessage {
|
||||
TODO("Not yet implemented: encryptAndSignData")
|
||||
}
|
||||
|
||||
override fun encryptAndSignDataWithCompression(
|
||||
data: ByteArray,
|
||||
publicKey: Armored,
|
||||
unlockedKey: Unarmored
|
||||
): EncryptedMessage {
|
||||
TODO("Not yet implemented: encryptAndSignDataWithCompression")
|
||||
}
|
||||
|
||||
override fun encryptAndSignData(data: ByteArray, sessionKey: SessionKey, unlockedKey: Unarmored): DataPacket {
|
||||
TODO("Not yet implemented: encryptAndSignData")
|
||||
}
|
||||
|
||||
override fun encryptAndSignFile(
|
||||
source: File,
|
||||
destination: File,
|
||||
sessionKey: SessionKey,
|
||||
unlockedKey: Unarmored
|
||||
): EncryptedFile {
|
||||
TODO("Not yet implemented: encryptAndSignFile")
|
||||
}
|
||||
|
||||
override fun encryptSessionKey(sessionKey: SessionKey, publicKey: Armored): KeyPacket {
|
||||
TODO("Not yet implemented: encryptSessionKey")
|
||||
}
|
||||
|
||||
override fun encryptSessionKeyWithPassword(sessionKey: SessionKey, password: ByteArray): KeyPacket {
|
||||
TODO("Not yet implemented: encryptSessionKeyWithPassword")
|
||||
}
|
||||
|
||||
override fun decryptText(message: EncryptedMessage, unlockedKey: Unarmored): String {
|
||||
TODO("Not yet implemented: decryptText")
|
||||
}
|
||||
|
||||
override fun decryptData(message: EncryptedMessage, unlockedKey: Unarmored): ByteArray {
|
||||
TODO("Not yet implemented: decryptData")
|
||||
}
|
||||
|
||||
override fun decryptData(data: DataPacket, sessionKey: SessionKey): ByteArray {
|
||||
TODO("Not yet implemented: decryptData")
|
||||
}
|
||||
|
||||
override fun decryptFile(source: EncryptedFile, destination: File, sessionKey: SessionKey): DecryptedFile {
|
||||
TODO("Not yet implemented: decryptFile")
|
||||
}
|
||||
|
||||
override fun decryptAndVerifyText(
|
||||
message: EncryptedMessage,
|
||||
publicKeys: List<Armored>,
|
||||
unlockedKeys: List<Unarmored>,
|
||||
time: VerificationTime
|
||||
): DecryptedText {
|
||||
TODO("Not yet implemented: decryptAndVerifyText")
|
||||
}
|
||||
|
||||
override fun decryptAndVerifyData(
|
||||
message: EncryptedMessage,
|
||||
publicKeys: List<Armored>,
|
||||
unlockedKeys: List<Unarmored>,
|
||||
time: VerificationTime
|
||||
): DecryptedData {
|
||||
TODO("Not yet implemented: decryptAndVerifyData")
|
||||
}
|
||||
|
||||
override fun decryptAndVerifyData(
|
||||
data: DataPacket,
|
||||
sessionKey: SessionKey,
|
||||
publicKeys: List<Armored>,
|
||||
time: VerificationTime
|
||||
): DecryptedData {
|
||||
TODO("Not yet implemented: decryptAndVerifyData")
|
||||
}
|
||||
|
||||
override fun decryptAndVerifyFile(
|
||||
source: EncryptedFile,
|
||||
destination: File,
|
||||
sessionKey: SessionKey,
|
||||
publicKeys: List<Armored>,
|
||||
time: VerificationTime
|
||||
): DecryptedFile {
|
||||
TODO("Not yet implemented: decryptAndVerifyFile")
|
||||
}
|
||||
|
||||
override fun decryptSessionKey(keyPacket: KeyPacket, unlockedKey: Unarmored): SessionKey {
|
||||
TODO("Not yet implemented: decryptSessionKey")
|
||||
}
|
||||
|
||||
override fun decryptSessionKeyWithPassword(keyPacket: KeyPacket, password: ByteArray): SessionKey {
|
||||
TODO("Not yet implemented: decryptSessionKeyWithPassword")
|
||||
}
|
||||
|
||||
override fun signText(plainText: String, unlockedKey: Unarmored): Signature {
|
||||
return "${plainText.hashCode()}"
|
||||
}
|
||||
|
||||
override fun signData(data: ByteArray, unlockedKey: Unarmored): Signature {
|
||||
TODO("Not yet implemented: signData")
|
||||
}
|
||||
|
||||
override fun signFile(file: File, unlockedKey: Unarmored): Signature {
|
||||
TODO("Not yet implemented: signFile")
|
||||
}
|
||||
|
||||
override fun signTextEncrypted(
|
||||
plainText: String,
|
||||
unlockedKey: Unarmored,
|
||||
encryptionKeys: List<Armored>
|
||||
): EncryptedSignature {
|
||||
TODO("Not yet implemented: signTextEncrypted")
|
||||
}
|
||||
|
||||
override fun signDataEncrypted(
|
||||
data: ByteArray,
|
||||
unlockedKey: Unarmored,
|
||||
encryptionKeys: List<Armored>
|
||||
): EncryptedSignature {
|
||||
TODO("Not yet implemented: signDataEncrypted")
|
||||
}
|
||||
|
||||
override fun signFileEncrypted(
|
||||
file: File,
|
||||
unlockedKey: Unarmored,
|
||||
encryptionKeys: List<Armored>
|
||||
): EncryptedSignature {
|
||||
TODO("Not yet implemented: signFileEncrypted")
|
||||
}
|
||||
|
||||
override fun verifyText(
|
||||
plainText: String,
|
||||
signature: Armored,
|
||||
publicKey: Armored,
|
||||
time: VerificationTime
|
||||
): Boolean {
|
||||
return "${plainText.hashCode()}" == signature
|
||||
}
|
||||
|
||||
override fun verifyData(data: ByteArray, signature: Armored, publicKey: Armored, time: VerificationTime): Boolean {
|
||||
TODO("Not yet implemented: verifyData")
|
||||
}
|
||||
|
||||
override fun verifyFile(
|
||||
file: DecryptedFile,
|
||||
signature: Armored,
|
||||
publicKey: Armored,
|
||||
time: VerificationTime
|
||||
): Boolean {
|
||||
TODO("Not yet implemented: verifyFile")
|
||||
}
|
||||
|
||||
override fun getVerifiedTimestampOfText(
|
||||
plainText: String,
|
||||
signature: Armored,
|
||||
publicKey: Armored,
|
||||
time: VerificationTime
|
||||
): Long? {
|
||||
TODO("Not yet implemented: getVerifiedTimestampOfText")
|
||||
}
|
||||
|
||||
override fun getVerifiedTimestampOfData(
|
||||
data: ByteArray,
|
||||
signature: Armored,
|
||||
publicKey: Armored,
|
||||
time: VerificationTime
|
||||
): Long? {
|
||||
TODO("Not yet implemented: getVerifiedTimestampOfData")
|
||||
}
|
||||
|
||||
override fun verifyTextEncrypted(
|
||||
plainText: String,
|
||||
encryptedSignature: EncryptedSignature,
|
||||
privateKey: Unarmored,
|
||||
publicKeys: List<Armored>,
|
||||
time: VerificationTime
|
||||
): Boolean {
|
||||
TODO("Not yet implemented: verifyTextEncrypted")
|
||||
}
|
||||
|
||||
override fun verifyDataEncrypted(
|
||||
data: ByteArray,
|
||||
encryptedSignature: EncryptedSignature,
|
||||
privateKey: Unarmored,
|
||||
publicKeys: List<Armored>,
|
||||
time: VerificationTime
|
||||
): Boolean {
|
||||
TODO("Not yet implemented: verifyDataEncrypted")
|
||||
}
|
||||
|
||||
override fun verifyFileEncrypted(
|
||||
file: File,
|
||||
encryptedSignature: EncryptedSignature,
|
||||
privateKey: Unarmored,
|
||||
publicKeys: List<Armored>,
|
||||
time: VerificationTime
|
||||
): Boolean {
|
||||
TODO("Not yet implemented: verifyFileEncrypted")
|
||||
}
|
||||
|
||||
override fun getArmored(data: Unarmored, header: PGPHeader): Armored {
|
||||
TODO("Not yet implemented: getArmored")
|
||||
}
|
||||
|
||||
override fun getUnarmored(data: Armored): Unarmored {
|
||||
TODO("Not yet implemented: getUnarmored")
|
||||
}
|
||||
|
||||
override fun getEncryptedPackets(message: EncryptedMessage): List<EncryptedPacket> {
|
||||
TODO("Not yet implemented: getEncryptedPackets")
|
||||
}
|
||||
|
||||
override fun getPublicKey(privateKey: Armored): Armored {
|
||||
return privateKey
|
||||
}
|
||||
|
||||
override fun getFingerprint(key: Armored): String {
|
||||
TODO("Not yet implemented: getFingerprint")
|
||||
}
|
||||
|
||||
override fun getJsonSHA256Fingerprints(key: Armored): String {
|
||||
TODO("Not yet implemented: getJsonSHA256Fingerprints")
|
||||
}
|
||||
|
||||
override fun getBase64Encoded(array: ByteArray): String {
|
||||
TODO("Not yet implemented: getBase64Encoded")
|
||||
}
|
||||
|
||||
override fun getBase64Decoded(string: String): ByteArray {
|
||||
TODO("Not yet implemented: getBase64Decoded")
|
||||
}
|
||||
|
||||
override fun getPassphrase(password: ByteArray, encodedSalt: String): ByteArray {
|
||||
return password
|
||||
}
|
||||
|
||||
override fun generateNewSessionKey(): SessionKey {
|
||||
TODO("Not yet implemented: generateNewSessionKey")
|
||||
}
|
||||
|
||||
override fun generateNewHashKey(): HashKey {
|
||||
TODO("Not yet implemented: generateNewHashKey")
|
||||
}
|
||||
|
||||
override fun generateNewKeySalt(): String {
|
||||
TODO("Not yet implemented: generateNewKeySalt")
|
||||
}
|
||||
|
||||
override fun generateNewToken(size: Long): ByteArray {
|
||||
TODO("Not yet implemented: generateNewToken")
|
||||
}
|
||||
|
||||
override fun generateRandomBytes(size: Long): ByteArray {
|
||||
TODO("Not yet implemented: generateRandomBytes")
|
||||
}
|
||||
|
||||
override fun generateNewPrivateKey(username: String, domain: String, passphrase: ByteArray): Armored {
|
||||
TODO("Not yet implemented: generateNewPrivateKey")
|
||||
}
|
||||
|
||||
override fun updatePrivateKeyPassphrase(
|
||||
privateKey: Armored,
|
||||
passphrase: ByteArray,
|
||||
newPassphrase: ByteArray
|
||||
): Armored {
|
||||
TODO("Not yet implemented: updatePrivateKeyPassphrase")
|
||||
}
|
||||
|
||||
override fun updateTime(epochSeconds: Long) = Unit
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.test.android.mocks
|
||||
|
||||
import me.proton.core.crypto.common.srp.Auth
|
||||
import me.proton.core.crypto.common.srp.SrpCrypto
|
||||
import me.proton.core.crypto.common.srp.SrpProofs
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
class FakeSrpCrypto : SrpCrypto {
|
||||
override fun generateSrpProofs(
|
||||
username: String,
|
||||
password: ByteArray,
|
||||
version: Long,
|
||||
salt: String,
|
||||
modulus: String,
|
||||
serverEphemeral: String
|
||||
): SrpProofs {
|
||||
return SrpProofs(
|
||||
clientEphemeral = "6PckVdFlaPSR8UhlCCnLg5Wpmob++1s1EPGOqjhR2etOgqnfQd102t0LDeM8QKO90yPsDbnSjjGcLkK7nWeCLhORNCs71XldJg8QP5ffwN4deXakWw5GOdMBqBMtHLAy9elCewg9B0wxjdYimbdKia7Zk/HyqMKK2Skd3JpRPNNUxK798G4dlISe7UaQ2L3PEIx5aRp+WmlRfT3oq0I4qspjrU64u+44KkchAi+/yQFj4PJQVDsc6NnGL28zGH7OIwmclOQgRZ70+4TiifrnoDfpewg+z37er5t/exYkhckfUaAt4EGQyc2zh8JtV1uu3/GjS8M5bKkzDZ2iHo6snA==",
|
||||
clientProof = "HkY/EcSS75nYf0IPcIfQEIx7Q5+P8t1KQJLTVibFFgeGNB8UNcBpTQO14ih3RWakP/gXrP0fq+ucYU8BluanISMaxXAR6wbPTLEYRQ0vAJJaHVNhOQJ3P31hklh6AKrXikaVU5+z9fToL/TSfb36E4Vkaj0TYenNo4rDKEnK2xuaLk2oezz8g+CjCnd1ekjCRv+zD2dEZPIrcYH6dVwM98YMxov8pzBGEaafMxNNo2GLifWKixAESAnLnLob8dnomv8tZTwZnSkOrT5aFQovQzq+42RwEprTP0fVHt7PnfFkdUzEUFLvbrnWz3QLx7b2zvaTMrW7eQ8AH48IdHyQlQ==",
|
||||
expectedServerProof = "hD3IgA8zIRu7/0uCMns2DTNv0abl++eEaGEbSCKmgknmNL9JGk0MgaUM3ut+t7jTfi3PJKtNZCU5kMN5oKFzMDiAojN8pG6XrjOorbxDN5PmfP7XLUHgFolLlxgb93sKyZPie73J/cf22VHkmlHmFTlbMZs2gqTrUDF/D4xdewlrA5weu4myP/baanaPaLR9UzFrgOMe69k6Ei2iUbOtuntUyj4zqiK1JvuyDjVZSYD91aVwx25VNLZMc+bTAWTwYjVQO+z6J7CnyDyRBu9OXzO54YwpiMdhWKvHZIPx5xxPWJzPtA4SaFl+t57/K1C23MY4OHnxKaQvllN5nKG+rQ=="
|
||||
)
|
||||
}
|
||||
|
||||
override fun calculatePasswordVerifier(
|
||||
username: String,
|
||||
password: ByteArray,
|
||||
modulusId: String,
|
||||
modulus: String
|
||||
): Auth {
|
||||
return Auth(
|
||||
version = 4,
|
||||
modulusId = "e_DzaCA0dI-vawVq4ab00WFHBMCbZ4ZbVEFBIAxEIkg_ozPFoN4QAQxTjO7vlQ5P9Y-MpUTgUAZ_CICYQz2uxA==",
|
||||
salt = "v0qLXBUgfQBIcQ==",
|
||||
verifier = "jHbPpeayCOGVuLK/cT+g/2Pw9UFgNapy2IDD1GELHej2uCaNfN2uHRoy/XGL27Ngrorb1nkt5ZWFvW/6HKjyUxn14h7ECU6jG6l+tkVLLd8RlDuxQ+PAoSJ4Zuog6H9yEupfuT0x1v6UuITS/i+PRVA4NQ47LaLXpAgiosiX9aLx5g67v8gbMSB50bydgJZakFXAbBiU/mbZwCpJzmyIlxbQSU0CGXfInN0uMbfOJ5AyBkLRZZCYVkWjyzNKABGkBS8nNx3l0p5LtNN5W4F59w27zxHYBli+zPGPgAbNqww4PjSSnhM1GFCF/VbGNnAcMlzwerI2vuzBSCqHAv2/CA=="
|
||||
)
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.test.android.uitests.giap
|
||||
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import com.android.billingclient.api.BillingClient
|
||||
import dagger.hilt.android.testing.BindValue
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import me.proton.android.core.coreexample.CoreExampleLogger
|
||||
import me.proton.android.core.coreexample.MainActivity
|
||||
import me.proton.core.network.data.di.BaseProtonApiUrl
|
||||
import me.proton.core.test.android.TestWebServerDispatcher
|
||||
import me.proton.core.test.android.mocks.FakeBillingClientFactory
|
||||
import me.proton.core.test.android.mocks.mockBillingClientSuccess
|
||||
import me.proton.core.test.android.robots.auth.AddAccountRobot
|
||||
import me.proton.core.test.android.robots.payments.GoogleIAPRobot
|
||||
import me.proton.core.test.android.uitests.CoreexampleRobot
|
||||
import me.proton.core.util.kotlin.CoreLogger
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.Rule
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import me.proton.core.test.android.plugins.data.Plan as TestPlan
|
||||
|
||||
@HiltAndroidTest
|
||||
class GiapUpgradeTests {
|
||||
private val testUsername = "test-mock-936"
|
||||
private val testPassword = "password"
|
||||
|
||||
@get:Rule(order = Rule.DEFAULT_ORDER - 1)
|
||||
val hiltAndroidRule = HiltAndroidRule(this)
|
||||
|
||||
@BindValue
|
||||
@BaseProtonApiUrl
|
||||
lateinit var baseProtonApiUrl: HttpUrl
|
||||
|
||||
@Inject
|
||||
lateinit var billingClientFactory: FakeBillingClientFactory
|
||||
|
||||
private val billingClient: BillingClient get() = billingClientFactory.billingClient
|
||||
private lateinit var dispatcher: TestWebServerDispatcher
|
||||
private lateinit var webServer: MockWebServer
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
Timber.plant(Timber.DebugTree())
|
||||
CoreLogger.set(CoreExampleLogger())
|
||||
|
||||
dispatcher = TestWebServerDispatcher()
|
||||
webServer = MockWebServer().apply {
|
||||
dispatcher = this@GiapUpgradeTests.dispatcher
|
||||
}
|
||||
baseProtonApiUrl = webServer.url("/")
|
||||
|
||||
hiltAndroidRule.inject()
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
fun tearDown() {
|
||||
webServer.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun freeUserUpgradeAllProvidersAvailable() {
|
||||
freeUserCanUpgrade()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun freeUserUpgradeOnlyGiapAvailable() {
|
||||
dispatcher.mockFromAssets(
|
||||
"GET", "/payments/v4/status/google",
|
||||
"GET/payments/v4/status/google-iap-only.json"
|
||||
)
|
||||
freeUserCanUpgrade()
|
||||
}
|
||||
|
||||
private fun freeUserCanUpgrade() {
|
||||
billingClient.mockBillingClientSuccess { billingClientFactory.listeners }
|
||||
|
||||
dispatcher.mockFromAssets(
|
||||
"GET", "/payments/v4/subscription",
|
||||
"GET/payments/v4/subscription-none.json", 422
|
||||
)
|
||||
dispatcher.mockFromAssets(
|
||||
"GET", "/payments/v4/subscription",
|
||||
"GET/payments/v4/subscription-mail-plus-google-managed.json"
|
||||
)
|
||||
|
||||
dispatcher.mockFromAssets(
|
||||
"GET", "/users",
|
||||
"GET/users-with-keys-not-subscribed.json"
|
||||
)
|
||||
dispatcher.mockFromAssets(
|
||||
"GET", "/organizations",
|
||||
"GET/organizations-none.json", 422
|
||||
)
|
||||
dispatcher.mockFromAssets(
|
||||
"GET", "/addresses",
|
||||
"GET/addresses-with-keys.json"
|
||||
)
|
||||
dispatcher.mockFromAssets(
|
||||
"POST", "/payments/v4/subscription",
|
||||
"POST/payments/v4/subscription-mail-plus-google-managed.json"
|
||||
)
|
||||
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
|
||||
AddAccountRobot()
|
||||
.signIn()
|
||||
.username(testUsername)
|
||||
.password(testPassword)
|
||||
.signIn<CoreexampleRobot>()
|
||||
|
||||
CoreexampleRobot()
|
||||
.plansUpgrade()
|
||||
.toggleExpandPlan(TestPlan.Plus)
|
||||
.selectPlan<GoogleIAPRobot>(TestPlan.Plus)
|
||||
.apply {
|
||||
verify<GoogleIAPRobot.Verify> {
|
||||
googleIAPElementsDisplayed()
|
||||
payWithGoogleButtonIsClickable()
|
||||
}
|
||||
}
|
||||
.payWithGPay<CoreexampleRobot>()
|
||||
.plansCurrent()
|
||||
.verify {
|
||||
currentPlanDetailsDisplayed()
|
||||
planDetailsDisplayed(TestPlan.Plus)
|
||||
}
|
||||
}
|
||||
}
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.test.android.uitests.giap
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.android.billingclient.api.BillingClient
|
||||
import com.android.billingclient.api.BillingClient.BillingResponseCode
|
||||
import dagger.hilt.android.testing.BindValue
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import me.proton.android.core.coreexample.CoreExampleLogger
|
||||
import me.proton.core.auth.presentation.ui.AddAccountActivity
|
||||
import me.proton.core.network.data.di.BaseProtonApiUrl
|
||||
import me.proton.core.plan.presentation.entity.PlanInput
|
||||
import me.proton.core.plan.presentation.ui.StartPlanChooser
|
||||
import me.proton.core.plan.presentation.ui.UpgradeActivity
|
||||
import me.proton.core.test.android.TestWebServerDispatcher
|
||||
import me.proton.core.test.android.mocks.FakeBillingClientFactory
|
||||
import me.proton.core.test.android.mocks.mockBillingClientSuccess
|
||||
import me.proton.core.test.android.mocks.mockStartConnection
|
||||
import me.proton.core.test.android.robots.auth.AddAccountRobot
|
||||
import me.proton.core.test.android.robots.auth.signup.RecoveryMethodsRobot
|
||||
import me.proton.core.test.android.robots.auth.signup.SignupFinishedRobot
|
||||
import me.proton.core.test.android.robots.payments.GoogleIAPRobot
|
||||
import me.proton.core.test.android.robots.plans.SelectPlanRobot
|
||||
import me.proton.core.util.kotlin.CoreLogger
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.Rule
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import me.proton.core.paymentiap.presentation.R as PaymentIapR
|
||||
import me.proton.core.test.android.plugins.data.Plan as TestPlan
|
||||
|
||||
@HiltAndroidTest
|
||||
class SignupWithGoogleIapTests {
|
||||
private val testUsername = "test-mock-936"
|
||||
private val testPassword = "password"
|
||||
|
||||
@get:Rule(order = Rule.DEFAULT_ORDER - 1)
|
||||
val hiltAndroidRule = HiltAndroidRule(this)
|
||||
|
||||
@BindValue
|
||||
@BaseProtonApiUrl
|
||||
lateinit var baseProtonApiUrl: HttpUrl
|
||||
|
||||
@Inject
|
||||
lateinit var billingClientFactory: FakeBillingClientFactory
|
||||
|
||||
private val appContext: Context get() = ApplicationProvider.getApplicationContext()
|
||||
private val billingClient: BillingClient get() = billingClientFactory.billingClient
|
||||
private lateinit var dispatcher: TestWebServerDispatcher
|
||||
private lateinit var webServer: MockWebServer
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
Timber.plant(Timber.DebugTree())
|
||||
CoreLogger.set(CoreExampleLogger())
|
||||
|
||||
dispatcher = TestWebServerDispatcher()
|
||||
webServer = MockWebServer().apply {
|
||||
dispatcher = this@SignupWithGoogleIapTests.dispatcher
|
||||
}
|
||||
baseProtonApiUrl = webServer.url("/")
|
||||
|
||||
hiltAndroidRule.inject()
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
fun tearDown() {
|
||||
webServer.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun googleBillingNotAvailable() {
|
||||
billingClient.mockStartConnection(BillingResponseCode.BILLING_UNAVAILABLE)
|
||||
|
||||
val intent = StartPlanChooser().createIntent(appContext, PlanInput())
|
||||
ActivityScenario.launch<UpgradeActivity>(intent)
|
||||
|
||||
SelectPlanRobot()
|
||||
.toggleExpandPlan(TestPlan.Plus)
|
||||
.selectPlan<GoogleIAPRobot>(TestPlan.Plus)
|
||||
.verify {
|
||||
errorSnackbarDisplayed(PaymentIapR.string.payments_iap_error_billing_client_unavailable)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun signUpAndSubscribeGiapOnly() {
|
||||
billingClient.mockBillingClientSuccess { billingClientFactory.listeners }
|
||||
|
||||
dispatcher.mockFromAssets(
|
||||
"GET", "/payments/v4/status/google",
|
||||
"GET/payments/v4/status/google-iap-only.json"
|
||||
)
|
||||
dispatcher.mockFromAssets(
|
||||
"POST", "/payments/v4/subscription",
|
||||
"POST/payments/v4/subscription-mail-plus-google-managed.json"
|
||||
)
|
||||
dispatcher.mockFromAssets(
|
||||
"GET", "/users",
|
||||
"GET/users-with-keys-subscribed.json"
|
||||
)
|
||||
dispatcher.mockFromAssets(
|
||||
"GET", "/addresses",
|
||||
"GET/addresses-with-keys.json"
|
||||
)
|
||||
|
||||
ActivityScenario.launch(AddAccountActivity::class.java)
|
||||
AddAccountRobot()
|
||||
.createAccount()
|
||||
.setUsername(testUsername)
|
||||
.setAndConfirmPassword<RecoveryMethodsRobot>(testPassword)
|
||||
.skip()
|
||||
.skipConfirm<SelectPlanRobot>()
|
||||
.toggleExpandPlan(TestPlan.Plus)
|
||||
.selectPlan<GoogleIAPRobot>(TestPlan.Plus)
|
||||
.payWithGPay<SignupFinishedRobot>()
|
||||
.verify {
|
||||
signupFinishedDisplayed()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun signUpAndSubscribeAllPaymentProviders() {
|
||||
billingClient.mockBillingClientSuccess { billingClientFactory.listeners }
|
||||
|
||||
dispatcher.mockFromAssets(
|
||||
"GET", "/users",
|
||||
"GET/users-with-keys-subscribed.json"
|
||||
)
|
||||
dispatcher.mockFromAssets(
|
||||
"POST", "/payments/v4/subscription",
|
||||
"POST/payments/v4/subscription-mail-plus-google-managed.json"
|
||||
)
|
||||
dispatcher.mockFromAssets(
|
||||
"GET", "/addresses",
|
||||
"GET/addresses-with-keys.json"
|
||||
)
|
||||
|
||||
ActivityScenario.launch(AddAccountActivity::class.java)
|
||||
AddAccountRobot()
|
||||
.createAccount()
|
||||
.setUsername(testUsername)
|
||||
.setAndConfirmPassword<RecoveryMethodsRobot>(testPassword)
|
||||
.skip()
|
||||
.skipConfirm<SelectPlanRobot>()
|
||||
.toggleExpandPlan(TestPlan.Plus)
|
||||
.selectPlan<GoogleIAPRobot>(TestPlan.Plus)
|
||||
.payWithGPay<SignupFinishedRobot>()
|
||||
.verify {
|
||||
signupFinishedDisplayed()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun switchPaymentOptions() {
|
||||
billingClient.mockBillingClientSuccess { billingClientFactory.listeners }
|
||||
|
||||
val intent = StartPlanChooser().createIntent(appContext, PlanInput())
|
||||
ActivityScenario.launch<UpgradeActivity>(intent)
|
||||
|
||||
SelectPlanRobot()
|
||||
.toggleExpandPlan(TestPlan.Plus)
|
||||
.selectPlan<GoogleIAPRobot>(TestPlan.Plus)
|
||||
.apply { verify { googleIAPElementsDisplayed() } }
|
||||
.switchPaymentProvider()
|
||||
.apply { verify { addCreditCardElementsDisplayed() } }
|
||||
.switchPaymentProvider()
|
||||
.apply { verify { googleIAPElementsDisplayed() } }
|
||||
.back<SelectPlanRobot>()
|
||||
.verify {
|
||||
planDetailsDisplayed(TestPlan.Plus)
|
||||
canSelectPlan(TestPlan.Plus)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun createFreeAccountWithoutPaymentProviders() {
|
||||
dispatcher.mockFromAssets(
|
||||
"GET", "/payments/v4/status/google",
|
||||
"GET/payments/v4/status/google-all-disabled.json"
|
||||
)
|
||||
dispatcher.mockFromAssets(
|
||||
"GET", "/users",
|
||||
"GET/users-with-keys-subscribed.json"
|
||||
)
|
||||
dispatcher.mockFromAssets(
|
||||
"GET", "/addresses",
|
||||
"GET/addresses-with-keys.json"
|
||||
)
|
||||
|
||||
ActivityScenario.launch(AddAccountActivity::class.java)
|
||||
AddAccountRobot()
|
||||
.createAccount()
|
||||
.setUsername(testUsername)
|
||||
.setAndConfirmPassword<RecoveryMethodsRobot>(testPassword)
|
||||
.skip()
|
||||
.skipConfirm<SignupFinishedRobot>()
|
||||
.verify {
|
||||
signupFinishedDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,11 @@ object ApplicationModule {
|
||||
@Singleton
|
||||
fun provideCoreExampleRepository(apiProvider: ApiProvider): CoreExampleRepository =
|
||||
CoreExampleRepository(apiProvider)
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object WorkManagerModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWorkManager(@ApplicationContext context: Context): WorkManager =
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2022 Proton Technologies AG
|
||||
~ This file is part of Proton AG and ProtonCore.
|
||||
~
|
||||
~ ProtonCore is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ ProtonCore is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application android:networkSecurityConfig="@xml/network_security_config">
|
||||
<provider
|
||||
android:name="androidx.startup.InitializationProvider"
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
tools:node="remove" />
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2022 Proton Technologies AG
|
||||
~ This file is part of Proton AG and ProtonCore.
|
||||
~
|
||||
~ ProtonCore is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ ProtonCore is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<network-security-config>
|
||||
<base-config cleartextTrafficPermitted="true" />
|
||||
</network-security-config>
|
||||
@@ -1,3 +1,7 @@
|
||||
public class hilt_aggregated_deps/_me_proton_core_paymentiap_dagger_CorePaymentIapBillingModule {
|
||||
public fun <init> ()V
|
||||
}
|
||||
|
||||
public class hilt_aggregated_deps/_me_proton_core_paymentiap_dagger_CorePaymentIapModule {
|
||||
public fun <init> ()V
|
||||
}
|
||||
@@ -9,9 +13,12 @@ public final class me/proton/core/paymentiap/dagger/BuildConfig {
|
||||
public fun <init> ()V
|
||||
}
|
||||
|
||||
public abstract interface class me/proton/core/paymentiap/dagger/CorePaymentIapBillingModule {
|
||||
public abstract fun bindBillingClientProvider (Lme/proton/core/paymentiap/data/BillingClientFactoryImpl;)Lme/proton/core/paymentiap/domain/BillingClientFactory;
|
||||
}
|
||||
|
||||
public abstract interface class me/proton/core/paymentiap/dagger/CorePaymentIapModule {
|
||||
public abstract fun bindAcknowledgeGooglePlayPurchase (Lme/proton/core/paymentiap/data/usecase/AcknowledgeGooglePlayPurchaseImpl;)Lme/proton/core/payment/domain/usecase/AcknowledgeGooglePlayPurchase;
|
||||
public abstract fun bindBillingClientProvider (Lme/proton/core/paymentiap/data/BillingClientFactoryImpl;)Lme/proton/core/paymentiap/domain/BillingClientFactory;
|
||||
public abstract fun bindFindUnredeemedGooglePurchase (Lme/proton/core/paymentiap/data/usecase/FindUnacknowledgedGooglePurchaseImpl;)Lme/proton/core/payment/domain/usecase/FindUnacknowledgedGooglePurchase;
|
||||
public abstract fun bindGoogleBillingRepository (Lme/proton/core/paymentiap/data/repository/GoogleBillingRepositoryImpl;)Lme/proton/core/paymentiap/domain/repository/GoogleBillingRepository;
|
||||
}
|
||||
|
||||
+5
-1
@@ -33,10 +33,14 @@ import me.proton.core.paymentiap.domain.repository.GoogleBillingRepository
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
public interface CorePaymentIapModule {
|
||||
public interface CorePaymentIapBillingModule {
|
||||
@Binds
|
||||
public fun bindBillingClientProvider(impl: BillingClientFactoryImpl): BillingClientFactory
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
public interface CorePaymentIapModule {
|
||||
@Binds
|
||||
public fun bindGoogleBillingRepository(impl: GoogleBillingRepositoryImpl): GoogleBillingRepository
|
||||
|
||||
|
||||
+5
-4
@@ -52,7 +52,6 @@ import me.proton.core.util.kotlin.DispatcherProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
public class GoogleBillingRepositoryImpl @Inject internal constructor(
|
||||
billingClientFactory: BillingClientFactory,
|
||||
connectedBillingClientFactory: ConnectedBillingClientFactory,
|
||||
dispatcherProvider: DispatcherProvider,
|
||||
) : GoogleBillingRepository {
|
||||
@@ -67,7 +66,7 @@ public class GoogleBillingRepositoryImpl @Inject internal constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private val connectedBillingClient = connectedBillingClientFactory(billingClientFactory(purchasesUpdatedListener))
|
||||
private val connectedBillingClient = connectedBillingClientFactory(purchasesUpdatedListener)
|
||||
|
||||
override suspend fun acknowledgePurchase(purchaseToken: GooglePurchaseToken) {
|
||||
val params = AcknowledgePurchaseParams.newBuilder()
|
||||
@@ -120,13 +119,15 @@ public class GoogleBillingRepositoryImpl @Inject internal constructor(
|
||||
|
||||
@AssistedFactory
|
||||
internal interface ConnectedBillingClientFactory {
|
||||
operator fun invoke(billingClient: BillingClient): ConnectedBillingClient
|
||||
operator fun invoke(purchasesUpdatedListener: PurchasesUpdatedListener): ConnectedBillingClient
|
||||
}
|
||||
|
||||
/** Manages access to [BillingClient], ensuring we are connected, before calling any of its methods. */
|
||||
internal class ConnectedBillingClient @AssistedInject constructor(
|
||||
@Assisted private val billingClient: BillingClient
|
||||
billingClientFactory: BillingClientFactory,
|
||||
@Assisted private val purchasesUpdatedListener: PurchasesUpdatedListener
|
||||
) : BillingClientStateListener {
|
||||
private val billingClient = billingClientFactory(purchasesUpdatedListener)
|
||||
private val connectionState = MutableStateFlow<BillingClientConnectionState>(BillingClientConnectionState.Idle)
|
||||
|
||||
fun destroy() {
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ internal class ConnectedBillingClientTest {
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
billingClient = mockk(relaxed = true)
|
||||
tested = ConnectedBillingClient(billingClient)
|
||||
tested = ConnectedBillingClient({ billingClient }, mockk())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-2
@@ -48,7 +48,7 @@ internal class GoogleBillingRepositoryImplTest {
|
||||
fun setUp() {
|
||||
billingClientFactory = FakeBillingClientFactory()
|
||||
factory = FakeConnectedBillingClientFactory()
|
||||
tested = GoogleBillingRepositoryImpl(billingClientFactory, factory, TestDispatcherProvider)
|
||||
tested = GoogleBillingRepositoryImpl(factory, TestDispatcherProvider)
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
@@ -125,5 +125,6 @@ private class FakeBillingClientFactory : BillingClientFactory {
|
||||
private class FakeConnectedBillingClientFactory : ConnectedBillingClientFactory {
|
||||
val connectedBillingClient: ConnectedBillingClient = mockk(relaxed = true)
|
||||
|
||||
override fun invoke(billingClient: BillingClient): ConnectedBillingClient = connectedBillingClient
|
||||
override fun invoke(purchasesUpdatedListener: PurchasesUpdatedListener): ConnectedBillingClient =
|
||||
connectedBillingClient
|
||||
}
|
||||
|
||||
+1
-1
@@ -21,6 +21,6 @@ package me.proton.core.paymentiap.domain
|
||||
import com.android.billingclient.api.BillingClient
|
||||
import com.android.billingclient.api.PurchasesUpdatedListener
|
||||
|
||||
public interface BillingClientFactory {
|
||||
public fun interface BillingClientFactory {
|
||||
public operator fun invoke(purchasesUpdatedListener: PurchasesUpdatedListener): BillingClient
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public val DependencyHandler.`compose-material`: Any
|
||||
public val DependencyHandler.`compose-material3`: Any
|
||||
get() = compose("material3", version = `material3 version`)
|
||||
public val DependencyHandler.`compose-material-icons-core`: Any
|
||||
get() = compose("material", moduleSuffix="icons-core")
|
||||
get() = compose("material", moduleSuffix = "icons-core")
|
||||
public val DependencyHandler.`compose-runtime`: Any
|
||||
get() = compose("runtime")
|
||||
public val DependencyHandler.`compose-ui`: Any
|
||||
@@ -151,7 +151,7 @@ public val DependencyHandler.`startup-runtime`: Any
|
||||
public val DependencyHandler.`serialization-core`: Any
|
||||
get() = serialization("core")
|
||||
public val DependencyHandler.`desugar-jdk-libs`: Any
|
||||
get() = dependency("com.android.tools", module="desugar_jdk_libs") version `desugar_jdk_libs version`
|
||||
get() = dependency("com.android.tools", module = "desugar_jdk_libs") version `desugar_jdk_libs version`
|
||||
|
||||
// region accessors
|
||||
public fun DependencyHandler.lint(moduleSuffix: String? = null, version: String = `android-tools version`): Any =
|
||||
@@ -163,6 +163,8 @@ public val DependencyHandler.`android-test-core-ktx`: Any
|
||||
get() = dependency("androidx.test", module = "core-ktx") version `android-test version`
|
||||
public val DependencyHandler.`androidx-test-monitor`: Any
|
||||
get() = androidx("test", module = "monitor") version `androidx-test-monitor version`
|
||||
public val DependencyHandler.`androidx-test-orchestrator`: Any
|
||||
get() = androidx("test", module = "orchestrator") version `androidx-test-orchestrator version`
|
||||
public val DependencyHandler.`espresso-contrib`: Any
|
||||
get() = androidx("test.espresso", module = "espresso-contrib") version `espresso version`
|
||||
public val DependencyHandler.`espresso-idling-resource`: Any
|
||||
|
||||
@@ -116,12 +116,13 @@ public const val `desugar_jdk_libs version`: String = "1.1.5"
|
||||
// endregion
|
||||
|
||||
// region Tests
|
||||
public const val `androidx-test-monitor version`: String = "1.5.0"
|
||||
public const val `hamcrest version`: String = "2.2"
|
||||
public const val `uiautomator version`: String = "2.2.0" // Released: Oct 25, 2018
|
||||
public const val `preference version`: String = "1.1.1" // Released: Apr 15, 2020
|
||||
public const val `json-simple version`: String = "1.1.1" // Released: Mar 21, 2012
|
||||
public const val `turbine version`: String = "0.7.0"
|
||||
public const val `junit version`: String = "4.13.2"
|
||||
public const val `junit-ktx version`: String = "1.1.3"
|
||||
public const val `androidx-test-monitor version`: String = "1.5.0"
|
||||
public const val `androidx-test-orchestrator version`: String = "1.4.1"
|
||||
public const val `hamcrest version`: String = "2.2"
|
||||
public const val `uiautomator version`: String = "2.2.0" // Released: Oct 25, 2018
|
||||
public const val `preference version`: String = "1.1.1" // Released: Apr 15, 2020
|
||||
public const val `json-simple version`: String = "1.1.1" // Released: Mar 21, 2012
|
||||
public const val `turbine version`: String = "0.7.0"
|
||||
public const val `junit version`: String = "4.13.2"
|
||||
public const val `junit-ktx version`: String = "1.1.3"
|
||||
// endregion
|
||||
|
||||
@@ -1055,6 +1055,14 @@ public final class me/proton/core/test/android/robots/payments/ExistingPaymentMe
|
||||
|
||||
public final class me/proton/core/test/android/robots/payments/GoogleIAPRobot : me/proton/core/test/android/robots/payments/PaymentRobot {
|
||||
public fun <init> ()V
|
||||
public final fun switchPaymentProvider ()Lme/proton/core/test/android/robots/payments/GoogleIAPRobot;
|
||||
public final fun verify (Lkotlin/jvm/functions/Function1;)Lme/proton/core/test/android/robots/payments/GoogleIAPRobot$Verify;
|
||||
}
|
||||
|
||||
public final class me/proton/core/test/android/robots/payments/GoogleIAPRobot$Verify : me/proton/core/test/android/robots/payments/PaymentRobot$Verify {
|
||||
public fun <init> ()V
|
||||
public final fun payWithCardButtonIsNotVisible ()V
|
||||
public final fun payWithGoogleButtonIsClickable ()V
|
||||
}
|
||||
|
||||
public class me/proton/core/test/android/robots/payments/PaymentRobot : me/proton/core/test/android/robots/CoreRobot {
|
||||
@@ -1062,12 +1070,13 @@ public class me/proton/core/test/android/robots/payments/PaymentRobot : me/proto
|
||||
public final fun verify (Lkotlin/jvm/functions/Function1;)Lme/proton/core/test/android/robots/payments/PaymentRobot$Verify;
|
||||
}
|
||||
|
||||
public final class me/proton/core/test/android/robots/payments/PaymentRobot$Verify : me/proton/core/test/android/robots/CoreVerify {
|
||||
public class me/proton/core/test/android/robots/payments/PaymentRobot$Verify : me/proton/core/test/android/robots/CoreVerify {
|
||||
public fun <init> ()V
|
||||
public final fun addCreditCardElementsDisplayed ()V
|
||||
public final fun billingDetailsDisplayed (Lme/proton/core/test/android/plugins/data/Plan;Ljava/lang/String;Z)V
|
||||
public final fun googleIAPElementsDisplayed ()V
|
||||
public final fun googlePaymentMethodDisplayed (Ljava/lang/String;)V
|
||||
public final fun nextPaymentProviderButtonDisplayed ()V
|
||||
public final fun paymentMethodDisplayed (Ljava/lang/String;Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
@@ -1086,6 +1095,7 @@ public final class me/proton/core/test/android/robots/plans/SelectPlanRobot$Veri
|
||||
public static synthetic fun billingCycleIs$default (Lme/proton/core/test/android/robots/plans/SelectPlanRobot$Verify;Lme/proton/core/test/android/plugins/data/Plan;Lme/proton/core/test/android/plugins/data/BillingCycle;Lme/proton/core/test/android/plugins/data/Currency;ILjava/lang/Object;)V
|
||||
public final fun canSelectPlan (Lme/proton/core/test/android/plugins/data/Plan;)V
|
||||
public final fun canUpgradeToPlan (Lme/proton/core/test/android/plugins/data/Plan;)V
|
||||
public final fun currentPlanDetailsDisplayed ()V
|
||||
public final fun planCycleDisplayed (Ljava/lang/String;)V
|
||||
public final fun planDetailsDisplayed (Lme/proton/core/test/android/plugins/data/Plan;)V
|
||||
public final fun planDetailsDisplayedInsideRecyclerView (Lme/proton/core/test/android/plugins/data/Plan;)V
|
||||
|
||||
+21
-1
@@ -18,10 +18,30 @@
|
||||
|
||||
package me.proton.core.test.android.robots.payments
|
||||
|
||||
import me.proton.core.payment.presentation.R
|
||||
|
||||
/**
|
||||
* [GoogleIAPRobot] class contains new credit card addition actions and verifications implementation
|
||||
*/
|
||||
class GoogleIAPRobot : PaymentRobot() {
|
||||
|
||||
inline fun <reified T> payWithGPay() = pay<T>()
|
||||
inline fun <reified T> payWithGPay(): T = clickElement(R.id.gPayButton)
|
||||
|
||||
fun switchPaymentProvider(): GoogleIAPRobot = clickElement(R.id.nextPaymentProviderButton)
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND")
|
||||
fun <V : Verify> verify(block: Verify.() -> Unit) = Verify().apply(block)
|
||||
|
||||
class Verify : PaymentRobot.Verify() {
|
||||
fun payWithCardButtonIsNotVisible() {
|
||||
view.withId(R.id.payButton).checkNotDisplayed()
|
||||
}
|
||||
|
||||
fun payWithGoogleButtonIsClickable() {
|
||||
view.withId(R.id.gPayButton)
|
||||
.checkDisplayed()
|
||||
.isEnabled()
|
||||
.isClickable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -35,7 +35,7 @@ open class PaymentRobot : CoreRobot() {
|
||||
*/
|
||||
inline fun <reified T> pay(): T = clickElement(R.id.payButton)
|
||||
|
||||
class Verify : CoreVerify() {
|
||||
open class Verify : CoreVerify() {
|
||||
fun billingDetailsDisplayed(
|
||||
plan: Plan,
|
||||
currency: String,
|
||||
@@ -75,12 +75,15 @@ open class PaymentRobot : CoreRobot() {
|
||||
fun googleIAPElementsDisplayed() {
|
||||
arrayOf(
|
||||
me.proton.core.paymentiap.presentation.R.id.termsConditionsInfoText,
|
||||
me.proton.core.paymentiap.presentation.R.id.priceSurchargeInfoText,
|
||||
R.id.nextPaymentProviderButton
|
||||
me.proton.core.paymentiap.presentation.R.id.priceSurchargeInfoText
|
||||
).forEach {
|
||||
view.withId(it).checkDisplayed()
|
||||
}
|
||||
}
|
||||
|
||||
fun nextPaymentProviderButtonDisplayed() {
|
||||
view.withId(R.id.nextPaymentProviderButton).checkDisplayed()
|
||||
}
|
||||
}
|
||||
|
||||
inline fun verify(block: Verify.() -> Unit) = Verify().apply(block)
|
||||
|
||||
+4
@@ -132,6 +132,10 @@ class SelectPlanRobot : CoreRobot() {
|
||||
view.withId(R.id.currentPlan).checkNotDisplayed()
|
||||
}
|
||||
|
||||
fun currentPlanDetailsDisplayed() {
|
||||
view.withId(R.id.currentPlan).checkDisplayed()
|
||||
}
|
||||
|
||||
fun canSelectPlan(plan: Plan) {
|
||||
scrollToPlan(plan)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user