migrate persistence from JSON to SwiftData
Models: - Convert Conversation and ChatMessage to SwiftData @Model classes with a one-to-many relationship and cascade delete. - Rename ChatMessage.id to clientID and drop Conversation.id in favor of jid (the string id would clash with PersistentIdentifier). - Keep MessageReaction as a Codable value stored in an array attribute. Persistence: - Add ArchiveStore, a per-account ModelContainer/ModelContext that replaces the JSON ChatArchive with load/save/erase. - Add ArchiveMetadataRecord for the durable MAM checkpoints, cursor, locally deleted message IDs and roster contact JIDs. - Add LegacyArchiveImporter to import the old JSON snapshot once and delete the file afterwards, preserving existing history. AppModel: - Replace ChatArchive with ArchiveStore and persist via context.save(); insert new models and delete the replaced object on upsert merges so SwiftData never keeps an orphaned duplicate. Tests: - Replace ChatArchiveTests with ArchiveStoreTests (store round-trip and legacy import) and drop the now-obsolete Codable round-trip assertions.
This commit is contained in:
1 parent
0317277d46
commit
cbb820a86e
30 files changed
+726
-665
No files matched your search
@@ -3,7 +3,7 @@ import XCTest
|
||||
@testable import Luma
|
||||
|
||||
final class MessageInteractionTests: XCTestCase {
|
||||
func testReplyMetadataRoundTrips() throws {
|
||||
func testReplyMetadataIsStored() {
|
||||
let message = ChatMessage(
|
||||
conversationID: "bob@example.org",
|
||||
senderJID: "alice@example.org",
|
||||
@@ -16,12 +16,9 @@ final class MessageInteractionTests: XCTestCase {
|
||||
replyPreview: "Исходный текст"
|
||||
)
|
||||
|
||||
let data = try JSONEncoder().encode(message)
|
||||
let decoded = try JSONDecoder().decode(ChatMessage.self, from: data)
|
||||
|
||||
XCTAssertEqual(decoded.replyToID, "original-id")
|
||||
XCTAssertEqual(decoded.replyToJID, "bob@example.org")
|
||||
XCTAssertEqual(decoded.replyPreview, "Исходный текст")
|
||||
XCTAssertEqual(message.replyToID, "original-id")
|
||||
XCTAssertEqual(message.replyToJID, "bob@example.org")
|
||||
XCTAssertEqual(message.replyPreview, "Исходный текст")
|
||||
}
|
||||
|
||||
func testRetractedMessageCannotBeEditedRepliedOrForwarded() {
|
||||
@@ -42,35 +39,6 @@ final class MessageInteractionTests: XCTestCase {
|
||||
XCTAssertEqual(message.previewText, "🚫 Сообщение удалено")
|
||||
}
|
||||
|
||||
func testLegacyMessageWithoutInteractionFieldsStillDecodes() throws {
|
||||
let original = ChatMessage(
|
||||
conversationID: "bob@example.org",
|
||||
senderJID: "alice@example.org",
|
||||
body: "Старый архив",
|
||||
direction: .incoming,
|
||||
delivery: .delivered,
|
||||
security: .omemo
|
||||
)
|
||||
let encoded = try JSONEncoder().encode(original)
|
||||
var object = try XCTUnwrap(JSONSerialization.jsonObject(with: encoded) as? [String: Any])
|
||||
for key in [
|
||||
"replyToID", "replyToJID", "replyPreview", "forwardedFrom", "retractedAt",
|
||||
"stanzaID", "senderDisplayName", "isGroupMessage", "callHistory"
|
||||
] {
|
||||
object.removeValue(forKey: key)
|
||||
}
|
||||
|
||||
let decoded = try JSONDecoder().decode(
|
||||
ChatMessage.self,
|
||||
from: JSONSerialization.data(withJSONObject: object)
|
||||
)
|
||||
|
||||
XCTAssertNil(decoded.replyToID)
|
||||
XCTAssertNil(decoded.retractedAt)
|
||||
XCTAssertEqual(decoded.body, "Старый архив")
|
||||
XCTAssertFalse(decoded.isGroupMessage)
|
||||
}
|
||||
|
||||
func testGroupReplyRequiresRoomAssignedStanzaID() {
|
||||
var message = ChatMessage(
|
||||
conversationID: "team@conference.example.org",
|
||||
@@ -92,7 +60,7 @@ final class MessageInteractionTests: XCTestCase {
|
||||
XCTAssertFalse(message.canBeRetracted)
|
||||
}
|
||||
|
||||
func testCompletedVideoCallHistoryRoundTripsWithDuration() throws {
|
||||
func testCompletedVideoCallHistoryShowsMetadata() {
|
||||
let metadata = CallHistoryMetadata(isVideo: true, outcome: .completed)
|
||||
let message = ChatMessage(
|
||||
id: "call-alice-123",
|
||||
@@ -107,14 +75,11 @@ final class MessageInteractionTests: XCTestCase {
|
||||
callHistory: metadata
|
||||
)
|
||||
|
||||
let data = try JSONEncoder().encode(message)
|
||||
let decoded = try JSONDecoder().decode(ChatMessage.self, from: data)
|
||||
|
||||
XCTAssertEqual(decoded.callHistory, metadata)
|
||||
XCTAssertEqual(decoded.callTitle, "Исходящий видеозвонок")
|
||||
XCTAssertEqual(decoded.callSubtitle, "Длительность 1:05")
|
||||
XCTAssertEqual(decoded.previewText, "🎥 Исходящий видеозвонок")
|
||||
XCTAssertFalse(decoded.canBeRepliedTo)
|
||||
XCTAssertFalse(decoded.canBeForwarded)
|
||||
XCTAssertEqual(message.callHistory, metadata)
|
||||
XCTAssertEqual(message.callTitle, "Исходящий видеозвонок")
|
||||
XCTAssertEqual(message.callSubtitle, "Длительность 1:05")
|
||||
XCTAssertEqual(message.previewText, "🎥 Исходящий видеозвонок")
|
||||
XCTAssertFalse(message.canBeRepliedTo)
|
||||
XCTAssertFalse(message.canBeForwarded)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user