Multi-device: - Add DeviceResource with a stable per-install UUID-based resource (Luma-<hex>) persisted in UserDefaults and migrated from legacy "Luma". - AccountConfiguration.effectiveResource falls back to DeviceResource.default for a blank or legacy resource; LoginView defaults resource to "" with an auto placeholder. - Prevents two devices from sharing the same full JID resource and kicking each other off the stream (the "xmpp stream error" conflict). MAM / history: - loadOlderHistory now always issues the RSM page request instead of deferring, so scrolling up reliably loads older messages. - Track interactive history mutations separately so live messages are not dropped while an older-history page is decoding. - ChatView: replace the unreliable GeometryReader/preference scroll trigger with onAppear/onDisappear on a top sentinel; auto-load the first page when the conversation is empty. - AppModel: reset hasMoreOlderHistory for empty conversations and clear isLoadingOlderHistory on disconnect. - Decode own/duplicate/notEncrypted messages to decryption-failed/plaintext placeholders instead of dropping them, so every MAM stanza is collected. OMEMO: - Remove the encrypt-to-self setup that built a Signal session with the local device (cryptographically invalid), which caused an endless "Bad MAC" loop and a stream of undecryptable messages in the self-chat. - Add LumaOMEMOStore.removeSessionWithOwnDevice() and call it on connect to purge any self-session persisted by the previous build. Tests: - Update AccountConfigurationTests for the unique resource and add coverage for legacy-resource migration and explicit-resource preservation.
55 lines
1.9 KiB
Swift
55 lines
1.9 KiB
Swift
import XCTest
|
|
@testable import Luma
|
|
|
|
final class AccountConfigurationTests: XCTestCase {
|
|
func testValidAccountIsNormalized() throws {
|
|
let account = AccountConfiguration(
|
|
jid: " Alice@Example.ORG ",
|
|
resource: "",
|
|
manualHost: " xmpp.example.org ",
|
|
manualPort: 5222
|
|
)
|
|
|
|
let validated = try account.validated()
|
|
|
|
XCTAssertEqual(validated.normalizedJID, "alice@example.org")
|
|
XCTAssertEqual(validated.domain, "example.org")
|
|
XCTAssertTrue(validated.effectiveResource.hasPrefix("Luma-"))
|
|
XCTAssertNotEqual(validated.effectiveResource, DeviceResource.legacyDefault)
|
|
XCTAssertEqual(validated.effectiveResource, DeviceResource.default)
|
|
XCTAssertEqual(validated.manualHost, "xmpp.example.org")
|
|
}
|
|
|
|
func testLegacyDefaultResourceIsMigratedToAUniqueDeviceResource() throws {
|
|
let account = try AccountConfiguration(
|
|
jid: "alice@example.org",
|
|
resource: "Luma"
|
|
).validated()
|
|
|
|
XCTAssertTrue(account.effectiveResource.hasPrefix("Luma-"))
|
|
XCTAssertNotEqual(account.effectiveResource, "Luma")
|
|
}
|
|
|
|
func testExplicitResourceIsPreserved() throws {
|
|
let account = try AccountConfiguration(
|
|
jid: "alice@example.org",
|
|
resource: " MyDevice "
|
|
).validated()
|
|
|
|
XCTAssertEqual(account.effectiveResource, "MyDevice")
|
|
}
|
|
|
|
func testInvalidJIDIsRejected() {
|
|
XCTAssertThrowsError(try AccountConfiguration(jid: "not-a-jid").validated())
|
|
XCTAssertThrowsError(try AccountConfiguration(jid: "@example.org").validated())
|
|
XCTAssertThrowsError(try AccountConfiguration(jid: "alice@").validated())
|
|
}
|
|
|
|
func testInvalidPortIsRejected() {
|
|
XCTAssertThrowsError(
|
|
try AccountConfiguration(jid: "alice@example.org", manualPort: 70_000).validated()
|
|
)
|
|
}
|
|
}
|
|
|