ios-projects/AIStudyApp/AIStudyAppTests/PositionAdapterTests.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

168 lines
5.0 KiB
Swift

import XCTest
@testable import AIStudyApp
final class PositionAdapterTests: XCTestCase {
// MARK: - Clamping
func test_clamp_normal_value() {
let pos = ReadingPositionAdapter.buildPosition(
materialType: .markdown,
blockId: "b1",
scrollProgress: 0.5
)
if case .markdown(_, let progress) = pos! {
XCTAssertEqual(progress, 0.5)
} else {
XCTFail("Expected markdown position")
}
}
func test_clamp_below_zero() {
let pos = ReadingPositionAdapter.buildPosition(
materialType: .markdown,
blockId: "b1",
scrollProgress: -0.5
)
if case .markdown(_, let progress) = pos! {
XCTAssertEqual(progress, 0.0, "Negative progress should clamp to 0")
} else {
XCTFail("Expected markdown position")
}
}
func test_clamp_above_one() {
let pos = ReadingPositionAdapter.buildPosition(
materialType: .markdown,
blockId: "b1",
scrollProgress: 1.5
)
if case .markdown(_, let progress) = pos! {
XCTAssertEqual(progress, 1.0, "Progress >1 should clamp to 1")
} else {
XCTFail("Expected markdown position")
}
}
func test_clamp_nan() {
let pos = ReadingPositionAdapter.buildPosition(
materialType: .markdown,
blockId: "b1",
scrollProgress: Float.nan
)
if case .markdown(_, let progress) = pos! {
XCTAssertEqual(progress, 0.0, "NaN progress should clamp to 0")
} else {
XCTFail("Expected markdown position")
}
}
// MARK: - Material types
func test_pdf_position() {
let pos = ReadingPositionAdapter.buildPosition(
materialType: .pdf,
pageNumber: 3,
pageProgress: 0.5,
overallProgress: 0.3
)
if case .pdf(let page, let pageProg, let overall) = pos! {
XCTAssertEqual(page, 3)
XCTAssertEqual(pageProg, 0.5)
XCTAssertEqual(overall, 0.3)
} else {
XCTFail("Expected PDF position")
}
}
func test_text_position() {
let pos = ReadingPositionAdapter.buildPosition(
materialType: .text,
lineNumber: 42,
scrollProgress: 0.7
)
if case .text(let line, let progress) = pos! {
XCTAssertEqual(line, 42)
XCTAssertEqual(progress, 0.7)
} else {
XCTFail("Expected text position")
}
}
func test_image_position() {
let pos = ReadingPositionAdapter.buildPosition(
materialType: .image,
zoomScale: 2.0,
offsetX: 100,
offsetY: 200
)
if case .image(let zoom, let ox, let oy) = pos! {
XCTAssertEqual(zoom, 2.0)
XCTAssertEqual(ox, 100)
XCTAssertEqual(oy, 200)
} else {
XCTFail("Expected image position")
}
}
func test_epub_position() {
let pos = ReadingPositionAdapter.buildPosition(
materialType: .epub,
chapterId: "ch3",
chapterProgress: 0.4,
overallProgress: 0.2
)
if case .epub(let ch, let chProg, let overall) = pos! {
XCTAssertEqual(ch, "ch3")
XCTAssertEqual(chProg, 0.4)
XCTAssertEqual(overall, 0.2)
} else {
XCTFail("Expected epub position")
}
}
func test_unknown_material_returns_unknown() {
let pos = ReadingPositionAdapter.buildPosition(materialType: .unknown)
if case .unknown = pos! {
// Expected
} else {
XCTFail("Expected unknown position")
}
}
func test_markdown_missing_block_id_returns_nil() {
let pos = ReadingPositionAdapter.buildPosition(
materialType: .markdown,
blockId: nil,
scrollProgress: 0.5
)
XCTAssertNil(pos, "Markdown without blockId should return nil")
}
func test_epub_missing_chapter_id_returns_nil() {
let pos = ReadingPositionAdapter.buildPosition(
materialType: .epub,
chapterId: nil,
chapterProgress: 0.5
)
XCTAssertNil(pos, "Epub without chapterId should return nil")
}
// MARK: - blockId extraction
func test_blockId_from_heading() {
let block = DocumentBlock.heading(id: "h1", level: 1, text: "Title")
XCTAssertEqual(ReadingPositionAdapter.blockId(from: block), "h1")
}
func test_blockId_from_paragraph() {
let block = DocumentBlock.paragraph(id: "p1", text: "Hello")
XCTAssertEqual(ReadingPositionAdapter.blockId(from: block), "p1")
}
func test_blockId_from_codeBlock() {
let block = DocumentBlock.codeBlock(id: "c1", language: "swift", code: "print()")
XCTAssertEqual(ReadingPositionAdapter.blockId(from: block), "c1")
}
}