Add Live module with stream management and album flows.
Migrate live stream management and live album from home placeholders, including alive album OSS upload, home routing, and unit tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
168
suixinkanTests/Live/LiveAPITests.swift
Normal file
168
suixinkanTests/Live/LiveAPITests.swift
Normal file
@ -0,0 +1,168 @@
|
||||
//
|
||||
// LiveAPITests.swift
|
||||
// suixinkanTests
|
||||
//
|
||||
// Created by Codex on 2026/6/25.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import suixinkan
|
||||
|
||||
@MainActor
|
||||
/// 直播 API 测试,覆盖直播管理和直播相册接口编码。
|
||||
final class LiveAPITests: XCTestCase {
|
||||
func testLiveListAndDetailDecodeFixturesAndUseExpectedQueries() async throws {
|
||||
let listSession = LiveRecordingURLSession(data: try TestFixture.data(named: "alive_list_success"))
|
||||
let listAPI = LiveAPI(client: APIClient(session: listSession))
|
||||
|
||||
let list = try await listAPI.liveList(scenicId: 88, page: 0, pageSize: 0)
|
||||
|
||||
XCTAssertEqual(list.total, 2)
|
||||
XCTAssertEqual(list.page, 1)
|
||||
XCTAssertEqual(list.pageSize, 1)
|
||||
XCTAssertEqual(list.items.first?.id, 8101)
|
||||
XCTAssertEqual(list.items.first?.manualPushMode, 2)
|
||||
let listRequest = try XCTUnwrap(listSession.requests.first)
|
||||
XCTAssertEqual(listRequest.httpMethod, "GET")
|
||||
XCTAssertEqual(listRequest.url?.path, "/api/app/manual-live/list")
|
||||
XCTAssertEqual(liveQueryItems(from: listRequest)["scenic_id"], "88")
|
||||
XCTAssertEqual(liveQueryItems(from: listRequest)["page"], "1")
|
||||
XCTAssertEqual(liveQueryItems(from: listRequest)["page_size"], "1")
|
||||
|
||||
let detailSession = LiveRecordingURLSession(data: try TestFixture.data(named: "alive_detail_success"))
|
||||
let detailAPI = LiveAPI(client: APIClient(session: detailSession))
|
||||
|
||||
let detail = try await detailAPI.liveDetail(liveId: 8101)
|
||||
|
||||
XCTAssertEqual(detail.id, 8101)
|
||||
XCTAssertEqual(detail.pushUrl, "rtmp://push.example.com/live/8101")
|
||||
XCTAssertEqual(detail.status, 2)
|
||||
XCTAssertEqual(detail.duration, 3600)
|
||||
let detailRequest = try XCTUnwrap(detailSession.requests.first)
|
||||
XCTAssertEqual(detailRequest.url?.path, "/api/app/manual-live/detail")
|
||||
XCTAssertEqual(liveQueryItems(from: detailRequest)["live_id"], "8101")
|
||||
}
|
||||
|
||||
func testLiveCreateAndControlsUseExpectedBodies() async throws {
|
||||
let createSession = LiveRecordingURLSession(responses: Array(repeating: try TestFixture.data(named: "empty_success"), count: 5))
|
||||
let api = LiveAPI(client: APIClient(session: createSession))
|
||||
|
||||
try await api.liveCreate(LiveCreateRequest(scenicId: "88", title: "景区晨间直播", coverImg: "https://cdn.example.com/cover.jpg"))
|
||||
|
||||
var request = try XCTUnwrap(createSession.requests.last)
|
||||
XCTAssertEqual(request.url?.path, "/api/app/manual-live/create")
|
||||
var json = try liveJSONBody(from: request)
|
||||
XCTAssertEqual(json["scenic_id"] as? String, "88")
|
||||
XCTAssertEqual(json["title"] as? String, "景区晨间直播")
|
||||
XCTAssertEqual(json["cover_img"] as? String, "https://cdn.example.com/cover.jpg")
|
||||
|
||||
try await api.liveStart(liveId: 8101)
|
||||
request = try XCTUnwrap(createSession.requests.last)
|
||||
XCTAssertEqual(request.url?.path, "/api/app/manual-live/start")
|
||||
XCTAssertEqual(try liveJSONBody(from: request)["live_id"] as? Int, 8101)
|
||||
|
||||
try await api.liveStop(liveId: 8101)
|
||||
request = try XCTUnwrap(createSession.requests.last)
|
||||
XCTAssertEqual(request.url?.path, "/api/app/manual-live/stop")
|
||||
XCTAssertEqual(try liveJSONBody(from: request)["live_id"] as? Int, 8101)
|
||||
|
||||
try await api.liveFinish(liveId: 8101)
|
||||
request = try XCTUnwrap(createSession.requests.last)
|
||||
XCTAssertEqual(request.url?.path, "/api/app/manual-live/finish")
|
||||
XCTAssertEqual(try liveJSONBody(from: request)["live_id"] as? Int, 8101)
|
||||
|
||||
try await api.liveSetPushMode(liveId: 8101, mode: 2)
|
||||
request = try XCTUnwrap(createSession.requests.last)
|
||||
XCTAssertEqual(request.url?.path, "/api/app/manual-live/set-push-mode")
|
||||
json = try liveJSONBody(from: request)
|
||||
XCTAssertEqual(json["live_id"] as? Int, 8101)
|
||||
XCTAssertEqual(json["manual_push_mode"] as? Int, 2)
|
||||
}
|
||||
|
||||
func testLiveAlbumAPIsUseExpectedRequests() async throws {
|
||||
let listSession = LiveRecordingURLSession(data: try TestFixture.data(named: "alive_album_list_success"))
|
||||
let api = LiveAPI(client: APIClient(session: listSession))
|
||||
|
||||
let response = try await api.liveAlbumList(scenicId: 88, startTime: "2026-05-01", endTime: "2026-05-23", page: 0, pageSize: 0)
|
||||
|
||||
XCTAssertEqual(response.total, 2)
|
||||
XCTAssertEqual(response.items.first?.id, 6101)
|
||||
XCTAssertEqual(response.items.first?.items.last?.previewURL, "https://cdn.example.com/alive/video-cover.jpg")
|
||||
var request = try XCTUnwrap(listSession.requests.last)
|
||||
XCTAssertEqual(request.url?.path, "/api/app/view-album/folders")
|
||||
var query = liveQueryItems(from: request)
|
||||
XCTAssertEqual(query["scenic_id"], "88")
|
||||
XCTAssertEqual(query["start_time"], "2026-05-01")
|
||||
XCTAssertEqual(query["end_time"], "2026-05-23")
|
||||
XCTAssertEqual(query["page"], "1")
|
||||
XCTAssertEqual(query["page_size"], "1")
|
||||
|
||||
let mutationSession = LiveRecordingURLSession(data: try TestFixture.data(named: "empty_success"))
|
||||
let mutationAPI = LiveAPI(client: APIClient(session: mutationSession))
|
||||
try await mutationAPI.liveAlbumCreateFolder(
|
||||
LiveAlbumCreateFolderRequest(
|
||||
scenicId: "88",
|
||||
name: "五一直播相册",
|
||||
items: [LiveAlbumCreateFileItem(url: "https://cdn.example.com/a.jpg", type: 1, size: 12, coverImg: nil)]
|
||||
)
|
||||
)
|
||||
request = try XCTUnwrap(mutationSession.requests.last)
|
||||
XCTAssertEqual(request.url?.path, "/api/app/view-album/create-folder")
|
||||
var json = try liveJSONBody(from: request)
|
||||
XCTAssertEqual(json["scenic_id"] as? String, "88")
|
||||
XCTAssertEqual((json["items"] as? [[String: Any]])?.first?["url"] as? String, "https://cdn.example.com/a.jpg")
|
||||
|
||||
try await mutationAPI.liveAlbumDeleteFolder(folderId: 6101)
|
||||
request = try XCTUnwrap(mutationSession.requests.last)
|
||||
XCTAssertEqual(request.url?.path, "/api/app/view-album/delete-folder")
|
||||
XCTAssertEqual(try liveJSONBody(from: request)["folder_id"] as? Int, 6101)
|
||||
|
||||
let detailSession = LiveRecordingURLSession(data: try TestFixture.data(named: "alive_album_folder_detail_success"))
|
||||
let detailAPI = LiveAPI(client: APIClient(session: detailSession))
|
||||
let folder = try await detailAPI.liveAlbumFolderDetail(folderId: 6101)
|
||||
XCTAssertEqual(folder.items.count, 2)
|
||||
request = try XCTUnwrap(detailSession.requests.last)
|
||||
XCTAssertEqual(request.url?.path, "/api/app/view-album/folder-detail")
|
||||
XCTAssertEqual(liveQueryItems(from: request)["folder_id"], "6101")
|
||||
|
||||
try await mutationAPI.liveAlbumDeleteFiles(folderId: 6101, fileIds: [7101, 7102])
|
||||
request = try XCTUnwrap(mutationSession.requests.last)
|
||||
XCTAssertEqual(request.url?.path, "/api/app/view-album/delete-files")
|
||||
json = try liveJSONBody(from: request)
|
||||
XCTAssertEqual(json["folder_id"] as? Int, 6101)
|
||||
XCTAssertEqual(json["file_ids"] as? [Int], [7101, 7102])
|
||||
}
|
||||
}
|
||||
|
||||
private final class LiveRecordingURLSession: URLSessionProtocol {
|
||||
private var responses: [Data]
|
||||
private(set) var requests: [URLRequest] = []
|
||||
|
||||
init(data: Data) {
|
||||
responses = [data]
|
||||
}
|
||||
|
||||
init(responses: [Data]) {
|
||||
self.responses = responses
|
||||
}
|
||||
|
||||
func data(for request: URLRequest) async throws -> (Data, URLResponse) {
|
||||
requests.append(request)
|
||||
let data = responses.isEmpty ? try TestFixture.data(named: "empty_success") : responses.removeFirst()
|
||||
return (data, HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!)
|
||||
}
|
||||
}
|
||||
|
||||
private func liveQueryItems(from request: URLRequest) -> [String: String] {
|
||||
guard let url = request.url,
|
||||
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
|
||||
else { return [:] }
|
||||
return Dictionary(uniqueKeysWithValues: (components.queryItems ?? []).compactMap { item in
|
||||
item.value.map { (item.name, $0) }
|
||||
})
|
||||
}
|
||||
|
||||
private func liveJSONBody(from request: URLRequest) throws -> [String: Any] {
|
||||
let body = try XCTUnwrap(request.httpBody)
|
||||
return try XCTUnwrap(JSONSerialization.jsonObject(with: body) as? [String: Any])
|
||||
}
|
||||
Reference in New Issue
Block a user