Introduce real payment collection and wallet screens to replace home menu placeholders, wire APIs through RootView, and support bank card OSS uploads plus QR code saving to the photo library. Co-authored-by: Cursor <cursoragent@cursor.com>
118 lines
5.2 KiB
Swift
118 lines
5.2 KiB
Swift
//
|
||
// UploadTests.swift
|
||
// suixinkanTests
|
||
//
|
||
// Created by Codex on 2026/6/22.
|
||
//
|
||
|
||
import XCTest
|
||
@testable import suixinkan
|
||
|
||
@MainActor
|
||
/// 上传模块测试,覆盖 OSS 上传策略和 STS 配置接口。
|
||
final class UploadTests: XCTestCase {
|
||
/// 测试文件校验能识别空文件、超大文件和不支持扩展名。
|
||
func testOSSUploadPolicyValidatesFileConstraints() {
|
||
XCTAssertThrowsError(try OSSUploadPolicy.validate(dataSize: 0, fileName: "avatar.jpg")) { error in
|
||
XCTAssertEqual(error as? OSSUploadError, .emptyFile)
|
||
}
|
||
XCTAssertThrowsError(try OSSUploadPolicy.validate(dataSize: OSSUploadPolicy.maxFileSize + 1, fileName: "avatar.jpg")) { error in
|
||
XCTAssertEqual(error as? OSSUploadError, .fileTooLarge)
|
||
}
|
||
XCTAssertThrowsError(try OSSUploadPolicy.validate(dataSize: 12, fileName: "avatar.txt")) { error in
|
||
XCTAssertEqual(error as? OSSUploadError, .unsupportedFileType)
|
||
}
|
||
XCTAssertNoThrow(try OSSUploadPolicy.validate(dataSize: 12, fileName: "avatar.heic"))
|
||
}
|
||
|
||
/// 测试 objectKey 按业务模块生成稳定路径并清洗文件名。
|
||
func testOSSUploadPolicyBuildsObjectKeys() {
|
||
let date = Date(timeIntervalSince1970: 1_767_225_600)
|
||
let uuid = UUID(uuidString: "12345678-1234-1234-1234-1234567890ab")!
|
||
let timeZone = TimeZone(secondsFromGMT: 0)!
|
||
|
||
XCTAssertEqual(
|
||
OSSUploadPolicy.objectKey(fileName: "a/b.jpg", scenicId: 9, moduleType: "user_avatar", date: date, uuid: uuid, timeZone: timeZone),
|
||
"avatar/20260101/9/123456781234123412341234567890AB_a_b.jpg"
|
||
)
|
||
XCTAssertEqual(
|
||
OSSUploadPolicy.objectKey(fileName: "id.jpg", scenicId: 9, moduleType: "real_name", date: date, uuid: uuid, timeZone: timeZone),
|
||
"real_name/20260101/9/123456781234123412341234567890AB_id.jpg"
|
||
)
|
||
XCTAssertEqual(
|
||
OSSUploadPolicy.objectKey(fileName: "video.mp4", scenicId: 9, moduleType: "task_upload", date: date, uuid: uuid, timeZone: timeZone),
|
||
"task_upload/20260101/9/123456781234123412341234567890AB_video.mp4"
|
||
)
|
||
XCTAssertEqual(
|
||
OSSUploadPolicy.objectKey(fileName: "bank.jpg", scenicId: 9, moduleType: "bank_card", date: date, uuid: uuid, timeZone: timeZone),
|
||
"bank_card/20260101/9/123456781234123412341234567890AB_bank.jpg"
|
||
)
|
||
}
|
||
|
||
/// 测试 MIME 类型推断和 URL 拼接规则。
|
||
func testOSSUploadPolicyContentTypeAndURLJoin() {
|
||
XCTAssertEqual(OSSUploadPolicy.contentType(for: "a.jpg", fileType: 2), "image/jpeg")
|
||
XCTAssertEqual(OSSUploadPolicy.contentType(for: "a.png", fileType: 2), "image/png")
|
||
XCTAssertEqual(OSSUploadPolicy.contentType(for: "a.mp4", fileType: 1), "video/mp4")
|
||
XCTAssertEqual(OSSUploadPolicy.contentType(for: "a.unknown", fileType: 1), "video/mp4")
|
||
XCTAssertEqual(OSSUploadPolicy.contentType(for: "a.unknown", fileType: 2), "image/jpeg")
|
||
XCTAssertEqual(OSSUploadPolicy.joinURL(baseURL: "https://cdn.example.com", objectKey: "a/b.jpg"), "https://cdn.example.com/a/b.jpg")
|
||
XCTAssertEqual(OSSUploadPolicy.joinURL(baseURL: "https://cdn.example.com/", objectKey: "a/b.jpg"), "https://cdn.example.com/a/b.jpg")
|
||
}
|
||
|
||
/// 测试 STS token 接口使用正确路径、bucket 参数并兼容宽松字段类型。
|
||
func testUploadAPIRequestsSTSConfig() async throws {
|
||
let response = """
|
||
{
|
||
"code": 100000,
|
||
"msg": "success",
|
||
"data": {
|
||
"base_url": 123,
|
||
"endpoint": "oss-cn-hangzhou.aliyuncs.com",
|
||
"region": "cn-hangzhou",
|
||
"bucket": "vipsky",
|
||
"expire_seconds": "900",
|
||
"credentials": {
|
||
"access_key_id": 456,
|
||
"access_key_secret": "secret",
|
||
"security_token": "token"
|
||
}
|
||
}
|
||
}
|
||
""".data(using: .utf8)!
|
||
let session = UploadURLSession(responses: [response])
|
||
let api = UploadAPI(client: APIClient(session: session))
|
||
|
||
let config = try await api.aliyunOSSBucket()
|
||
|
||
XCTAssertEqual(config.baseUrl, "123")
|
||
XCTAssertEqual(config.expireSeconds, 900)
|
||
XCTAssertEqual(config.credentials.accessKeyId, "456")
|
||
let request = try XCTUnwrap(session.requests.first)
|
||
XCTAssertEqual(request.httpMethod, "GET")
|
||
XCTAssertEqual(request.url?.path, "/api/app/config/get-sts-token")
|
||
XCTAssertEqual(URLComponents(url: request.url!, resolvingAgainstBaseURL: false)?.queryItems?.first?.value, "vipsky")
|
||
}
|
||
}
|
||
|
||
/// 上传测试用 URLSession 替身,按顺序返回预设响应。
|
||
private final class UploadURLSession: URLSessionProtocol {
|
||
private var responses: [Data]
|
||
private(set) var requests: [URLRequest] = []
|
||
|
||
/// 初始化顺序响应。
|
||
init(responses: [Data]) {
|
||
self.responses = responses
|
||
}
|
||
|
||
/// 记录请求并返回下一份响应。
|
||
func data(for request: URLRequest) async throws -> (Data, URLResponse) {
|
||
requests.append(request)
|
||
let data = responses.isEmpty ? Data() : responses.removeFirst()
|
||
return (
|
||
data,
|
||
HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!
|
||
)
|
||
}
|
||
}
|