mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-04-29 10:52:47 -04:00
258 lines
8.8 KiB
Swift
258 lines
8.8 KiB
Swift
import XCTest
|
||
|
||
@testable import SpacedriveClient
|
||
|
||
final class SerializationTests: XCTestCase {
|
||
|
||
func testLibraryCreateInputSerialization() throws {
|
||
// Test that Swift types serialize to JSON correctly
|
||
let input = LibraryCreateInput(name: "Test Library", path: "/test/path")
|
||
|
||
// Serialize to JSON
|
||
let jsonData = try JSONEncoder().encode(input)
|
||
let jsonString = String(data: jsonData, encoding: .utf8)!
|
||
|
||
print("LibraryCreateInput JSON: \(jsonString)")
|
||
|
||
// Verify JSON structure matches what Rust expects
|
||
let jsonObject = try JSONSerialization.jsonObject(with: jsonData) as! [String: Any]
|
||
XCTAssertEqual(jsonObject["name"] as? String, "Test Library")
|
||
XCTAssertEqual(jsonObject["path"] as? String, "/test/path")
|
||
|
||
// Test round-trip serialization
|
||
let decoded = try JSONDecoder().decode(LibraryCreateInput.self, from: jsonData)
|
||
XCTAssertEqual(decoded.name, input.name)
|
||
XCTAssertEqual(decoded.path, input.path)
|
||
}
|
||
|
||
func testLibraryCreateOutputDeserialization() throws {
|
||
// Test that we can deserialize JSON from daemon into Swift types
|
||
let jsonString = """
|
||
{
|
||
"libraryId": "123e4567-e89b-12d3-a456-426614174000",
|
||
"name": "Test Library",
|
||
"path": "/test/path"
|
||
}
|
||
"""
|
||
|
||
let jsonData = jsonString.data(using: .utf8)!
|
||
let output = try JSONDecoder().decode(LibraryCreateOutput.self, from: jsonData)
|
||
|
||
XCTAssertEqual(output.libraryId, "123e4567-e89b-12d3-a456-426614174000")
|
||
XCTAssertEqual(output.name, "Test Library")
|
||
XCTAssertEqual(output.path, "/test/path")
|
||
|
||
print("LibraryCreateOutput deserialized successfully: \(output)")
|
||
}
|
||
|
||
func testUnionTypeSerialization() throws {
|
||
// Test union types (enums with associated values)
|
||
let physicalPath = SdPath.physical(
|
||
SdPathPhysicalData(deviceId: "device-123", path: "/test/file.txt"))
|
||
let contentPath = SdPath.content(SdPathContentData(contentId: "content-456"))
|
||
|
||
// Test physical path serialization
|
||
let physicalData = try JSONEncoder().encode(physicalPath)
|
||
let physicalJson = String(data: physicalData, encoding: .utf8)!
|
||
print("Physical SdPath JSON: \(physicalJson)")
|
||
|
||
// Test content path serialization
|
||
let contentData = try JSONEncoder().encode(contentPath)
|
||
let contentJson = String(data: contentData, encoding: .utf8)!
|
||
print("Content SdPath JSON: \(contentJson)")
|
||
|
||
// Test round-trip
|
||
let decodedPhysical = try JSONDecoder().decode(SdPath.self, from: physicalData)
|
||
let decodedContent = try JSONDecoder().decode(SdPath.self, from: contentData)
|
||
|
||
// Verify the decoded values match
|
||
switch decodedPhysical {
|
||
case .physical(let data):
|
||
XCTAssertEqual(data.deviceId, "device-123")
|
||
XCTAssertEqual(data.path, "/test/file.txt")
|
||
case .content:
|
||
XCTFail("Expected physical path")
|
||
}
|
||
|
||
switch decodedContent {
|
||
case .content(let data):
|
||
XCTAssertEqual(data.contentId, "content-456")
|
||
case .physical:
|
||
XCTFail("Expected content path")
|
||
}
|
||
}
|
||
|
||
func testJobStatusSerialization() throws {
|
||
// Test simple enum serialization
|
||
let statuses: [JobStatus] = [.queued, .running, .completed, .failed]
|
||
|
||
for status in statuses {
|
||
let data = try JSONEncoder().encode(status)
|
||
let json = String(data: data, encoding: .utf8)!
|
||
let decoded = try JSONDecoder().decode(JobStatus.self, from: data)
|
||
|
||
print("JobStatus \(status) → JSON: \(json)")
|
||
XCTAssertEqual(decoded, status)
|
||
}
|
||
}
|
||
|
||
func testJobOutputSerialization() throws {
|
||
// Test complex enum with associated values
|
||
let indexedOutput = JobOutput.indexed(
|
||
JobOutputIndexedData(
|
||
stats: IndexerStats(
|
||
files: 100, dirs: 10, bytes: 1_024_000, symlinks: 5, skipped: 2, errors: 0),
|
||
metrics: IndexerMetrics(
|
||
totalDuration: 30.5,
|
||
discoveryDuration: 5.0,
|
||
processingDuration: 20.0,
|
||
contentDuration: 5.5,
|
||
filesPerSecond: 3.33,
|
||
bytesPerSecond: 34133.33,
|
||
dirsPerSecond: 0.33,
|
||
dbWrites: 110,
|
||
dbReads: 50,
|
||
batchCount: 5,
|
||
avgBatchSize: 20.0,
|
||
totalErrors: 0,
|
||
criticalErrors: 0,
|
||
nonCriticalErrors: 0,
|
||
skippedPaths: 2,
|
||
peakMemoryBytes: 1_048_576,
|
||
avgMemoryBytes: 524288
|
||
)
|
||
))
|
||
|
||
// Test serialization
|
||
let data = try JSONEncoder().encode(indexedOutput)
|
||
let json = String(data: data, encoding: .utf8)!
|
||
print("Complex JobOutput JSON: \(json)")
|
||
|
||
// Test round-trip
|
||
let decoded = try JSONDecoder().decode(JobOutput.self, from: data)
|
||
|
||
switch decoded {
|
||
case .indexed(let data):
|
||
XCTAssertEqual(data.stats.files, 100)
|
||
XCTAssertEqual(data.metrics.filesPerSecond, 3.33, accuracy: 0.01)
|
||
default:
|
||
XCTFail("Expected indexed output")
|
||
}
|
||
}
|
||
|
||
func testFileSystemEnumSerialization() throws {
|
||
// Test enum with associated values
|
||
let apfs = FileSystem.aPFS
|
||
let other = FileSystem.other("custom-fs")
|
||
|
||
// Test simple variant
|
||
let apfsData = try JSONEncoder().encode(apfs)
|
||
let apfsJson = String(data: apfsData, encoding: .utf8)!
|
||
print("FileSystem.apfs JSON: \(apfsJson)")
|
||
|
||
// Test variant with associated value
|
||
let otherData = try JSONEncoder().encode(other)
|
||
let otherJson = String(data: otherData, encoding: .utf8)!
|
||
print("FileSystem.other JSON: \(otherJson)")
|
||
|
||
// Test round-trip
|
||
let decodedApfs = try JSONDecoder().decode(FileSystem.self, from: apfsData)
|
||
let decodedOther = try JSONDecoder().decode(FileSystem.self, from: otherData)
|
||
|
||
// XCTAssertEqual(decodedApfs, .apfs) // TODO: Add Equatable to generated enums
|
||
switch decodedOther {
|
||
case .other(let fs):
|
||
XCTAssertEqual(fs, "custom-fs")
|
||
default:
|
||
XCTFail("Expected other filesystem")
|
||
}
|
||
}
|
||
|
||
func testRealDaemonIntegration() async throws {
|
||
// Skip if daemon is not running
|
||
let socketPath =
|
||
"\(NSHomeDirectory())/Library/Application Support/spacedrive/daemon/daemon.sock"
|
||
|
||
guard FileManager.default.fileExists(atPath: socketPath) else {
|
||
throw XCTSkip("Daemon not running - skipping integration test")
|
||
}
|
||
|
||
let client = SpacedriveClient(socketPath: socketPath)
|
||
|
||
// Test real API call with generated types
|
||
do {
|
||
let libraries = try await client.executeQuery(
|
||
LibraryListQuery(),
|
||
method: "query:libraries.list",
|
||
responseType: [LibraryInfo].self
|
||
)
|
||
|
||
print("Real daemon integration successful - found \(libraries.count) libraries")
|
||
|
||
// If we have libraries, test job list with generated types
|
||
if !libraries.isEmpty {
|
||
let jobsResponse = try await client.executeQuery(
|
||
JobListQuery(),
|
||
method: "query:jobs.list",
|
||
responseType: JobListOutput.self
|
||
)
|
||
|
||
print("Jobs query successful - found \(jobsResponse.jobs.count) jobs")
|
||
|
||
// Verify the types match our generated Swift types
|
||
for job in jobsResponse.jobs {
|
||
XCTAssertFalse(job.id.isEmpty)
|
||
XCTAssertFalse(job.name.isEmpty)
|
||
// job.status should be a JobStatus enum value
|
||
print(" Job: \(job.name) (\(job.status)) - \(Int(job.progress * 100))%")
|
||
}
|
||
}
|
||
|
||
} catch {
|
||
print("️ Daemon integration failed: \(error)")
|
||
// Don't fail the test - daemon might not have libraries
|
||
}
|
||
}
|
||
}
|
||
|
||
// Helper types for testing (these should eventually be generated too)
|
||
struct LibraryListQuery: Codable {
|
||
let include_stats: Bool
|
||
|
||
init() {
|
||
self.include_stats = false
|
||
}
|
||
}
|
||
|
||
struct JobListQuery: Codable {
|
||
let status: String?
|
||
|
||
init() {
|
||
self.status = nil
|
||
}
|
||
}
|
||
|
||
struct LibraryInfo: Codable {
|
||
let id: String
|
||
let name: String
|
||
let path: String
|
||
let stats: LibraryStatistics?
|
||
}
|
||
|
||
struct LibraryStatistics: Codable {
|
||
let total_files: UInt64
|
||
let total_size: UInt64
|
||
let location_count: UInt32
|
||
}
|
||
|
||
struct JobListOutput: Codable {
|
||
let jobs: [JobListItem]
|
||
}
|
||
|
||
struct JobListItem: Codable {
|
||
let id: String
|
||
let name: String
|
||
let status: String
|
||
let progress: Float
|
||
}
|