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
@@ -4,31 +4,32 @@ import XCTest
|
||||
|
||||
final class MessageCorrectionTests: XCTestCase {
|
||||
func testOnlyDeliveredOrSentOutgoingTextCanBeEdited() {
|
||||
let editable = ChatMessage(
|
||||
conversationID: "bob@example.org",
|
||||
senderJID: "alice@example.org",
|
||||
body: "До исправления",
|
||||
direction: .outgoing,
|
||||
delivery: .sent,
|
||||
security: .omemo
|
||||
)
|
||||
var incoming = editable
|
||||
incoming.direction = .incoming
|
||||
var attachment = editable
|
||||
attachment.kind = .attachment
|
||||
var failed = editable
|
||||
failed.delivery = .failed
|
||||
var retracted = editable
|
||||
retracted.retractedAt = Date()
|
||||
func make(
|
||||
direction: ChatMessage.Direction = .outgoing,
|
||||
delivery: ChatMessage.Delivery = .sent,
|
||||
kind: ChatMessage.Kind = .text,
|
||||
retractedAt: Date? = nil
|
||||
) -> ChatMessage {
|
||||
ChatMessage(
|
||||
conversationID: "bob@example.org",
|
||||
senderJID: "alice@example.org",
|
||||
body: "До исправления",
|
||||
direction: direction,
|
||||
delivery: delivery,
|
||||
security: .omemo,
|
||||
kind: kind,
|
||||
retractedAt: retractedAt
|
||||
)
|
||||
}
|
||||
|
||||
XCTAssertTrue(editable.canBeEdited)
|
||||
XCTAssertFalse(incoming.canBeEdited)
|
||||
XCTAssertFalse(attachment.canBeEdited)
|
||||
XCTAssertFalse(failed.canBeEdited)
|
||||
XCTAssertFalse(retracted.canBeEdited)
|
||||
XCTAssertTrue(make().canBeEdited)
|
||||
XCTAssertFalse(make(direction: .incoming).canBeEdited)
|
||||
XCTAssertFalse(make(kind: .attachment).canBeEdited)
|
||||
XCTAssertFalse(make(delivery: .failed).canBeEdited)
|
||||
XCTAssertFalse(make(retractedAt: Date()).canBeEdited)
|
||||
}
|
||||
|
||||
func testEditedTimestampRoundTrips() throws {
|
||||
func testEditedTimestampIsStored() {
|
||||
let editedAt = Date(timeIntervalSince1970: 1_700_000_000)
|
||||
let message = ChatMessage(
|
||||
conversationID: "bob@example.org",
|
||||
@@ -40,30 +41,6 @@ final class MessageCorrectionTests: XCTestCase {
|
||||
editedAt: editedAt
|
||||
)
|
||||
|
||||
let data = try JSONEncoder().encode(message)
|
||||
let decoded = try JSONDecoder().decode(ChatMessage.self, from: data)
|
||||
|
||||
XCTAssertEqual(decoded.editedAt, editedAt)
|
||||
}
|
||||
|
||||
func testLegacyMessageWithoutEditedTimestampStillDecodes() throws {
|
||||
let message = ChatMessage(
|
||||
conversationID: "bob@example.org",
|
||||
senderJID: "alice@example.org",
|
||||
body: "Старое сообщение",
|
||||
direction: .incoming,
|
||||
delivery: .delivered,
|
||||
security: .omemo
|
||||
)
|
||||
let encoded = try JSONEncoder().encode(message)
|
||||
var object = try XCTUnwrap(JSONSerialization.jsonObject(with: encoded) as? [String: Any])
|
||||
object.removeValue(forKey: "editedAt")
|
||||
|
||||
let decoded = try JSONDecoder().decode(
|
||||
ChatMessage.self,
|
||||
from: JSONSerialization.data(withJSONObject: object)
|
||||
)
|
||||
|
||||
XCTAssertNil(decoded.editedAt)
|
||||
XCTAssertEqual(message.editedAt, editedAt)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user