V3 Migration Phase 2-4: Implementation of specialized Binary Actions (Upload, Stream, Delete) and Orphan management logic. Full E2E coverage.
This commit is contained in:
70
tests/e2e/test_e2e_v3_action_delete.py
Normal file
70
tests/e2e/test_e2e_v3_action_delete.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import requests
|
||||
import json
|
||||
import io
|
||||
|
||||
# Configuration
|
||||
BASE_URL = "https://dev-api.oneskyit.com/v3/action/hosted_file"
|
||||
API_KEY = "IDF68Em5X4HTZlswRNgepQ"
|
||||
ACCOUNT_ID = "Q8lR8Ai8hx2FjbQ3C_EH1Q"
|
||||
LINK_TO_TYPE = "archive_content"
|
||||
LINK_TO_ID = "bZOa7CtUm0E"
|
||||
|
||||
def test_v3_delete_flow():
|
||||
print(f"--- Starting V3 Action Delete Tests ---")
|
||||
|
||||
headers = {
|
||||
"X-Aether-API-Key": API_KEY,
|
||||
"x-account-id": ACCOUNT_ID
|
||||
}
|
||||
|
||||
# 1. Setup: Upload a fresh file to test with
|
||||
print("\n[Step 1] Uploading test file...")
|
||||
files = [("file_list", ("v3_delete_test.txt", io.BytesIO(b"Delete me"), "text/plain"))]
|
||||
data = {
|
||||
"account_id": ACCOUNT_ID,
|
||||
"link_to_type": LINK_TO_TYPE,
|
||||
"link_to_id": LINK_TO_ID
|
||||
}
|
||||
up_resp = requests.post(f"{BASE_URL}/upload", headers=headers, files=files, data=data)
|
||||
file_id = up_resp.json()['data'][0]['id']
|
||||
print(f"Created file: {file_id}")
|
||||
|
||||
# 2. Test Fake Delete
|
||||
print("\n[Step 2] Testing Fake Delete (Testing Mode)...")
|
||||
params_fake = {
|
||||
"link_to_type": LINK_TO_TYPE,
|
||||
"link_to_id": LINK_TO_ID,
|
||||
"fake_delete": "true"
|
||||
}
|
||||
resp_fake = requests.delete(f"{BASE_URL}/{file_id}", headers=headers, params=params_fake)
|
||||
print(f"Status: {resp_fake.status_code}")
|
||||
print(f"Response: {json.dumps(resp_fake.json()['data'], indent=2)}")
|
||||
assert resp_fake.json()['data']['fake_delete'] == True
|
||||
|
||||
# 3. Test Real Delete (Link Only)
|
||||
print("\n[Step 3] Testing Real Delete (Link Only, rm_orphan=False)...")
|
||||
params_link = {
|
||||
"link_to_type": LINK_TO_TYPE,
|
||||
"link_to_id": LINK_TO_ID,
|
||||
"rm_orphan": "false"
|
||||
}
|
||||
resp_link = requests.delete(f"{BASE_URL}/{file_id}", headers=headers, params=params_link)
|
||||
print(f"Status: {resp_link.status_code}")
|
||||
print(f"Response: {json.dumps(resp_link.json()['data'], indent=2)}")
|
||||
assert resp_link.json()['data']['link_removed'] == True
|
||||
assert resp_link.json()['data']['is_orphan'] == True # Should be orphan now, but not removed
|
||||
|
||||
# 4. Test Orphan Cleanup (rm_orphan=True)
|
||||
print("\n[Step 4] Testing Orphan Cleanup (rm_orphan=True, method=delete)...")
|
||||
params_orphan = {
|
||||
"rm_orphan": "true",
|
||||
"method": "delete"
|
||||
}
|
||||
resp_orphan = requests.delete(f"{BASE_URL}/{file_id}", headers=headers, params=params_orphan)
|
||||
print(f"Status: {resp_orphan.status_code}")
|
||||
print(f"Response: {json.dumps(resp_orphan.json()['data'], indent=2)}")
|
||||
assert resp_orphan.json()['data']['physical_removed'] == True
|
||||
assert resp_orphan.json()['data']['record_removed'] == True
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_v3_delete_flow()
|
||||
71
tests/e2e/test_e2e_v3_action_download.py
Normal file
71
tests/e2e/test_e2e_v3_action_download.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import requests
|
||||
import json
|
||||
|
||||
# Configuration
|
||||
BASE_URL = "https://dev-api.oneskyit.com/v3/action/hosted_file"
|
||||
API_KEY = "IDF68Em5X4HTZlswRNgepQ"
|
||||
ACCOUNT_ID = "Q8lR8Ai8hx2FjbQ3C_EH1Q"
|
||||
|
||||
# This file was created during our earlier upload tests
|
||||
VALID_FILE_ID = "2R06T6yuQLw"
|
||||
# Use a known site key from the DB for the bypass test
|
||||
# SITE_KEY = "..."
|
||||
|
||||
def test_download_standard():
|
||||
print(f"--- Testing Standard Download via V3 Action: {VALID_FILE_ID} ---")
|
||||
|
||||
url = f"{BASE_URL}/{VALID_FILE_ID}/download"
|
||||
headers = {
|
||||
"X-Aether-API-Key": API_KEY,
|
||||
"x-account-id": ACCOUNT_ID
|
||||
}
|
||||
|
||||
try:
|
||||
# We don't want to download the whole binary in a test, so we'll check headers
|
||||
response = requests.get(url, headers=headers, stream=True)
|
||||
print(f"Status: {response.status_code}")
|
||||
print(f"Content-Type: {response.headers.get('Content-Type')}")
|
||||
print(f"Content-Length: {response.headers.get('Content-Length')}")
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ Success: Standard download works.")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Failed: {response.text}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"💥 Exception: {e}")
|
||||
return False
|
||||
|
||||
def test_download_streaming():
|
||||
print(f"\n--- Testing Byte-Range Streaming: {VALID_FILE_ID} ---")
|
||||
|
||||
url = f"{BASE_URL}/{VALID_FILE_ID}/download"
|
||||
headers = {
|
||||
"X-Aether-API-Key": API_KEY,
|
||||
"x-account-id": ACCOUNT_ID,
|
||||
"Range": "bytes=0-10"
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=headers)
|
||||
print(f"Status: {response.status_code} (Expected 206)")
|
||||
print(f"Content-Range: {response.headers.get('Content-Range')}")
|
||||
|
||||
if response.status_code == 206:
|
||||
print("✅ Success: Byte-range streaming works.")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Failed: {response.status_code}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"💥 Exception: {e}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
s1 = test_download_standard()
|
||||
s2 = test_download_streaming()
|
||||
if s1 and s2:
|
||||
print("\n🎉 ALL DOWNLOAD ACTION TESTS PASSED!")
|
||||
else:
|
||||
print("\n❌ SOME TESTS FAILED.")
|
||||
44
tests/e2e/test_e2e_v3_action_scaffold.py
Normal file
44
tests/e2e/test_e2e_v3_action_scaffold.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import requests
|
||||
import json
|
||||
|
||||
# Configuration
|
||||
BASE_URL = "https://dev-api.oneskyit.com/v3/action/hosted_file"
|
||||
API_KEY = "IDF68Em5X4HTZlswRNgepQ"
|
||||
ACCOUNT_ID = "Q8lR8Ai8hx2FjbQ3C_EH1Q"
|
||||
|
||||
def test_scaffold_reachability():
|
||||
print("--- Testing V3 Action Router Scaffold Reachability ---")
|
||||
|
||||
headers = {
|
||||
"X-Aether-API-Key": API_KEY,
|
||||
"x-account-id": ACCOUNT_ID
|
||||
}
|
||||
|
||||
# 1. Test Upload Scaffold
|
||||
print("\n[1] Testing Upload Action Reachability...")
|
||||
files = [("file_list", ("test.txt", b"content", "text/plain"))]
|
||||
data = {
|
||||
"account_id": ACCOUNT_ID,
|
||||
"link_to_type": "archive_content",
|
||||
"link_to_id": "bZOa7CtUm0E8hx2FjbQ3C_"
|
||||
}
|
||||
resp = requests.post(f"{BASE_URL}/upload", headers=headers, files=files, data=data)
|
||||
print(f"Status: {resp.status_code}")
|
||||
if resp.status_code == 200:
|
||||
print(f"✅ Success: {resp.json().get('status_message')}")
|
||||
else:
|
||||
print(f"❌ Failed: {resp.text}")
|
||||
|
||||
# 2. Test Download Scaffold with Delay
|
||||
print("\n[2] Testing Download Action Reachability (with 500ms delay)...")
|
||||
headers_w_delay = headers.copy()
|
||||
headers_w_delay["X-Delay-ms"] = "500"
|
||||
resp = requests.get(f"{BASE_URL}/some_file_id/download", headers=headers_w_delay)
|
||||
print(f"Status: {resp.status_code}")
|
||||
if resp.status_code == 200:
|
||||
print(f"✅ Success: {resp.json().get('status_message')}")
|
||||
else:
|
||||
print(f"❌ Failed: {resp.text}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_scaffold_reachability()
|
||||
91
tests/e2e/test_e2e_v3_action_upload.py
Normal file
91
tests/e2e/test_e2e_v3_action_upload.py
Normal file
@@ -0,0 +1,91 @@
|
||||
import requests
|
||||
import io
|
||||
import json
|
||||
|
||||
# Configuration
|
||||
BASE_URL = "https://dev-api.oneskyit.com/v3/action/hosted_file"
|
||||
API_KEY = "IDF68Em5X4HTZlswRNgepQ"
|
||||
ACCOUNT_ID = "Q8lR8Ai8hx2FjbQ3C_EH1Q"
|
||||
LINK_TO_TYPE = "archive_content"
|
||||
LINK_TO_ID = "bZOa7CtUm0E"
|
||||
|
||||
def test_v3_upload_flow():
|
||||
print(f"--- Starting V3 Action Upload Tests against {BASE_URL} ---")
|
||||
|
||||
headers = {
|
||||
"X-Aether-API-Key": API_KEY,
|
||||
"x-account-id": ACCOUNT_ID
|
||||
}
|
||||
|
||||
# 1. Multi-File Upload with Extension Check
|
||||
print("\n[Test 1] Multi-File Upload + Extension Validation...")
|
||||
files = [
|
||||
("file_list", ("v3_multi_1.txt", io.BytesIO(b"V3 Content 1"), "text/plain")),
|
||||
("file_list", ("v3_multi_2.txt", io.BytesIO(b"V3 Content 2"), "text/plain")),
|
||||
]
|
||||
data = {
|
||||
"account_id": ACCOUNT_ID,
|
||||
"link_to_type": LINK_TO_TYPE,
|
||||
"link_to_id": LINK_TO_ID
|
||||
}
|
||||
params = {"allowed_extensions": ["txt", "pdf"]}
|
||||
|
||||
url = f"{BASE_URL}/upload"
|
||||
|
||||
try:
|
||||
response = requests.post(url, headers=headers, files=files, data=data, params=params)
|
||||
print(f"Status: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
data_list = result.get('data', [])
|
||||
print(f"✅ Success! Processed {len(data_list)} files.")
|
||||
for i, f in enumerate(data_list):
|
||||
print(f" File {i+1} ID: {f.get('id')} | Name: {f.get('filename')}")
|
||||
assert isinstance(f.get('id'), str)
|
||||
else:
|
||||
print(f"❌ Failed: {response.text}")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"💥 Exception: {e}")
|
||||
return
|
||||
|
||||
# 2. Test Deduplication (Upload same file again)
|
||||
print("\n[Test 2] Testing Deduplication Logic...")
|
||||
files_dup = [
|
||||
("file_list", ("v3_multi_1.txt", io.BytesIO(b"V3 Content 1"), "text/plain"))
|
||||
]
|
||||
|
||||
try:
|
||||
response = requests.post(url, headers=headers, files=files_dup, data=data)
|
||||
print(f"Status: {response.status_code}")
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
file_data = result.get('data', [])[0]
|
||||
print(f"Already Exists Flag: {file_data.get('already_exists')}")
|
||||
assert file_data.get('already_exists') == True
|
||||
print("✅ Deduplication logic verified.")
|
||||
else:
|
||||
print(f"❌ Failed: {response.text}")
|
||||
except Exception as e:
|
||||
print(f"💥 Exception: {e}")
|
||||
|
||||
# 3. Test Extension Rejection
|
||||
print("\n[Test 3] Testing Extension Rejection...")
|
||||
files_bad = [
|
||||
("file_list", ("virus.exe", io.BytesIO(b"bad"), "application/octet-stream"))
|
||||
]
|
||||
params_restrict = {"allowed_extensions": ["txt"]}
|
||||
|
||||
try:
|
||||
response = requests.post(url, headers=headers, files=files_bad, data=data, params=params_restrict)
|
||||
print(f"Status: {response.status_code} (Expected 400)")
|
||||
if response.status_code == 400:
|
||||
print(f"✅ Success: correctly rejected .exe file.")
|
||||
else:
|
||||
print(f"❌ Failure: allowed .exe file with status {response.status_code}")
|
||||
except Exception as e:
|
||||
print(f"💥 Exception: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_v3_upload_flow()
|
||||
Reference in New Issue
Block a user