ios-projects/AIStudyApp/AIStudyAppTests/V2AdapterSmokeTests.swift
wangdl 4ba747af46 feat: iOS learning info P0 complete (IOS-INFO-000~017, 038~039)
Models: ReadingMaterialContext, ReadingEventUploadItem, AppContext
Runtime: ReadingRuntimeAdapter, SessionManager, RecoveryService
Reader: MaterialReaderView lifecycle, Heartbeat Timer, Position Adapter
Export: EventMapper, UploadQueue, export→queue→ack flow
Tests: 16 test files covering models, runtime, lifecycle, upload
Docs: ios-learning-info-architecture.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-15 21:53:36 +08:00

105 lines
3.3 KiB
Swift

import XCTest
@testable import AIStudyApp
final class V2AdapterSmokeTests: XCTestCase {
// MARK: - V2 Adapter Existence
func test_v2Adapter_canBeInstantiated() {
let adapter = RustReadingRuntimeAdapter()
XCTAssertNotNil(adapter)
}
func test_noopAdapter_canBeInstantiated() {
let adapter = NoopReadingRuntimeAdapter()
XCTAssertNotNil(adapter)
}
func test_v1Adapter_canBeInstantiated() {
let adapter = V1ReadingRuntimeAdapter()
XCTAssertNotNil(adapter)
}
// MARK: - Session Lifecycle (Noop)
func test_noopAdapter_startSession_returnsSessionId() {
let adapter = NoopReadingRuntimeAdapter()
let ref = ReadingMaterialRef(materialId: "test-material")
let sid = try! adapter.startSession(material: ref, timestampMs: 1_700_000_000_000)
XCTAssertTrue(sid.hasPrefix("noop-"))
}
func test_noopAdapter_closeSession_doesNotThrow() {
let adapter = NoopReadingRuntimeAdapter()
XCTAssertNoThrow(try adapter.closeSession("noop-session"))
}
// MARK: - Buffer (Noop)
func test_noopAdapter_exportEvents_returnsEmpty() {
let adapter = NoopReadingRuntimeAdapter()
let events = adapter.exportEvents(limit: 100, timestampMs: 1_700_000_000_000)
XCTAssertTrue(events.isEmpty)
}
func test_noopAdapter_ackEvents_returnsCount() {
let adapter = NoopReadingRuntimeAdapter()
let count = adapter.ackEvents(["evt-1", "evt-2"])
XCTAssertEqual(count, 2)
}
func test_noopAdapter_reloadStaleEvents_returnsZero() {
let adapter = NoopReadingRuntimeAdapter()
let count = adapter.reloadStaleEvents()
XCTAssertEqual(count, 0)
}
// MARK: - Error Conversion
func test_runtimeError_wraps_documentError() {
// DocumentError is UniFFI-generated; verify wrapping doesn't crash
let docErr = DocumentError.IoError(message: "test error")
let runtimeErr = RuntimeError(from: docErr)
switch runtimeErr {
case .document(let e):
XCTAssertEqual(e, docErr)
default:
XCTFail("Expected .document error")
}
}
func test_runtimeError_wraps_adapterError() {
let adapterErr = ReadingRuntimeAdapterError.unavailable
let runtimeErr = RuntimeError(from: adapterErr)
switch runtimeErr {
case .adapter(let e):
XCTAssertEqual(e, adapterErr)
default:
XCTFail("Expected .adapter error")
}
}
func test_runtimeError_wraps_unknown() {
let nsErr = NSError(domain: "test", code: 1, userInfo: [NSLocalizedDescriptionKey: "test message"])
let runtimeErr = RuntimeError(from: nsErr)
switch runtimeErr {
case .unknown(let msg):
XCTAssertTrue(msg.contains("test message"))
default:
XCTFail("Expected .unknown error")
}
}
// MARK: - Error Descriptions
func test_adapterError_unavailable_hasDescription() {
let err = ReadingRuntimeAdapterError.unavailable
XCTAssertFalse(err.errorDescription?.isEmpty ?? true)
}
func test_adapterError_v1ReturnTypeUnsupported_hasDescription() {
let err = ReadingRuntimeAdapterError.v1ReturnTypeUnsupported
XCTAssertFalse(err.errorDescription?.isEmpty ?? true)
}
}