import XCTest @testable import AIStudyApp final class MaterialPathResolverTests: XCTestCase { // MARK: - resolveKnowledgeSource path func test_resolveKnowledgeSource_finds_file_in_knowledge_sources_dir() { let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let sourceId = "ks-test-\(UUID().uuidString)" let ksDir = docs.appendingPathComponent("knowledge_sources/\(sourceId)") try? FileManager.default.createDirectory(at: ksDir, withIntermediateDirectories: true) let fileURL = ksDir.appendingPathComponent("content.md") defer { try? FileManager.default.removeItem(at: fileURL) try? FileManager.default.removeItem(at: ksDir) } FileManager.default.createFile(atPath: fileURL.path, contents: "test".data(using: .utf8)) let result = MaterialPathResolver.resolve(materialId: sourceId, apiType: "knowledge_source", title: "content.md") XCTAssertNotNil(result) XCTAssertEqual(result?.filePath, fileURL.path) XCTAssertEqual(result?.materialType, .markdown) } func test_resolveKnowledgeSource_returns_nil_for_empty_dir() { let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let sourceId = "ks-empty-\(UUID().uuidString)" let ksDir = docs.appendingPathComponent("knowledge_sources/\(sourceId)") try? FileManager.default.createDirectory(at: ksDir, withIntermediateDirectories: true) defer { try? FileManager.default.removeItem(at: ksDir) } // Empty directory → no files → falls through to Documents/Caches let result = MaterialPathResolver.resolve(materialId: sourceId, apiType: "knowledge_source", title: "note.md") // Directory exists but is empty → resolveKnowledgeSource returns nil → falls through XCTAssertNil(result) // No file in Documents or Caches either } // MARK: - resolve returns nil for nonexistent file func test_resolve_returns_nil_for_nonexistent_material() { let result = MaterialPathResolver.resolve( materialId: UUID().uuidString, apiType: nil, title: nil ) XCTAssertNil(result) } // MARK: - resolve finds file in Documents func test_resolve_finds_file_in_documents() { let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileId = "test-docs-\(UUID().uuidString)" let fileURL = docs.appendingPathComponent(fileId) defer { try? FileManager.default.removeItem(at: fileURL) } FileManager.default.createFile(atPath: fileURL.path, contents: "test".data(using: .utf8)) let result = MaterialPathResolver.resolve(materialId: fileId, apiType: nil, title: nil) XCTAssertNotNil(result) XCTAssertEqual(result?.filePath, fileURL.path) } // MARK: - resolve finds file in Caches func test_resolve_finds_file_in_caches() { let cache = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! let fileId = "test-cache-\(UUID().uuidString)" let fileURL = cache.appendingPathComponent(fileId) defer { try? FileManager.default.removeItem(at: fileURL) } FileManager.default.createFile(atPath: fileURL.path, contents: "test".data(using: .utf8)) let result = MaterialPathResolver.resolve(materialId: fileId, apiType: nil, title: nil) XCTAssertNotNil(result) XCTAssertEqual(result?.filePath, fileURL.path) } // MARK: - materialType inference from title extension func test_resolve_infers_markdown_from_title() { let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileId = "test-md-\(UUID().uuidString)" let fileURL = docs.appendingPathComponent(fileId) defer { try? FileManager.default.removeItem(at: fileURL) } FileManager.default.createFile(atPath: fileURL.path, contents: "test".data(using: .utf8)) let result = MaterialPathResolver.resolve(materialId: fileId, apiType: "temporary_file", title: "note.md") XCTAssertNotNil(result) XCTAssertEqual(result?.materialType, .markdown) } func test_resolve_infers_pdf_from_title() { let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileId = "test-pdf-\(UUID().uuidString)" let fileURL = docs.appendingPathComponent(fileId) defer { try? FileManager.default.removeItem(at: fileURL) } FileManager.default.createFile(atPath: fileURL.path, contents: "test".data(using: .utf8)) let result = MaterialPathResolver.resolve(materialId: fileId, apiType: "temporary_file", title: "book.pdf") XCTAssertNotNil(result) XCTAssertEqual(result?.materialType, .pdf) } func test_resolve_infers_text_from_title() { let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileId = "test-txt-\(UUID().uuidString)" let fileURL = docs.appendingPathComponent(fileId) defer { try? FileManager.default.removeItem(at: fileURL) } FileManager.default.createFile(atPath: fileURL.path, contents: "test".data(using: .utf8)) let result = MaterialPathResolver.resolve(materialId: fileId, apiType: "knowledge_source", title: "notes.txt") XCTAssertNotNil(result) XCTAssertEqual(result?.materialType, .text) } func test_resolve_infers_epub_from_title() { let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileId = "test-epub-\(UUID().uuidString)" let fileURL = docs.appendingPathComponent(fileId) defer { try? FileManager.default.removeItem(at: fileURL) } FileManager.default.createFile(atPath: fileURL.path, contents: "test".data(using: .utf8)) let result = MaterialPathResolver.resolve(materialId: fileId, apiType: "temporary_file", title: "book.epub") XCTAssertNotNil(result) XCTAssertEqual(result?.materialType, .epub) } func test_resolve_infers_image_from_title() { let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileId = "test-img-\(UUID().uuidString)" let fileURL = docs.appendingPathComponent(fileId) defer { try? FileManager.default.removeItem(at: fileURL) } FileManager.default.createFile(atPath: fileURL.path, contents: "test".data(using: .utf8)) for ext in ["png", "jpg", "jpeg", "webp"] { let r = MaterialPathResolver.resolve(materialId: fileId, apiType: "knowledge_source", title: "photo.\(ext)") XCTAssertNotNil(r) XCTAssertEqual(r?.materialType, .image, "ext .\(ext) should infer .image") } } func test_resolve_unknown_apiType_defaults_to_unknown() { let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileId = "test-unk-\(UUID().uuidString)" let fileURL = docs.appendingPathComponent(fileId) defer { try? FileManager.default.removeItem(at: fileURL) } FileManager.default.createFile(atPath: fileURL.path, contents: "test".data(using: .utf8)) let result = MaterialPathResolver.resolve(materialId: fileId, apiType: "other_type", title: "file.bin") XCTAssertNotNil(result) XCTAssertEqual(result?.materialType, .unknown) } }