Files
element-x-ios/UnitTests/Sources/ContentScannerServiceTests.swift
Mauro 6967f99626 Content scanner also checks for thumbnails (#5911)
* scan also thumbnails for medias

* pr suggestion
2026-07-22 16:43:08 +02:00

171 lines
6.7 KiB
Swift

//
// Copyright 2026 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
@testable import ElementX
import Foundation
import Testing
struct ContentScannerServiceTests {
@Test
func safeContentIsCached() async throws {
let proxy = ContentScannerProxyMock()
proxy.scanMediaSourceReturnValue = .success(true)
let service = ContentScannerService(contentScannerProxy: proxy)
let source = makeSource("1")
#expect(service.scanResultFromSource(source) == nil, "A source should not have a verdict before being scanned.")
let result = await service.loadScanResultFromSource(source)
#expect(try result.get() == true)
#expect(service.scanResultFromSource(source) == true)
// A second load returns the cached verdict without scanning again.
await service.loadScanResultFromSource(source)
#expect(proxy.scanMediaSourceCallsCount == 1)
}
@Test
func unsafeContentIsCached() async throws {
let proxy = ContentScannerProxyMock()
proxy.scanMediaSourceReturnValue = .success(false)
let service = ContentScannerService(contentScannerProxy: proxy)
let source = makeSource("1")
let result = await service.loadScanResultFromSource(source)
#expect(try result.get() == false)
#expect(service.scanResultFromSource(source) == false)
await service.loadScanResultFromSource(source)
#expect(proxy.scanMediaSourceCallsCount == 1)
}
@Test
func sourcesAreCachedIndependently() async {
let proxy = ContentScannerProxyMock()
let service = ContentScannerService(contentScannerProxy: proxy)
proxy.scanMediaSourceReturnValue = .success(true)
await service.loadScanResultFromSource(makeSource("safe"))
proxy.scanMediaSourceReturnValue = .success(false)
await service.loadScanResultFromSource(makeSource("unsafe"))
#expect(service.scanResultFromSource(makeSource("safe")) == true)
#expect(service.scanResultFromSource(makeSource("unsafe")) == false)
#expect(service.scanResultFromSource(makeSource("unscanned")) == nil)
}
@Test
func failedScanIsNotCachedAndCanBeRetried() async throws {
let proxy = ContentScannerProxyMock()
proxy.scanMediaSourceReturnValue = .failure(.missingClient)
let service = ContentScannerService(contentScannerProxy: proxy)
let source = makeSource("1")
var result = await service.loadScanResultFromSource(source)
guard case .failure(.failedScanning) = result else {
Issue.record("The scan should fail when the proxy fails.")
return
}
#expect(service.scanResultFromSource(source) == nil, "A failed scan should not be cached.")
// The scan is retried and now succeeds.
proxy.scanMediaSourceReturnValue = .success(true)
result = await service.loadScanResultFromSource(source)
#expect(try result.get() == true)
#expect(proxy.scanMediaSourceCallsCount == 2)
}
@Test
func concurrentLoadsShareASingleScan() async {
let proxy = ContentScannerProxyMock()
proxy.scanMediaSourceClosure = { _ in
try? await Task.sleep(for: .milliseconds(50))
return .success(true)
}
let service = ContentScannerService(contentScannerProxy: proxy)
let source = makeSource("1")
async let first = service.loadScanResultFromSource(source)
async let second = service.loadScanResultFromSource(source)
let results = await [first, second]
#expect(results.allSatisfy { (try? $0.get()) == true })
#expect(proxy.scanMediaSourceCallsCount == 1, "Concurrent loads for the same source should share a single scan.")
}
// MARK: - Multiple sources
@Test
func multipleSourcesAreSafeWhenAllSafe() async throws {
let proxy = ContentScannerProxyMock()
proxy.scanMediaSourceReturnValue = .success(true)
let service = ContentScannerService(contentScannerProxy: proxy)
let result = await service.loadScanResultFromSources([makeSource("media"), makeSource("thumbnail")])
#expect(try result.get() == true)
}
@Test
func multipleSourcesAreUnsafeWhenAnySourceIsUnsafe() async throws {
let proxy = ContentScannerProxyMock()
let unsafeSource = makeSource("thumbnail")
proxy.scanMediaSourceClosure = { source in
source == unsafeSource ? .success(false) : .success(true)
}
let service = ContentScannerService(contentScannerProxy: proxy)
// The media is safe but its thumbnail is unsafe, which is enough to flag the whole item.
let result = await service.loadScanResultFromSources([makeSource("media"), unsafeSource])
#expect(try result.get() == false)
}
@Test
func multipleSourcesFailWhenAnySourceFails() async {
let proxy = ContentScannerProxyMock()
let failingSource = makeSource("thumbnail")
proxy.scanMediaSourceClosure = { source in
source == failingSource ? .failure(.missingClient) : .success(true)
}
let service = ContentScannerService(contentScannerProxy: proxy)
let result = await service.loadScanResultFromSources([makeSource("media"), failingSource])
guard case .failure(.failedScanning) = result else {
Issue.record("A failure of any source should fail the combined scan.")
return
}
}
@Test
func multipleSourcesSyncLookupCombinesVerdicts() async {
let proxy = ContentScannerProxyMock()
let service = ContentScannerService(contentScannerProxy: proxy)
proxy.scanMediaSourceReturnValue = .success(true)
await service.loadScanResultFromSource(makeSource("safe"))
proxy.scanMediaSourceReturnValue = .success(false)
await service.loadScanResultFromSource(makeSource("unsafe"))
#expect(service.scanResultFromSources([makeSource("safe")]) == true)
#expect(service.scanResultFromSources([makeSource("safe"), makeSource("unsafe")]) == false)
#expect(service.scanResultFromSources([makeSource("safe"), makeSource("unscanned")]) == nil)
}
// MARK: - Helpers
private func makeSource(_ id: String) -> MediaSourceProxy {
// swiftlint:disable:next force_unwrapping force_try
try! MediaSourceProxy(url: URL(string: "mxc://example.com/\(id)")!, mimeType: nil)
}
}