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>
105 lines
3.3 KiB
Swift
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)
|
|
}
|
|
}
|