Files
session-ios/SessionMessagingKitTests/Jobs/DisplayPictureDownloadJobSpec.swift

1132 lines
57 KiB
Swift

// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import GRDB
import Quick
import Nimble
@testable import SessionNetworkingKit
@testable import SessionMessagingKit
@testable import SessionUtilitiesKit
class DisplayPictureDownloadJobSpec: AsyncSpec {
override class func spec() {
// MARK: Configuration
@TestState var job: Job!
@TestState var profile: Profile!
@TestState var group: ClosedGroup!
@TestState var community: OpenGroup!
@TestState var dependencies: TestDependencies! = TestDependencies { dependencies in
dependencies.forceSynchronous = true
dependencies.dateNow = Date(timeIntervalSince1970: 1234567890)
}
@TestState(cache: .libSession, in: dependencies) var mockLibSessionCache: MockLibSessionCache! = MockLibSessionCache(
initialSetup: {
$0.defaultInitialSetup()
$0.when { $0.profile(contactId: .any, threadId: .any, threadVariant: .any, visibleMessage: .any) }
.thenReturn(nil)
}
)
@TestState(singleton: .storage, in: dependencies) var mockStorage: Storage! = SynchronousStorage(
customWriter: try! DatabaseQueue(),
migrations: SNMessagingKit.migrations,
using: dependencies,
initialData: { db in
try Identity(variant: .x25519PublicKey, data: Data(hex: TestConstants.publicKey)).insert(db)
try Identity(variant: .x25519PrivateKey, data: Data(hex: TestConstants.privateKey)).insert(db)
try Identity(variant: .ed25519PublicKey, data: Data(hex: TestConstants.edPublicKey)).insert(db)
try Identity(variant: .ed25519SecretKey, data: Data(hex: TestConstants.edSecretKey)).insert(db)
}
)
@TestState var encryptionKey: Data! = Data(hex: "c8e52eb1016702a663ac9a1ab5522daa128ab40762a514de271eddf598e3b8d4")
@TestState var encryptedData: Data! = Data(
hex: "778921bdd0e432227b53ee49c23421aeb796b7e5663468ff79daffb1af08cd1" +
"a68343377fe05ab01917ce0fb8732c746a60f157f7798cdf999364b37ff9016ab2fe" +
"673120e153a5cb6b869380744d493068ebc418266d6596d728cfc60b30662a089376" +
"f2761e3bb6ee837a26b24b5"
)
@TestState(singleton: .network, in: dependencies) var mockNetwork: MockNetwork! = MockNetwork(
initialSetup: { network in
network
.when {
$0.send(
endpoint: MockEndpoint.any,
destination: .any,
body: .any,
requestTimeout: .any,
requestAndPathBuildTimeout: .any
)
}
.thenReturn(MockNetwork.response(data: encryptedData))
}
)
@TestState(singleton: .fileManager, in: dependencies) var mockFileManager: MockFileManager! = MockFileManager(
initialSetup: { $0.defaultInitialSetup() }
)
@TestState(singleton: .crypto, in: dependencies) var mockCrypto: MockCrypto! = MockCrypto(
initialSetup: { crypto in
crypto.when { $0.generate(.uuid()) }.thenReturn(UUID(uuidString: "00000000-0000-0000-0000-000000001234"))
crypto
.when { $0.generate(.legacyDecryptedDisplayPicture(data: .any, key: .any)) }
.thenReturn(TestConstants.validImageData)
crypto.when { $0.generate(.hash(message: .any, length: .any)) }.thenReturn("TestHash".bytes)
crypto
.when { $0.generate(.blinded15KeyPair(serverPublicKey: .any, ed25519SecretKey: .any)) }
.thenReturn(
KeyPair(
publicKey: Data(hex: TestConstants.publicKey).bytes,
secretKey: Data(hex: TestConstants.edSecretKey).bytes
)
)
crypto
.when { $0.generate(.randomBytes(16)) }
.thenReturn(Data(base64Encoded: "pK6YRtQApl4NhECGizF0Cg==")!.bytes)
crypto
.when { $0.generate(.signatureBlind15(message: .any, serverPublicKey: .any, ed25519SecretKey: .any)) }
.thenReturn("TestSogsSignature".bytes)
crypto
.when { $0.generate(.x25519(ed25519Pubkey: .any)) }
.thenReturn(Array(Data(hex: TestConstants.serverPublicKey)))
}
)
@TestState(singleton: .imageDataManager, in: dependencies) var mockImageDataManager: MockImageDataManager! = MockImageDataManager(
initialSetup: { imageDataManager in
imageDataManager
.when { await $0.load(.any) }
.thenReturn(nil)
imageDataManager
.when { await $0.removeImage(identifier: .any) }
.thenReturn(())
}
)
@TestState(cache: .general, in: dependencies) var mockGeneralCache: MockGeneralCache! = MockGeneralCache(
initialSetup: { cache in
cache.when { $0.sessionId }.thenReturn(SessionId(.standard, hex: TestConstants.publicKey))
cache.when { $0.ed25519SecretKey }.thenReturn(Array(Data(hex: TestConstants.edSecretKey)))
cache
.when { $0.ed25519Seed }
.thenReturn(Array(Array(Data(hex: TestConstants.edSecretKey)).prefix(upTo: 32)))
}
)
// MARK: - a DisplayPictureDownloadJob
describe("a DisplayPictureDownloadJob") {
// MARK: -- fails when not given any details
it("fails when not given any details") {
job = Job(variant: .displayPictureDownload)
var error: Error? = nil
var permanentFailure: Bool = false
DisplayPictureDownloadJob.run(
job,
scheduler: DispatchQueue.main,
success: { _, _ in },
failure: { _, runError, runPermanentFailure in
error = runError
permanentFailure = runPermanentFailure
},
deferred: { _ in },
using: dependencies
)
await expect(error).toEventually(matchError(JobRunnerError.missingRequiredDetails))
expect(permanentFailure).to(beTrue())
}
// MARK: -- when initialising details
context("when initialising details") {
// MARK: ---- for a profile
context("for a profile") {
// MARK: ------ with a target
context("with a target") {
// MARK: -------- returns nil when given an empty url
it("returns nil when given an empty url") {
expect(
DisplayPictureDownloadJob.Details(
target: .profile(id: "", url: "", encryptionKey: Data()),
timestamp: 0
)
).to(beNil())
}
// MARK: -------- returns nil when given a url which does not have a file id
it("returns nil when given a url which does not have a file id") {
expect(
DisplayPictureDownloadJob.Details(
target: .profile(id: "", url: "http://oxen.io", encryptionKey: Data()),
timestamp: 0
)
).to(beNil())
}
// MARK: -------- returns nil when given a url which does not have a file id
it("returns nil when given a url which does not have a file id") {
expect(
DisplayPictureDownloadJob.Details(
target: .profile(id: "", url: "http://oxen.io", encryptionKey: Data()),
timestamp: 0
)
).to(beNil())
}
// MARK: -------- returns nil when given encryption key data with the wrong length
it("returns nil when given encryption key data with the wrong length") {
expect(
DisplayPictureDownloadJob.Details(
target: .profile(id: "", url: "http://oxen.io/1234/", encryptionKey: Data([1, 2, 3])),
timestamp: 0
)
).to(beNil())
}
// MARK: -------- returns a value when given valid data
it("returns a value when given valid data") {
expect(
DisplayPictureDownloadJob.Details(
target: .profile(
id: "",
url: "http://oxen.io/1234/",
encryptionKey: encryptionKey
),
timestamp: 0
)
).toNot(beNil())
}
}
}
// MARK: ---- for a group
context("for a group") {
// MARK: ------ with a target
context("with a target") {
// MARK: -------- returns nil when given an empty url
it("returns nil when given an empty url") {
expect(
DisplayPictureDownloadJob.Details(
target: .group(id: "", url: "", encryptionKey: Data()),
timestamp: 0
)
).to(beNil())
}
// MARK: -------- returns nil when given a url which does not have a file id
it("returns nil when given a url which does not have a file id") {
expect(
DisplayPictureDownloadJob.Details(
target: .group(id: "", url: "http://oxen.io", encryptionKey: Data()),
timestamp: 0
)
).to(beNil())
}
// MARK: -------- returns nil when given a url which does not have a file id
it("returns nil when given a url which does not have a file id") {
expect(
DisplayPictureDownloadJob.Details(
target: .group(id: "", url: "http://oxen.io", encryptionKey: Data()),
timestamp: 0
)
).to(beNil())
}
// MARK: -------- returns nil when given encryption key data with the wrong length
it("returns nil when given encryption key data with the wrong length") {
expect(
DisplayPictureDownloadJob.Details(
target: .group(id: "", url: "http://oxen.io/1234/", encryptionKey: Data([1, 2, 3])),
timestamp: 0
)
).to(beNil())
}
// MARK: -------- returns a value when given valid data
it("returns a value when given valid data") {
expect(
DisplayPictureDownloadJob.Details(
target: .group(
id: "",
url: "http://oxen.io/1234/",
encryptionKey: encryptionKey
),
timestamp: 0
)
).toNot(beNil())
}
}
}
// MARK: ---- for a community
context("for a community") {
// MARK: ------ with a target
context("with a target") {
// MARK: -------- returns nil when given an empty imageId
it("returns nil when given an empty imageId") {
expect(
DisplayPictureDownloadJob.Details(
target: .community(imageId: "", roomToken: "", server: ""),
timestamp: 0
)
).to(beNil())
}
// MARK: -------- returns a value when given valid data
it("returns a value when given valid data") {
expect(
DisplayPictureDownloadJob.Details(
target: .community(imageId: "12", roomToken: "", server: ""),
timestamp: 0
)
).toNot(beNil())
}
}
}
}
// MARK: -- when hashing
context("when hashing") {
// MARK: ---- generates the same hash with the same data
it("generates the same hash with the same data") {
expect(
DisplayPictureDownloadJob.Details(
target: .community(imageId: "12", roomToken: "test", server: "test"),
timestamp: 1234
)?.hashValue
).to(equal(
DisplayPictureDownloadJob.Details(
target: .community(imageId: "12", roomToken: "test", server: "test"),
timestamp: 1234
)?.hashValue
))
}
// MARK: ---- generates a different hash with different data
it("generates a different hash with different data") {
expect(
DisplayPictureDownloadJob.Details(
target: .community(imageId: "12", roomToken: "test", server: "test"),
timestamp: 1234
)?.hashValue
).toNot(equal(
DisplayPictureDownloadJob.Details(
target: .community(imageId: "13", roomToken: "test", server: "test"),
timestamp: 1234
)?.hashValue
))
}
// MARK: ---- excludes the timestamp when generating the hash value
it("excludes the timestamp when generating the hash value") {
expect(
DisplayPictureDownloadJob.Details(
target: .community(imageId: "12", roomToken: "test", server: "test"),
timestamp: 1234
)?.hashValue
).to(equal(
DisplayPictureDownloadJob.Details(
target: .community(imageId: "12", roomToken: "test", server: "test"),
timestamp: 4321
)?.hashValue
))
}
}
// MARK: -- generates a FileServer download request correctly
it("generates a FileServer download request correctly") {
profile = Profile(
id: "1234",
name: "test",
nickname: nil,
displayPictureUrl: nil,
displayPictureEncryptionKey: nil,
profileLastUpdated: nil,
blocksCommunityMessageRequests: nil,
proFeatures: .none,
proExpiryUnixTimestampMs: 0,
proGenIndexHashHex: nil
)
mockStorage.write { db in try profile.insert(db) }
job = Job(
variant: .displayPictureDownload,
shouldBeUnique: true,
details: DisplayPictureDownloadJob.Details(
target: .profile(
id: "1234",
url: "http://filev2.getsession.org/file/1234",
encryptionKey: encryptionKey
),
timestamp: 0
)
)
let expectedRequest: Network.PreparedRequest<Data> = try Network.FileServer.preparedDownload(
url: URL(string: "http://filev2.getsession.org/file/1234")!,
using: dependencies
)
var receivedResult: Bool = false
DisplayPictureDownloadJob.run(
job,
scheduler: DispatchQueue.main,
success: { _, _ in receivedResult = true },
failure: { _, _, _ in receivedResult = true },
deferred: { _ in receivedResult = true },
using: dependencies
)
await expect(receivedResult).toEventually(beTrue())
await expect(mockNetwork)
.toEventually(call(.exactly(times: 1), matchingParameters: .all) { network in
network.send(
endpoint: Network.FileServer.Endpoint.directUrl(
URL(string: "http://filev2.getsession.org/file/1234")!
),
destination: expectedRequest.destination,
body: expectedRequest.body,
requestTimeout: expectedRequest.requestTimeout,
requestAndPathBuildTimeout: expectedRequest.requestAndPathBuildTimeout
)
})
}
// MARK: -- generates a SOGS download request correctly
it("generates a SOGS download request correctly") {
mockStorage.write { db in
try OpenGroup(
server: "testServer",
roomToken: "testRoom",
publicKey: TestConstants.serverPublicKey,
shouldPoll: false,
name: "test",
imageId: "12",
userCount: 0,
infoUpdates: 0
).insert(db)
}
job = Job(
variant: .displayPictureDownload,
shouldBeUnique: true,
details: DisplayPictureDownloadJob.Details(
target: .community(
imageId: "12",
roomToken: "testRoom",
server: "testServer"
),
timestamp: 0
)
)
let expectedRequest: Network.PreparedRequest<Data> = mockStorage.read { db in
try Network.SOGS.preparedDownload(
fileId: "12",
roomToken: "testRoom",
authMethod: Authentication.Community(
info: LibSession.OpenGroupCapabilityInfo(
roomToken: "",
server: "testserver",
publicKey: TestConstants.serverPublicKey,
capabilities: []
),
forceBlinded: false
),
using: dependencies
)
}!
var receivedResult: Bool = false
DisplayPictureDownloadJob.run(
job,
scheduler: DispatchQueue.main,
success: { _, _ in receivedResult = true },
failure: { _, _, _ in receivedResult = true },
deferred: { _ in receivedResult = true },
using: dependencies
)
await expect(receivedResult).toEventually(beTrue())
await expect(mockNetwork)
.toEventually(call(.exactly(times: 1), matchingParameters: .all) { network in
network.send(
endpoint: Network.SOGS.Endpoint.roomFileIndividual("testRoom", "12"),
destination: expectedRequest.destination,
body: expectedRequest.body,
requestTimeout: expectedRequest.requestTimeout,
requestAndPathBuildTimeout: expectedRequest.requestAndPathBuildTimeout
)
})
}
// MARK: -- checking if a downloaded display picture is valid
context("checking if a downloaded display picture is valid") {
@TestState var jobResult: JobRunner.JobResult?
beforeEach {
profile = Profile(
id: "1234",
name: "test",
nickname: nil,
displayPictureUrl: nil,
displayPictureEncryptionKey: nil,
profileLastUpdated: nil,
blocksCommunityMessageRequests: nil,
proFeatures: .none,
proExpiryUnixTimestampMs: 0,
proGenIndexHashHex: nil
)
mockStorage.write { db in try profile.insert(db) }
job = Job(
variant: .displayPictureDownload,
shouldBeUnique: true,
details: DisplayPictureDownloadJob.Details(
target: .profile(
id: "1234",
url: "http://oxen.io/100/",
encryptionKey: encryptionKey
),
timestamp: 1234567891
)
)
}
justBeforeEach {
DisplayPictureDownloadJob.run(
job,
scheduler: DispatchQueue.main,
success: { _, _ in jobResult = .succeeded },
failure: { _, error, permanent in jobResult = .failed(error, permanent) },
deferred: { _ in jobResult = .deferred },
using: dependencies
)
await expect(jobResult).toEventuallyNot(beNil())
}
// MARK: ---- when it fails to decrypt the data
context("when it fails to decrypt the data") {
beforeEach {
mockCrypto
.when { $0.generate(.legacyDecryptedDisplayPicture(data: .any, key: .any)) }
.thenReturn(nil)
}
// MARK: ------ does not save the picture
it("does not save the picture") {
expect(mockFileManager)
.toNot(call { $0.createFile(atPath: .any, contents: .any, attributes: .any) })
await expect(mockImageDataManager).toEventuallyNot(call {
await $0.load(.any)
})
expect(mockStorage.read { db in try Profile.fetchOne(db) }).to(equal(profile))
}
}
// MARK: ---- when it decrypts invalid image data
context("when it decrypts invalid image data") {
beforeEach {
mockCrypto
.when { $0.generate(.legacyDecryptedDisplayPicture(data: .any, key: .any)) }
.thenReturn(TestConstants.invalidImageData)
}
// MARK: ------ does not save the picture
it("does not save the picture") {
expect(mockFileManager)
.toNot(call { $0.createFile(atPath: .any, contents: .any, attributes: .any) })
await expect(mockImageDataManager).toEventuallyNot(call {
await $0.load(.any)
})
expect(mockStorage.read { db in try Profile.fetchOne(db) }).to(equal(profile))
}
}
// MARK: ---- when it fails to write to disk
context("when it fails to write to disk") {
beforeEach {
mockFileManager
.when { $0.createFile(atPath: .any, contents: .any, attributes: .any) }
.thenReturn(false)
}
// MARK: ------ does not save the picture
it("does not save the picture") {
await expect(mockImageDataManager).toEventuallyNot(call {
await $0.load(.any)
})
expect(mockStorage.read { db in try Profile.fetchOne(db) }).to(equal(profile))
}
}
// MARK: ---- writes the file to disk
it("writes the file to disk") {
expect(mockFileManager)
.to(call(.exactly(times: 1), matchingParameters: .all) { mockFileManager in
mockFileManager.createFile(
atPath: "/test/DisplayPictures/5465737448617368",
contents: TestConstants.validImageData,
attributes: nil
)
})
}
// MARK: ---- adds the image data to the displayPicture cache
it("adds the image data to the displayPicture cache") {
await expect(mockImageDataManager)
.toEventually(call(.exactly(times: 1), matchingParameters: .all) {
await $0.load(
.url(URL(fileURLWithPath: "/test/DisplayPictures/5465737448617368"))
)
})
}
// MARK: ---- successfully completes the job
it("successfully completes the job") {
expect(jobResult).to(equal(.succeeded))
}
// MARK: ---- for a profile
context("for a profile") {
beforeEach {
profile = Profile(
id: "1234",
name: "test",
nickname: nil,
displayPictureUrl: "http://oxen.io/100/",
displayPictureEncryptionKey: encryptionKey,
profileLastUpdated: 1234567890,
blocksCommunityMessageRequests: nil,
proFeatures: .none,
proExpiryUnixTimestampMs: 0,
proGenIndexHashHex: nil
)
mockStorage.write { db in
_ = try Profile.deleteAll(db)
try profile.insert(db)
}
job = Job(
variant: .displayPictureDownload,
shouldBeUnique: true,
details: DisplayPictureDownloadJob.Details(
target: .profile(
id: "1234",
url: "http://oxen.io/100/",
encryptionKey: encryptionKey
),
timestamp: 1234567891
)
)
}
// MARK: ------ that does not exist
context("that does not exist") {
beforeEach {
mockStorage.write { db in try Profile.deleteAll(db) }
}
// MARK: -------- does not save the picture
it("does not save the picture") {
/// Succeeds as the download has been superseded
await expect(jobResult).toEventually(equal(.succeeded))
expect(mockCrypto)
.toNot(call {
$0.generate(.legacyDecryptedDisplayPicture(data: .any, key: .any))
})
expect(mockFileManager)
.toNot(call { $0.createFile(atPath: .any, contents: .any, attributes: .any) })
await expect(mockImageDataManager).toEventuallyNot(call {
await $0.load(.any)
})
expect(mockStorage.read { db in try Profile.fetchOne(db) }).to(beNil())
}
}
// MARK: ------ that has a different encryption key and more recent update
context("that has a different encryption key and more recent update") {
beforeEach {
mockStorage.write { db in
try Profile
.updateAll(
db,
Profile.Columns.displayPictureEncryptionKey.set(to: Data([1, 2, 3])),
Profile.Columns.profileLastUpdated.set(to: 9999999999)
)
}
}
// MARK: -------- does not save the picture
it("does not save the picture") {
expect(mockCrypto)
.toNot(call {
$0.generate(.legacyDecryptedDisplayPicture(data: .any, key: .any))
})
expect(mockFileManager)
.toNot(call { $0.createFile(atPath: .any, contents: .any, attributes: .any) })
await expect(mockImageDataManager).toEventuallyNot(call {
await $0.load(.any)
})
expect(mockStorage.read { db in try Profile.fetchOne(db) })
.toNot(equal(
Profile(
id: "1234",
name: "test",
nickname: nil,
displayPictureUrl: "http://oxen.io/100/",
displayPictureEncryptionKey: encryptionKey,
profileLastUpdated: 1234567891,
blocksCommunityMessageRequests: nil,
proFeatures: .none,
proExpiryUnixTimestampMs: 0,
proGenIndexHashHex: nil
)
))
}
}
// MARK: ------ that has a different url and more recent update
context("that has a different url and more recent update") {
beforeEach {
mockStorage.write { db in
try Profile
.updateAll(
db,
Profile.Columns.displayPictureUrl.set(to: "testUrl"),
Profile.Columns.profileLastUpdated.set(to: 9999999999)
)
}
}
// MARK: -------- does not save the picture
it("does not save the picture") {
expect(mockCrypto)
.toNot(call {
$0.generate(.legacyDecryptedDisplayPicture(data: .any, key: .any))
})
expect(mockFileManager)
.toNot(call { $0.createFile(atPath: .any, contents: .any, attributes: .any) })
await expect(mockImageDataManager).toEventuallyNot(call {
await $0.load(.any)
})
expect(mockStorage.read { db in try Profile.fetchOne(db) })
.toNot(equal(
Profile(
id: "1234",
name: "test",
nickname: nil,
displayPictureUrl: "http://oxen.io/100/",
displayPictureEncryptionKey: encryptionKey,
profileLastUpdated: 1234567891,
blocksCommunityMessageRequests: nil,
proFeatures: .none,
proExpiryUnixTimestampMs: 0,
proGenIndexHashHex: nil
)
))
}
}
// MARK: ------ that has a more recent update but the same url and encryption key
context("that has a more recent update but the same url and encryption key") {
beforeEach {
mockStorage.write { db in
try Profile
.updateAll(
db,
Profile.Columns.profileLastUpdated.set(to: 9999999999)
)
}
}
// MARK: -------- saves the picture
it("saves the picture") {
expect(mockCrypto)
.to(call {
$0.generate(.legacyDecryptedDisplayPicture(data: .any, key: .any))
})
expect(mockFileManager).to(call(.exactly(times: 1), matchingParameters: .all) {
$0.createFile(
atPath: "/test/DisplayPictures/5465737448617368",
contents: TestConstants.validImageData,
attributes: nil
)
})
await expect(mockImageDataManager)
.toEventually(call(.exactly(times: 1), matchingParameters: .all) {
await $0.load(
.url(URL(fileURLWithPath: "/test/DisplayPictures/5465737448617368"))
)
})
expect(mockStorage.read { db in try Profile.fetchOne(db) })
.to(equal(
Profile(
id: "1234",
name: "test",
nickname: nil,
displayPictureUrl: "http://oxen.io/100/",
displayPictureEncryptionKey: encryptionKey,
profileLastUpdated: 1234567891,
blocksCommunityMessageRequests: nil,
proFeatures: .none,
proExpiryUnixTimestampMs: 0,
proGenIndexHashHex: nil
)
))
}
}
// MARK: ------ updates the database values
it("updates the database values") {
expect(mockStorage.read { db in try Profile.fetchOne(db) })
.to(equal(
Profile(
id: "1234",
name: "test",
nickname: nil,
displayPictureUrl: "http://oxen.io/100/",
displayPictureEncryptionKey: encryptionKey,
profileLastUpdated: 1234567891,
blocksCommunityMessageRequests: nil,
proFeatures: .none,
proExpiryUnixTimestampMs: 0,
proGenIndexHashHex: nil
)
))
}
}
// MARK: ---- for a group
context("for a group") {
beforeEach {
group = ClosedGroup(
threadId: "03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
name: "TestGroup",
groupDescription: nil,
formationTimestamp: 1234567890,
displayPictureUrl: "http://oxen.io/100/",
displayPictureEncryptionKey: encryptionKey,
shouldPoll: true,
groupIdentityPrivateKey: nil,
authData: Data([1, 2, 3]),
invited: false
)
mockStorage.write { db in
_ = try ClosedGroup.deleteAll(db)
try SessionThread.upsert(
db,
id: "03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
variant: .group,
values: SessionThread.TargetValues(
creationDateTimestamp: .setTo(1234567890),
shouldBeVisible: .setTo(true)
),
using: dependencies
).upsert(db)
try group.insert(db)
}
job = Job(
variant: .displayPictureDownload,
shouldBeUnique: true,
details: DisplayPictureDownloadJob.Details(
target: .group(
id: "03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
url: "http://oxen.io/100/",
encryptionKey: encryptionKey
),
timestamp: 1234567891
)
)
}
// MARK: ------ that does not exist
context("that does not exist") {
beforeEach {
mockStorage.write { db in try ClosedGroup.deleteAll(db) }
}
// MARK: -------- does not save the picture
it("does not save the picture") {
expect(mockCrypto)
.toNot(call {
$0.generate(.legacyDecryptedDisplayPicture(data: .any, key: .any))
})
expect(mockFileManager)
.toNot(call { $0.createFile(atPath: .any, contents: .any, attributes: .any) })
await expect(mockImageDataManager).toEventuallyNot(call {
await $0.load(.any)
})
expect(mockStorage.read { db in try ClosedGroup.fetchOne(db) }).to(beNil())
}
}
// MARK: ------ that has a different encryption key and more recent update
context("that has a different encryption key and more recent update") {
beforeEach {
mockStorage.write { db in
try ClosedGroup
.updateAll(
db,
ClosedGroup.Columns.displayPictureEncryptionKey.set(to: Data([1, 2, 3]))
)
}
}
// MARK: -------- does not save the picture
it("does not save the picture") {
expect(mockCrypto)
.toNot(call {
$0.generate(.legacyDecryptedDisplayPicture(data: .any, key: .any))
})
expect(mockFileManager)
.toNot(call { $0.createFile(atPath: .any, contents: .any, attributes: .any) })
await expect(mockImageDataManager).toEventuallyNot(call {
await $0.load(.any)
})
expect(mockStorage.read { db in try ClosedGroup.fetchOne(db) })
.toNot(equal(
ClosedGroup(
threadId: "03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
name: "TestGroup",
groupDescription: nil,
formationTimestamp: 1234567890,
displayPictureUrl: "http://oxen.io/100/",
displayPictureEncryptionKey: encryptionKey,
shouldPoll: true,
groupIdentityPrivateKey: nil,
authData: Data([1, 2, 3]),
invited: false
)
))
}
}
// MARK: ------ that has a different url and more recent update
context("that has a different url and more recent update") {
beforeEach {
mockStorage.write { db in
try ClosedGroup
.updateAll(
db,
ClosedGroup.Columns.displayPictureUrl.set(to: "testUrl")
)
}
}
// MARK: -------- does not save the picture
it("does not save the picture") {
expect(mockCrypto)
.toNot(call {
$0.generate(.legacyDecryptedDisplayPicture(data: .any, key: .any))
})
expect(mockFileManager)
.toNot(call { $0.createFile(atPath: .any, contents: .any, attributes: .any) })
await expect(mockImageDataManager).toEventuallyNot(call {
await $0.load(.any)
})
expect(mockStorage.read { db in try ClosedGroup.fetchOne(db) })
.toNot(equal(
ClosedGroup(
threadId: "03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
name: "TestGroup",
groupDescription: nil,
formationTimestamp: 1234567890,
displayPictureUrl: "http://oxen.io/100/",
displayPictureEncryptionKey: encryptionKey,
shouldPoll: true,
groupIdentityPrivateKey: nil,
authData: Data([1, 2, 3]),
invited: false
)
))
}
}
// MARK: ------ updates the database values
it("updates the database values") {
expect(mockStorage.read { db in try ClosedGroup.fetchOne(db) })
.to(equal(
ClosedGroup(
threadId: "03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
name: "TestGroup",
groupDescription: nil,
formationTimestamp: 1234567890,
displayPictureUrl: "http://oxen.io/100/",
displayPictureEncryptionKey: encryptionKey,
shouldPoll: true,
groupIdentityPrivateKey: nil,
authData: Data([1, 2, 3]),
invited: false
)
))
}
}
// MARK: ---- for a community
context("for a community") {
beforeEach {
community = OpenGroup(
server: "testServer",
roomToken: "testRoom",
publicKey: "03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
shouldPoll: true,
name: "name",
imageId: "100",
userCount: 1,
infoUpdates: 1,
displayPictureOriginalUrl: nil
)
mockStorage.write { db in
_ = try OpenGroup.deleteAll(db)
try SessionThread.upsert(
db,
id: OpenGroup.idFor(roomToken: "testRoom", server: "testServer"),
variant: .community,
values: SessionThread.TargetValues(
creationDateTimestamp: .setTo(1234567890),
shouldBeVisible: .setTo(true)
),
using: dependencies
).upsert(db)
try community.insert(db)
}
job = Job(
variant: .displayPictureDownload,
shouldBeUnique: true,
details: DisplayPictureDownloadJob.Details(
target: .community(
imageId: "100",
roomToken: "testRoom",
server: "testServer"
),
timestamp: 1234567891
)
)
// SOGS doesn't encrypt it's images so replace the encrypted mock response
mockNetwork
.when {
$0.send(
endpoint: MockEndpoint.any,
destination: .any,
body: .any,
requestTimeout: .any,
requestAndPathBuildTimeout: .any
)
}
.thenReturn(MockNetwork.response(data: TestConstants.validImageData))
}
// MARK: ------ that does not exist
context("that does not exist") {
beforeEach {
mockStorage.write { db in try OpenGroup.deleteAll(db) }
}
// MARK: -------- does not save the picture
it("does not save the picture") {
expect(mockFileManager)
.toNot(call { $0.createFile(atPath: .any, contents: .any, attributes: .any) })
await expect(mockImageDataManager).toEventuallyNot(call { await $0.load(.any) })
expect(mockStorage.read { db in try OpenGroup.fetchOne(db) }).to(beNil())
}
}
// MARK: ------ that has a different imageId
context("that has a different imageId") {
beforeEach {
mockStorage.write { db in
try OpenGroup
.updateAll(
db,
OpenGroup.Columns.imageId.set(to: "101")
)
}
}
// MARK: -------- does not save the picture
it("does not save the picture") {
expect(mockFileManager)
.toNot(call { $0.createFile(atPath: .any, contents: .any, attributes: .any) })
await expect(mockImageDataManager).toEventuallyNot(call { await $0.load(.any) })
expect(mockStorage.read { db in try OpenGroup.fetchOne(db) })
.toNot(equal(
OpenGroup(
server: "testServer",
roomToken: "testRoom",
publicKey: "03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
shouldPoll: true,
name: "name",
imageId: "100",
userCount: 1,
infoUpdates: 1
)
))
}
}
// MARK: ------ that has the same imageId
context("that has the same imageId") {
beforeEach {
mockStorage.write { db in
try OpenGroup
.updateAll(
db,
OpenGroup.Columns.imageId.set(to: "100")
)
}
}
// MARK: -------- saves the picture
it("saves the picture") {
expect(mockFileManager).to(call(.exactly(times: 1), matchingParameters: .all) {
$0.createFile(
atPath: "/test/DisplayPictures/5465737448617368",
contents: TestConstants.validImageData,
attributes: nil
)
})
await expect(mockImageDataManager)
.toEventually(call(.exactly(times: 1), matchingParameters: .all) {
await $0.load(
.url(URL(fileURLWithPath: "/test/DisplayPictures/5465737448617368"))
)
})
expect(mockStorage.read { db in try OpenGroup.fetchOne(db) })
.to(equal(
OpenGroup(
server: "testServer",
roomToken: "testRoom",
publicKey: "03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
shouldPoll: true,
name: "name",
imageId: "100",
userCount: 1,
infoUpdates: 1,
displayPictureOriginalUrl: "testserver/room/testRoom/file/100"
)
))
}
}
// MARK: ------ updates the database values
it("updates the database values") {
expect(mockStorage.read { db in try OpenGroup.fetchOne(db) })
.to(equal(
OpenGroup(
server: "testServer",
roomToken: "testRoom",
publicKey: "03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
shouldPoll: true,
name: "name",
imageId: "100",
userCount: 1,
infoUpdates: 1,
displayPictureOriginalUrl: "testserver/room/testRoom/file/100"
)
))
}
}
}
}
}
}