test(api): add API test coverage on files, folders, favorites, recent, trash
This commit is contained in:
@@ -39,6 +39,10 @@ jobs:
|
||||
- 'src/**'
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
- 'tests/api/**'
|
||||
- 'tests/e2e/**'
|
||||
- 'tests/fixtures/**'
|
||||
- 'tests/common/**'
|
||||
|
||||
frontend-check:
|
||||
name: Frontend — CSS and JS checks (format, lint, rules)
|
||||
|
||||
+87
-1
@@ -44,9 +44,95 @@ hurl --variables-file tests/api/test.env --test --report-html /tmp/hurl-report t
|
||||
| File | Description |
|
||||
|---|---|
|
||||
| `setup.hurl` | One-time admin account creation; also asserts the endpoint is locked afterwards |
|
||||
| `contacts.hurl` | Full contacts CRUD scenario (13 steps, see below) |
|
||||
| `files-folders.hurl` | Files & folders CRUD scenario (22 steps, see below) |
|
||||
| `favorites.hurl` | Favorites add/list/remove scenario (11 steps); depends on `files-folders.hurl` state |
|
||||
| `trash.hurl` | Trash move/restore/purge scenario (16 steps); depends on `files-folders.hurl` state |
|
||||
| `recent.hurl` | Recent items record/list/clear scenario (6 steps); depends on `files-folders.hurl` state |
|
||||
| `contacts.hurl` | Full contacts CRUD scenario (14 steps, see below) |
|
||||
| `test.env` | Variables: `base_url`, `username`, `email`, `password` — used by both Hurl and `run.sh` |
|
||||
|
||||
## Scenario: `files-folders.hurl`
|
||||
|
||||
| Step | Description |
|
||||
|---|---|
|
||||
| 1 | Login – capture JWT token |
|
||||
| 2 | List root folders – assert exactly 1 (home folder), capture `home_folder_id`, assert `parent_id` is null |
|
||||
| 3 | Browse home folder sub-folders – assert empty |
|
||||
| 4 | Browse home folder files – assert empty |
|
||||
| 5 | Create folder named `"/"` – assert HTTP 400, `error_type == "Invalid Input"` |
|
||||
| 6 | Create `test1` inside home folder – capture `test1_id`, assert `parent_id` == `home_folder_id` |
|
||||
| 7 | Create `test2` inside `test1` – capture `test2_id`, assert `parent_id` == `test1_id` |
|
||||
| 8 | Browse home folder – assert exactly 1 sub-folder (`test1`) |
|
||||
| 9 | Browse `test1` – assert exactly 1 sub-folder (`test2`), assert `parent_id` |
|
||||
| 10 | Upload `fixtures/hello.txt` into `test2` – capture `file_id`, assert name/size/mime_type/folder_id |
|
||||
| 11 | List files in `test2` – assert count=1, `mime_type == text/plain`, `icon_class == fas fa-file-alt` |
|
||||
| 12 | Move `test2` from `test1` into home folder – assert `parent_id` == `home_folder_id` |
|
||||
| 13 | Browse `test1` – assert empty (no more children) |
|
||||
| 14 | Browse home folder – assert 2 sub-folders (`test1` and `test2`) |
|
||||
| 15 | Rename `test2` → `test2-renamed` – assert new name, `parent_id` unchanged |
|
||||
| 16 | Rename `hello.txt` → `hello-renamed.txt` – assert new name, `folder_id` unchanged |
|
||||
| 17 | Rename `hello-renamed.txt` to `"."` – assert HTTP 400, `error_type == "Invalid Input"` |
|
||||
| 18 | Upload `fixtures/oxicloud-logo.jpg` into home folder – capture `logo_id`, assert `mime_type == image/jpeg` |
|
||||
| 19 | List files in home folder – assert count=1, `icon_class == fas fa-file-image` |
|
||||
| 20 | `GET /api/files/{logo_id}/thumbnail/icon` → HTTP 200 |
|
||||
| 21 | `GET /api/files/{logo_id}/thumbnail/preview` → HTTP 200 |
|
||||
| 22 | `GET /api/files/{logo_id}/thumbnail/large` → HTTP 200 |
|
||||
|
||||
## Scenario: `favorites.hurl`
|
||||
|
||||
Depends on `files-folders.hurl` having run first (test1, test2-renamed/hello-renamed.txt must exist).
|
||||
|
||||
| Step | Description |
|
||||
|---|---|
|
||||
| 1 | Login – capture JWT token |
|
||||
| 2 | Assert no favorites yet |
|
||||
| 3 | Discover item IDs: home folder → contents (test1=$[0], test2-renamed=$[1]) → files in test2-renamed |
|
||||
| 4 | `POST /api/favorites/file/{file_id}` – add hello-renamed.txt → HTTP 201 |
|
||||
| 5 | List favorites – assert count=1, item_type=file, item_name=hello-renamed.txt |
|
||||
| 6 | `POST /api/favorites/folder/{test1_id}` – add test1 → HTTP 201 |
|
||||
| 7 | List favorites – assert count=2, both IDs present (order-independent) |
|
||||
| 8 | `DELETE /api/favorites/file/{file_id}` – remove hello-renamed.txt → HTTP 200 |
|
||||
| 9 | List favorites – assert count=1, item_type=folder, item_name=test1 |
|
||||
| 10 | Cleanup: `DELETE /api/favorites/folder/{test1_id}` → HTTP 200 |
|
||||
| 11 | List favorites – assert count=0 |
|
||||
|
||||
## Scenario: `trash.hurl`
|
||||
|
||||
Depends on `files-folders.hurl` having run first (home folder must exist).
|
||||
|
||||
| Step | Description |
|
||||
|---|---|
|
||||
| 1 | Login – capture JWT token |
|
||||
| 2 | Assert trash is empty |
|
||||
| 3 | Capture home folder ID |
|
||||
| 4 | Create `to-delete` folder in home folder – capture `to_delete_id` |
|
||||
| 5 | Upload `hello.txt` into `to-delete` |
|
||||
| 6 | `DELETE /api/folders/{to_delete_id}` – moves folder to trash → HTTP 204 |
|
||||
| 7 | List trash – assert count=1, item_type=folder, name=to-delete, original_id matches; capture `trash_id` |
|
||||
| 8 | List home folder contents – assert `to-delete` is not present |
|
||||
| 9 | `POST /api/trash/{trash_id}/restore` → HTTP 200, `success == true` |
|
||||
| 10 | List home folder contents – assert `to-delete` is present |
|
||||
| 11 | List files in `to-delete` – assert count=1, name=hello.txt |
|
||||
| 12 | List trash – assert empty (restore removed the entry) |
|
||||
| 13 | `DELETE /api/folders/{to_delete_id}` – move restored folder to trash again → HTTP 204 |
|
||||
| 14 | `DELETE /api/trash/empty` – purge trash → HTTP 200, `success == true` |
|
||||
| 15 | List trash – assert empty after purge |
|
||||
| 16 | List home folder contents – assert `to-delete` is not present (permanently gone) |
|
||||
|
||||
## Scenario: `recent.hurl`
|
||||
|
||||
Depends on `files-folders.hurl` having run first (test2-renamed/hello-renamed.txt must exist).
|
||||
Recent items are not auto-recorded on upload — step 3 explicitly registers the access.
|
||||
|
||||
| Step | Description |
|
||||
|---|---|
|
||||
| 1 | Login – capture JWT token |
|
||||
| 2 | Discover `file_id` of hello-renamed.txt via home folder → test2-renamed contents |
|
||||
| 3 | `POST /api/recent/file/{file_id}` – record access → HTTP 200 |
|
||||
| 4 | `GET /api/recent` – assert count=1, item_type=file, item_name=hello-renamed.txt |
|
||||
| 5 | `DELETE /api/recent/clear` – clear all recent items → HTTP 200 |
|
||||
| 6 | `GET /api/recent` – assert count=0 |
|
||||
|
||||
## Scenario: `contacts.hurl`
|
||||
|
||||
| Step | Description |
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
# =============================================================
|
||||
# OxiCloud – Favorites API end-to-end scenario
|
||||
# =============================================================
|
||||
# Depends on files-folders.hurl having run first:
|
||||
# - home folder exists with sub-folders test1 and test2-renamed
|
||||
# - test2-renamed contains hello-renamed.txt
|
||||
#
|
||||
# Run:
|
||||
# hurl --variables-file tests/api/test.env --test tests/api/favorites.hurl
|
||||
# =============================================================
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 1 – Login and capture the JWT token
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/auth/login
|
||||
Content-Type: application/json
|
||||
{
|
||||
"username": "{{username}}",
|
||||
"password": "{{password}}"
|
||||
}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
token: jsonpath "$.access_token"
|
||||
[Asserts]
|
||||
jsonpath "$.access_token" isString
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 2 – No favorites yet
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/favorites
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" isCollection
|
||||
jsonpath "$" count == 0
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 3 – Discover item IDs from the folder structure
|
||||
# Folders are ORDER BY name: test1 ($[0]), test2-renamed ($[1])
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/folders
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
home_folder_id: jsonpath "$[0].id"
|
||||
|
||||
|
||||
GET {{base_url}}/api/folders/{{home_folder_id}}/contents
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
test1_id: jsonpath "$[0].id"
|
||||
test2_id: jsonpath "$[1].id"
|
||||
[Asserts]
|
||||
jsonpath "$[0].name" == "test1"
|
||||
jsonpath "$[1].name" == "test2-renamed"
|
||||
|
||||
|
||||
GET {{base_url}}/api/files?folder_id={{test2_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
file_id: jsonpath "$[0].id"
|
||||
[Asserts]
|
||||
jsonpath "$[0].name" == "hello-renamed.txt"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 4 – Add hello-renamed.txt to favorites
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/favorites/file/{{file_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 201
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 5 – Favorites contains only hello-renamed.txt
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/favorites
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" count == 1
|
||||
jsonpath "$[0].item_id" == {{file_id}}
|
||||
jsonpath "$[0].item_type" == "file"
|
||||
jsonpath "$[0].item_name" == "hello-renamed.txt"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 6 – Add test1 folder to favorites
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/favorites/folder/{{test1_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 201
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 7 – Favorites contains both items (order-independent)
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/favorites
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" count == 2
|
||||
jsonpath "$[*].item_id" contains {{file_id}}
|
||||
jsonpath "$[*].item_id" contains {{test1_id}}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 8 – Remove hello-renamed.txt from favorites
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
DELETE {{base_url}}/api/favorites/file/{{file_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 9 – Favorites contains only test1 folder
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/favorites
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" count == 1
|
||||
jsonpath "$[0].item_id" == {{test1_id}}
|
||||
jsonpath "$[0].item_type" == "folder"
|
||||
jsonpath "$[0].item_name" == "test1"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 10 – Cleanup: remove test1 from favorites
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
DELETE {{base_url}}/api/favorites/folder/{{test1_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 11 – Favorites is empty again
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/favorites
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" count == 0
|
||||
@@ -0,0 +1,344 @@
|
||||
# =============================================================
|
||||
# OxiCloud – Files & Folders API end-to-end scenario
|
||||
# =============================================================
|
||||
# Run:
|
||||
# hurl --variables-file tests/api/test.env --test tests/api/files-folders.hurl
|
||||
# =============================================================
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 1 – Login and capture the JWT token
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/auth/login
|
||||
Content-Type: application/json
|
||||
{
|
||||
"username": "{{username}}",
|
||||
"password": "{{password}}"
|
||||
}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
token: jsonpath "$.access_token"
|
||||
[Asserts]
|
||||
jsonpath "$.access_token" isString
|
||||
jsonpath "$.token_type" == "Bearer"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 2 – List root folders
|
||||
# A fresh admin account has exactly one root folder
|
||||
# (the home folder created at setup time).
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/folders
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
home_folder_id: jsonpath "$[0].id"
|
||||
home_folder_name: jsonpath "$[0].name"
|
||||
[Asserts]
|
||||
jsonpath "$" isCollection
|
||||
jsonpath "$" count == 1
|
||||
jsonpath "$[0].id" isString
|
||||
jsonpath "$[0].name" isString
|
||||
jsonpath "$[0].parent_id" == null
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 3 – Browse the home folder: sub-folders must be empty
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/folders/{{home_folder_id}}/contents
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" isCollection
|
||||
jsonpath "$" count == 0
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 4 – Browse the home folder: files must be empty
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/files?folder_id={{home_folder_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" isCollection
|
||||
jsonpath "$" count == 0
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 5 – Creating a folder named "/" must be rejected (400)
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/folders
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"name": "/",
|
||||
"parent_id": "{{home_folder_id}}"
|
||||
}
|
||||
|
||||
HTTP 400
|
||||
[Asserts]
|
||||
jsonpath "$.error_type" == "Invalid Input"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 6 – Create folder test1 inside the home folder
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/folders
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"name": "test1",
|
||||
"parent_id": "{{home_folder_id}}"
|
||||
}
|
||||
|
||||
HTTP 201
|
||||
[Captures]
|
||||
test1_id: jsonpath "$.id"
|
||||
[Asserts]
|
||||
jsonpath "$.id" isString
|
||||
jsonpath "$.name" == "test1"
|
||||
jsonpath "$.parent_id" == {{home_folder_id}}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 7 – Create folder test2 inside test1
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/folders
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"name": "test2",
|
||||
"parent_id": "{{test1_id}}"
|
||||
}
|
||||
|
||||
HTTP 201
|
||||
[Captures]
|
||||
test2_id: jsonpath "$.id"
|
||||
[Asserts]
|
||||
jsonpath "$.id" isString
|
||||
jsonpath "$.name" == "test2"
|
||||
jsonpath "$.parent_id" == {{test1_id}}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 8 – Home folder now has exactly one sub-folder (test1)
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/folders/{{home_folder_id}}/contents
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" count == 1
|
||||
jsonpath "$[0].id" == {{test1_id}}
|
||||
jsonpath "$[0].name" == "test1"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 9 – test1 has exactly one sub-folder (test2)
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/folders/{{test1_id}}/contents
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" count == 1
|
||||
jsonpath "$[0].id" == {{test2_id}}
|
||||
jsonpath "$[0].name" == "test2"
|
||||
jsonpath "$[0].parent_id" == {{test1_id}}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 10 – Upload hello.txt into test2
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/files/upload
|
||||
Authorization: Bearer {{token}}
|
||||
[MultipartFormData]
|
||||
folder_id: {{test2_id}}
|
||||
file: file,fixtures/hello.txt; text/plain
|
||||
|
||||
HTTP 201
|
||||
[Captures]
|
||||
file_id: jsonpath "$.id"
|
||||
[Asserts]
|
||||
jsonpath "$.id" isString
|
||||
jsonpath "$.name" == "hello.txt"
|
||||
jsonpath "$.folder_id" == {{test2_id}}
|
||||
jsonpath "$.size" == 21
|
||||
jsonpath "$.mime_type" == "text/plain"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 11 – test2 contains exactly hello.txt; verify mime and icon
|
||||
# icon_class is only available in the full FileDto
|
||||
# returned by the listing endpoint
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/files?folder_id={{test2_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" count == 1
|
||||
jsonpath "$[0].id" == {{file_id}}
|
||||
jsonpath "$[0].name" == "hello.txt"
|
||||
jsonpath "$[0].mime_type" == "text/plain"
|
||||
jsonpath "$[0].icon_class" == "fas fa-file-alt"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 12 – Move test2 from test1 into the home folder
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
PUT {{base_url}}/api/folders/{{test2_id}}/move
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"parent_id": "{{home_folder_id}}"
|
||||
}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.id" == {{test2_id}}
|
||||
jsonpath "$.name" == "test2"
|
||||
jsonpath "$.parent_id" == {{home_folder_id}}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 13 – test1 is now empty (test2 was its only child)
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/folders/{{test1_id}}/contents
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" count == 0
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 14 – Home folder now has two sub-folders: test1 and test2
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/folders/{{home_folder_id}}/contents
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" count == 2
|
||||
jsonpath "$[*].id" contains {{test1_id}}
|
||||
jsonpath "$[*].id" contains {{test2_id}}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 15 – Rename test2 to test2-renamed
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
PUT {{base_url}}/api/folders/{{test2_id}}/rename
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"name": "test2-renamed"
|
||||
}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.id" == {{test2_id}}
|
||||
jsonpath "$.name" == "test2-renamed"
|
||||
jsonpath "$.parent_id" == {{home_folder_id}}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 16 – Rename hello.txt to hello-renamed.txt
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
PUT {{base_url}}/api/files/{{file_id}}/rename
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"name": "hello-renamed.txt"
|
||||
}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.id" == {{file_id}}
|
||||
jsonpath "$.name" == "hello-renamed.txt"
|
||||
jsonpath "$.folder_id" == {{test2_id}}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 17 – Renaming a file to "." must be rejected (400)
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
PUT {{base_url}}/api/files/{{file_id}}/rename
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"name": "."
|
||||
}
|
||||
|
||||
HTTP 400
|
||||
[Asserts]
|
||||
jsonpath "$.error_type" == "Invalid Input"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 18 – Upload oxicloud-logo.jpg into the home folder
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/files/upload
|
||||
Authorization: Bearer {{token}}
|
||||
[MultipartFormData]
|
||||
folder_id: {{home_folder_id}}
|
||||
file: file,fixtures/oxicloud-logo.jpg; image/jpeg
|
||||
|
||||
HTTP 201
|
||||
[Captures]
|
||||
logo_id: jsonpath "$.id"
|
||||
[Asserts]
|
||||
jsonpath "$.name" == "oxicloud-logo.jpg"
|
||||
jsonpath "$.mime_type" == "image/jpeg"
|
||||
jsonpath "$.folder_id" == {{home_folder_id}}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 19 – Home folder file listing: verify icon class for JPEG
|
||||
# (icon_class is only present in the listing response)
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/files?folder_id={{home_folder_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" count == 1
|
||||
jsonpath "$[0].name" == "oxicloud-logo.jpg"
|
||||
jsonpath "$[0].mime_type" == "image/jpeg"
|
||||
jsonpath "$[0].icon_class" == "fas fa-file-image"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 20 – Thumbnail icon size exists (200 with image bytes)
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/files/{{logo_id}}/thumbnail/icon
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
header "Content-Type" startsWith "image/"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 21 – Thumbnail preview size exists
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/files/{{logo_id}}/thumbnail/preview
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
header "Content-Type" startsWith "image/"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 22 – Thumbnail large size exists
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/files/{{logo_id}}/thumbnail/large
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
header "Content-Type" startsWith "image/"
|
||||
@@ -0,0 +1,102 @@
|
||||
# =============================================================
|
||||
# OxiCloud – Recent items API end-to-end scenario
|
||||
# =============================================================
|
||||
# Depends on files-folders.hurl having run first:
|
||||
# - home folder exists with test1 and test2-renamed
|
||||
# - test2-renamed contains hello-renamed.txt
|
||||
#
|
||||
# Run:
|
||||
# hurl --variables-file tests/api/test.env --test tests/api/recent.hurl
|
||||
# =============================================================
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 1 – Login and capture the JWT token
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/auth/login
|
||||
Content-Type: application/json
|
||||
{
|
||||
"username": "{{username}}",
|
||||
"password": "{{password}}"
|
||||
}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
token: jsonpath "$.access_token"
|
||||
[Asserts]
|
||||
jsonpath "$.access_token" isString
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 2 – Discover the file ID of hello-renamed.txt
|
||||
# Folders are ORDER BY name: test1 ($[0]), test2-renamed ($[1])
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/folders
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
home_folder_id: jsonpath "$[0].id"
|
||||
|
||||
|
||||
GET {{base_url}}/api/folders/{{home_folder_id}}/contents
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
test2_id: jsonpath "$[1].id"
|
||||
[Asserts]
|
||||
jsonpath "$[1].name" == "test2-renamed"
|
||||
|
||||
|
||||
GET {{base_url}}/api/files?folder_id={{test2_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
file_id: jsonpath "$[0].id"
|
||||
[Asserts]
|
||||
jsonpath "$[0].name" == "hello-renamed.txt"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 3 – Record access to hello-renamed.txt
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/recent/file/{{file_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 4 – Recent list contains hello-renamed.txt
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/recent
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" count == 1
|
||||
jsonpath "$[0].item_type" == "file"
|
||||
jsonpath "$[0].item_name" == "hello-renamed.txt"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 5 – Clear all recent items
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
DELETE {{base_url}}/api/recent/clear
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 6 – Recent list is empty after clear
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/recent
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" isCollection
|
||||
jsonpath "$" count == 0
|
||||
+5
-1
@@ -85,8 +85,12 @@ log "Server is ready."
|
||||
# ── 4. Run Hurl tests ─────────────────────────────────────────────────────────
|
||||
|
||||
log "Running Hurl tests..."
|
||||
hurl --variables-file "$API_DIR/test.env" --test --jobs 1 \
|
||||
hurl --variables-file "$API_DIR/test.env" --file-root "$REPO_ROOT/tests" --test --jobs 1 \
|
||||
"$API_DIR/setup.hurl" \
|
||||
"$API_DIR/files-folders.hurl" \
|
||||
"$API_DIR/favorites.hurl" \
|
||||
"$API_DIR/trash.hurl" \
|
||||
"$API_DIR/recent.hurl" \
|
||||
"$API_DIR/contacts.hurl"
|
||||
|
||||
log "All tests passed."
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
# =============================================================
|
||||
# OxiCloud – Trash API end-to-end scenario
|
||||
# =============================================================
|
||||
# Depends on files-folders.hurl having run first (home folder
|
||||
# with test1 and test2-renamed must exist).
|
||||
#
|
||||
# Run:
|
||||
# hurl --variables-file tests/api/test.env --test tests/api/trash.hurl
|
||||
# =============================================================
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 1 – Login and capture the JWT token
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/auth/login
|
||||
Content-Type: application/json
|
||||
{
|
||||
"username": "{{username}}",
|
||||
"password": "{{password}}"
|
||||
}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
token: jsonpath "$.access_token"
|
||||
[Asserts]
|
||||
jsonpath "$.access_token" isString
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 2 – Trash is empty at the start
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/trash
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" isCollection
|
||||
jsonpath "$" count == 0
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 3 – Capture the home folder ID
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/folders
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
home_folder_id: jsonpath "$[0].id"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 4 – Create "to-delete" folder inside the home folder
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/folders
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"name": "to-delete",
|
||||
"parent_id": "{{home_folder_id}}"
|
||||
}
|
||||
|
||||
HTTP 201
|
||||
[Captures]
|
||||
to_delete_id: jsonpath "$.id"
|
||||
[Asserts]
|
||||
jsonpath "$.name" == "to-delete"
|
||||
jsonpath "$.parent_id" == {{home_folder_id}}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 5 – Upload hello.txt into "to-delete"
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/files/upload
|
||||
Authorization: Bearer {{token}}
|
||||
[MultipartFormData]
|
||||
folder_id: {{to_delete_id}}
|
||||
file: file,fixtures/hello.txt; text/plain
|
||||
|
||||
HTTP 201
|
||||
[Asserts]
|
||||
jsonpath "$.name" == "hello.txt"
|
||||
jsonpath "$.folder_id" == {{to_delete_id}}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 6 – Delete "to-delete" folder (moves it to trash)
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
DELETE {{base_url}}/api/folders/{{to_delete_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 204
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 7 – Trash contains exactly the "to-delete" folder
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/trash
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Captures]
|
||||
trash_id: jsonpath "$[0].id"
|
||||
[Asserts]
|
||||
jsonpath "$" count == 1
|
||||
jsonpath "$[0].item_type" == "folder"
|
||||
jsonpath "$[0].name" == "to-delete"
|
||||
jsonpath "$[0].original_id" == {{to_delete_id}}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 8 – "to-delete" is no longer listed in the home folder
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/folders/{{home_folder_id}}/contents
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$[*].name" not contains "to-delete"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 9 – Restore "to-delete" from trash
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
POST {{base_url}}/api/trash/{{trash_id}}/restore
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.success" == true
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 10 – "to-delete" folder is back in the home folder
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/folders/{{home_folder_id}}/contents
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$[*].name" contains "to-delete"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 11 – hello.txt is still inside the restored folder
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/files?folder_id={{to_delete_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" count == 1
|
||||
jsonpath "$[0].name" == "hello.txt"
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 12 – Trash is empty after restore
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/trash
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" count == 0
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 13 – Delete "to-delete" folder again (moves to trash)
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
DELETE {{base_url}}/api/folders/{{to_delete_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 204
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 14 – Empty the trash
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
DELETE {{base_url}}/api/trash/empty
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.success" == true
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 15 – Trash is empty after purge
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/trash
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$" count == 0
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 16 – "to-delete" folder no longer exists in home folder
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
GET {{base_url}}/api/folders/{{home_folder_id}}/contents
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$[*].name" not contains "to-delete"
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
hello from hurl test
|
||||
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 162 KiB |
Reference in New Issue
Block a user