Skip to content

Commit 6376984

Browse files
Test quota
Signed-off-by: tobiasKaminsky <[email protected]>
1 parent c870630 commit 6376984

File tree

5 files changed

+186
-3
lines changed

5 files changed

+186
-3
lines changed

.drone.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ services:
175175
- su www-data -c "OC_PASS=test php /var/www/html/occ user:add --password-from-env --display-name='Test@Test' test@test"
176176
- su www-data -c "OC_PASS=test php /var/www/html/occ user:add --password-from-env --display-name='Test Spaces' 'test test'"
177177
- su www-data -c "php /var/www/html/occ user:setting user2 files quota 1G"
178+
- su www-data -c "php /var/www/html/occ user:setting user3 files quota 1M"
178179
- su www-data -c "php /var/www/html/occ group:add users"
179180
- su www-data -c "php /var/www/html/occ group:adduser users user1"
180181
- su www-data -c "php /var/www/html/occ group:adduser users user2"

.idea/codeStyles/Project.xml

Lines changed: 1 addition & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Nextcloud Android client application
3+
*
4+
* @author Tobias Kaminsky
5+
* Copyright (C) 2023 Tobias Kaminsky
6+
* Copyright (C) 2023 Nextcloud GmbH
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as published by
10+
* the Free Software Foundation, either version 3 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License
19+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
23+
package com.owncloud.android.lib.resources.files
24+
25+
import com.owncloud.android.AbstractIT
26+
import com.owncloud.android.lib.common.OwnCloudBasicCredentials
27+
import com.owncloud.android.lib.common.OwnCloudClientFactory
28+
import org.junit.Assert.assertFalse
29+
import org.junit.Assert.assertTrue
30+
import org.junit.Test
31+
32+
class CheckEnoughQuotaRemoteOperationIT : AbstractIT() {
33+
@Test
34+
fun enoughQuota() {
35+
val sut = CheckEnoughQuotaRemoteOperation("/", LARGE_FILE).execute(client)
36+
assertTrue(sut.isSuccess)
37+
}
38+
39+
@Test
40+
fun noQuota() {
41+
// user3 has only 1M quota
42+
val client3 = OwnCloudClientFactory.createOwnCloudClient(url, context, true)
43+
client3.credentials = OwnCloudBasicCredentials("user3", "user3")
44+
val sut = CheckEnoughQuotaRemoteOperation("/", LARGE_FILE).execute(client3)
45+
assertFalse(sut.isSuccess)
46+
}
47+
48+
companion object {
49+
const val LARGE_FILE = 5 * 1024 * 1024L
50+
}
51+
}

library/src/androidTest/java/com/owncloud/android/lib/resources/files/UploadFileRemoteOperationIT.kt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@ package com.owncloud.android.lib.resources.files
2323

2424
import android.os.Build
2525
import com.owncloud.android.AbstractIT
26+
import com.owncloud.android.lib.common.OwnCloudClientFactory
27+
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
2628
import com.owncloud.android.lib.common.utils.Log_OC
2729
import com.owncloud.android.lib.resources.files.model.RemoteFile
2830
import junit.framework.TestCase.assertEquals
31+
import org.junit.Assert.assertFalse
2932
import org.junit.Assert.assertNotNull
3033
import org.junit.Assert.assertTrue
3134
import org.junit.Test
@@ -94,6 +97,33 @@ class UploadFileRemoteOperationIT : AbstractIT() {
9497
)
9598
}
9699

100+
@Throws(Throwable::class)
101+
@Test
102+
fun uploadFileWithQuotaExceeded() {
103+
// user3 has quota of 1Mb
104+
val client3 = OwnCloudClientFactory.createOwnCloudClient(url, context, true)
105+
client3.credentials = OwnCloudBasicCredentials("user3", "user3")
106+
107+
// create file
108+
val filePath = createFile("quota", LARGE_FILE)
109+
val remotePath = "/quota.md"
110+
111+
val creationTimestamp = getCreationTimestamp(File(filePath))
112+
val sut = UploadFileRemoteOperation(
113+
filePath,
114+
remotePath,
115+
"text/markdown",
116+
"",
117+
RANDOM_MTIME,
118+
creationTimestamp,
119+
true
120+
)
121+
122+
val uploadResult = sut.execute(client3)
123+
assertFalse(uploadResult.isSuccess)
124+
assertEquals(ResultCode.QUOTA_EXCEEDED, uploadResult.code)
125+
}
126+
97127
private fun getCreationTimestamp(file: File): Long? {
98128
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
99129
return null
@@ -114,5 +144,6 @@ class UploadFileRemoteOperationIT : AbstractIT() {
114144

115145
companion object {
116146
const val TIME_OFFSET = 10
147+
const val LARGE_FILE = 5 * 1024 * 1024L
117148
}
118149
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/* Nextcloud Android Library is available under MIT license
2+
*
3+
* @author Tobias Kaminsky
4+
* Copyright (C) 2023 Tobias Kaminsky
5+
* Copyright (C) 2023 Nextcloud GmbH
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21+
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22+
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*
26+
*/
27+
package com.owncloud.android.lib.resources.files
28+
29+
import com.owncloud.android.lib.common.OwnCloudClient
30+
import com.owncloud.android.lib.common.network.WebdavEntry
31+
import com.owncloud.android.lib.common.operations.RemoteOperation
32+
import com.owncloud.android.lib.common.operations.RemoteOperationResult
33+
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
34+
import com.owncloud.android.lib.common.utils.Log_OC
35+
import org.apache.commons.httpclient.HttpStatus
36+
import org.apache.jackrabbit.webdav.DavException
37+
import org.apache.jackrabbit.webdav.client.methods.PropFindMethod
38+
import org.apache.jackrabbit.webdav.property.DavPropertyName
39+
import org.apache.jackrabbit.webdav.property.DavPropertyNameSet
40+
import java.io.IOException
41+
42+
/**
43+
* Check if remaining quota is big enough
44+
* @param fileSize filesize in bytes
45+
*/
46+
class CheckEnoughQuotaRemoteOperation(val path: String, private val fileSize: Long) :
47+
RemoteOperation<Boolean>() {
48+
49+
@Suppress("Detekt.ReturnCount")
50+
override fun run(client: OwnCloudClient): RemoteOperationResult<Boolean> {
51+
var propfind: PropFindMethod? = null
52+
try {
53+
val propSet = DavPropertyNameSet()
54+
propSet.add(QUOTA_PROPERTY)
55+
propfind = PropFindMethod(
56+
client.getFilesDavUri(path),
57+
propSet,
58+
0
59+
)
60+
val status = client.executeMethod(propfind, SYNC_READ_TIMEOUT, SYNC_CONNECTION_TIMEOUT)
61+
if (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK) {
62+
val resp = propfind.responseBodyAsMultiStatus.responses[0]
63+
val string = resp.getProperties(HttpStatus.SC_OK)[QUOTA_PROPERTY].value as String
64+
val quota = string.toLong()
65+
return if (isSuccess(quota)) {
66+
RemoteOperationResult<Boolean>(true, propfind)
67+
} else {
68+
RemoteOperationResult<Boolean>(false, propfind)
69+
}
70+
}
71+
if (status == HttpStatus.SC_NOT_FOUND) {
72+
return RemoteOperationResult(ResultCode.FILE_NOT_FOUND)
73+
}
74+
} catch (e: DavException) {
75+
Log_OC.e(TAG, "Error while retrieving quota")
76+
} catch (e: IOException) {
77+
Log_OC.e(TAG, "Error while retrieving quota")
78+
} catch (e: NumberFormatException) {
79+
Log_OC.e(TAG, "Error while retrieving quota")
80+
} finally {
81+
propfind?.releaseConnection()
82+
}
83+
return RemoteOperationResult(ResultCode.ETAG_CHANGED)
84+
}
85+
86+
private fun isSuccess(quota: Long) : Boolean {
87+
retunr quota >= fileSize ||
88+
quota == UNKNOWN_FREE_SPACE ||
89+
quota == UNCOMPUTED_FREE_SPACE ||
90+
quota == UNLIMITED_FREE_SPACE
91+
}
92+
93+
companion object {
94+
private const val SYNC_READ_TIMEOUT = 40000
95+
private const val SYNC_CONNECTION_TIMEOUT = 5000
96+
private const val UNCOMPUTED_FREE_SPACE = -1L
97+
private const val UNKNOWN_FREE_SPACE = -2L
98+
private const val UNLIMITED_FREE_SPACE = -3L
99+
private val QUOTA_PROPERTY = DavPropertyName.create(WebdavEntry.PROPERTY_QUOTA_AVAILABLE_BYTES)
100+
private val TAG = CheckEnoughQuotaRemoteOperation::class.java.simpleName
101+
}
102+
}

0 commit comments

Comments
 (0)