370 lines
15 KiB
Swift
370 lines
15 KiB
Swift
//
|
|
// Copyright Amazon.com Inc. or its affiliates.
|
|
// All Rights Reserved.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
import XCTest
|
|
|
|
import AWSPluginsCore
|
|
import AmplifyTestCommon
|
|
|
|
@testable import Amplify
|
|
@testable import AWSDataStorePlugin
|
|
|
|
|
|
// swfitlint:disable file_length
|
|
// swiftlint:disable type_body_length
|
|
class AWSDataStoreLocalStoreTests: LocalStoreIntegrationTestBase {
|
|
|
|
struct TestModelRegistration: AmplifyModelRegistration {
|
|
func registerModels(registry: ModelRegistry.Type) {
|
|
registry.register(modelType: Post.self)
|
|
registry.register(modelType: Comment.self)
|
|
}
|
|
|
|
let version: String = "1"
|
|
}
|
|
|
|
/// - Given: 4 posts that has been saved
|
|
/// - When:
|
|
/// - query with grouped predicate
|
|
/// - Then:
|
|
/// - 2 post instances will be returned
|
|
/// - second page returns the remaining 5 posts
|
|
/// - the 15 retrieved posts have unique identifiers
|
|
func testQueryWithGroupedQueryPredicateInput() async throws {
|
|
setUp(withModels: TestModelRegistration())
|
|
try await setUpLocalStoreForGroupedPredicateTest()
|
|
let post = Post.keys
|
|
let predicate = (post.id <= 1 && post.title == "title1")
|
|
|| (post.rating > 2 && post.status == PostStatus.private)
|
|
|
|
let queriedPosts = try await Amplify.DataStore.query(Post.self, where: predicate)
|
|
XCTAssertEqual(queriedPosts.count, 2)
|
|
}
|
|
|
|
/// - Given: 15 posts that has been saved
|
|
/// - When:
|
|
/// - query with pagination input given a page number and limit 10
|
|
/// - Then:
|
|
/// - first page returns the 10 (the defined limit) of 15 posts
|
|
/// - second page returns the remaining 5 posts
|
|
/// - the 15 retrieved posts have unique identifiers
|
|
func testQueryWithPaginationInput() async throws {
|
|
setUp(withModels: TestModelRegistration())
|
|
_ = try await setUpLocalStore(numberOfPosts: 15)
|
|
var posts = [Post]()
|
|
let queriedPosts = try await Amplify.DataStore.query(Post.self, paginate: .page(0, limit: 10))
|
|
XCTAssertEqual(queriedPosts.count, 10)
|
|
posts.append(contentsOf: queriedPosts)
|
|
|
|
let queriedPosts2 = try await Amplify.DataStore.query(Post.self, paginate: .page(1, limit: 10))
|
|
XCTAssertEqual(queriedPosts2.count, 5)
|
|
posts.append(contentsOf: queriedPosts2)
|
|
|
|
let idSet = Set(posts.map { $0.id })
|
|
XCTAssertEqual(idSet.count, 15)
|
|
}
|
|
|
|
/// - Given: 20 posts that has been saved
|
|
/// - When:
|
|
/// - attempt to query existing posts that are sorted by rating in ascending order
|
|
/// - Then:
|
|
/// - the existing data will be returned in expected order
|
|
func testQueryWithSortReturnsPostsInAscendingOrder() async throws {
|
|
setUp(withModels: TestModelRegistration())
|
|
_ = try await setUpLocalStore(numberOfPosts: 20)
|
|
|
|
let posts = try await Amplify.DataStore.query(Post.self, sort: .ascending(Post.keys.rating))
|
|
XCTAssertEqual(posts.count, 20)
|
|
|
|
var previousRating: Double = 0
|
|
for post in posts {
|
|
guard let rating = post.rating else {
|
|
XCTFail("Rating should not be nil")
|
|
return
|
|
}
|
|
if rating < previousRating {
|
|
XCTFail("ratings should be in ascending order")
|
|
} else {
|
|
previousRating = rating
|
|
}
|
|
}
|
|
}
|
|
|
|
/// - Given: 50 posts that has been saved
|
|
/// - When:
|
|
/// - attempt to query existing posts firstly sorted by rating in ascending order and secondly sorted by title in descending order
|
|
/// - Then:
|
|
/// - the existing data will be returned in expected order
|
|
func testQueryWithMultipleSortsReturnsPosts() async throws {
|
|
setUp(withModels: TestModelRegistration())
|
|
_ = try await setUpLocalStore(numberOfPosts: 50)
|
|
|
|
let posts = try await Amplify.DataStore.query(Post.self,
|
|
sort: .by(.ascending(Post.keys.rating),
|
|
.descending(Post.keys.title)))
|
|
var previousRating: Double = 0
|
|
for post in posts {
|
|
guard let rating = post.rating else {
|
|
XCTFail("Rating should not be nil")
|
|
return
|
|
}
|
|
if rating < previousRating {
|
|
XCTFail("ratings should be in ascending order")
|
|
} else {
|
|
previousRating = rating
|
|
}
|
|
}
|
|
|
|
// Group the posts by the first sort field, rating. By using `Dictionary(grouping:by:)`,
|
|
// the values in each grouop will be the same order as the original Post array
|
|
// See https://developer.apple.com/documentation/swift/dictionary/3127163-init for more details
|
|
let postsDic = Dictionary(grouping: posts, by: { $0.rating! })
|
|
for (_, pairs) in postsDic {
|
|
for index in 0 ..< pairs.count - 1 {
|
|
if pairs[index].title < pairs[index + 1].title {
|
|
XCTFail("title should be in descending order")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// - Given: 20 posts that has been saved
|
|
/// - When:
|
|
/// - attempt to query existing posts matching a condition and sorted by rating in ascending order
|
|
/// - Then:
|
|
/// - the existing data that matches the given condition will be returned in ascending order by rating
|
|
func testQueryWithPredicateAndSort() async throws {
|
|
setUp(withModels: TestModelRegistration())
|
|
_ = try await setUpLocalStore(numberOfPosts: 20)
|
|
|
|
let posts = try await Amplify.DataStore.query(Post.self,
|
|
where: Post.keys.rating >= 2,
|
|
sort: .ascending(Post.keys.rating))
|
|
|
|
var previousRating: Double = 0
|
|
for post in posts {
|
|
guard let rating = post.rating else {
|
|
XCTFail("Rating should not be nil")
|
|
return
|
|
}
|
|
if rating < 2 {
|
|
XCTFail("Predicate is not working as expected")
|
|
}
|
|
if rating < previousRating {
|
|
XCTFail("ratings should be in ascending order")
|
|
} else {
|
|
previousRating = rating
|
|
}
|
|
}
|
|
}
|
|
|
|
/// - Given: 20 posts that has been saved
|
|
/// - When:
|
|
/// - attempt to query the first 10 existing posts that are sorted by rating in ascending order
|
|
/// - Then:
|
|
/// - the existing data that matches the given condition will be returned in ascending order by rating
|
|
func testQueryWithSortAndPagintate() async throws {
|
|
setUp(withModels: TestModelRegistration())
|
|
_ = try await setUpLocalStore(numberOfPosts: 20)
|
|
let posts = try await Amplify.DataStore.query(Post.self,
|
|
sort: .by(.ascending(Post.keys.rating),
|
|
.descending(Post.keys.title)),
|
|
paginate: .page(0, limit: 10))
|
|
XCTAssertEqual(posts.count, 10)
|
|
|
|
var previousRating: Double = 0
|
|
for post in posts {
|
|
guard let rating = post.rating else {
|
|
XCTFail("Rating should not be nil")
|
|
return
|
|
}
|
|
if rating < previousRating {
|
|
XCTFail("ratings should be in ascending order")
|
|
} else {
|
|
previousRating = rating
|
|
}
|
|
}
|
|
}
|
|
|
|
/// - Given: 50 posts that has been saved
|
|
/// - When:
|
|
/// - attempt to query the first 10 existing posts that mathches a condition and are sorted by rating in ascending order
|
|
/// - Then:
|
|
/// - 10 or less existing data that matches the given condition will be returned in ascending order by rating
|
|
func testQueryWithPredicateAndSortAndPagintate() async throws {
|
|
setUp(withModels: TestModelRegistration())
|
|
let localPosts = try await setUpLocalStore(numberOfPosts: 50)
|
|
let filteredPosts = localPosts.filter { $0.rating! >= 2.0 }
|
|
let count = filteredPosts.count
|
|
|
|
let posts = try await Amplify.DataStore.query(Post.self,
|
|
where: Post.keys.rating >= 2,
|
|
sort: .ascending(Post.keys.rating),
|
|
paginate: .page(0, limit: 10))
|
|
if count >= 10 {
|
|
XCTAssertEqual(posts.count, 10)
|
|
} else {
|
|
XCTAssertEqual(posts.count, count)
|
|
}
|
|
|
|
var previousRating: Double = 0
|
|
for post in posts {
|
|
guard let rating = post.rating else {
|
|
XCTFail("Rating should not be nil")
|
|
return
|
|
}
|
|
if rating < 2 {
|
|
XCTFail("Predicate is not working as expected")
|
|
}
|
|
if rating < previousRating {
|
|
XCTFail("ratings should be in ascending order")
|
|
} else {
|
|
previousRating = rating
|
|
}
|
|
}
|
|
}
|
|
|
|
/// - Given: 50 posts that has been saved
|
|
/// - When:
|
|
/// - attempt to query the every existing posts that mathches a condition and are sorted by rating in ascending order
|
|
/// - Then:
|
|
/// - the existing data that matches the given condition will be returned in ascending order by rating
|
|
func testQueryWithPredicateAndSortWithMultiplePages() async throws {
|
|
setUp(withModels: TestModelRegistration())
|
|
_ = try await setUpLocalStore(numberOfPosts: 50)
|
|
|
|
let queriedPosts = try await Amplify.DataStore.query(Post.self,
|
|
where: Post.keys.rating >= 2)
|
|
|
|
var posts = [Post]()
|
|
var currentPage: UInt = 0
|
|
var shouldRepeat = true
|
|
repeat {
|
|
let returnPosts = try await Amplify.DataStore.query(Post.self,
|
|
where: Post.keys.rating >= 2,
|
|
sort: .ascending(Post.keys.rating),
|
|
paginate: .page(currentPage, limit: 10))
|
|
posts.append(contentsOf: returnPosts)
|
|
if returnPosts.count == 10 {
|
|
currentPage += 1
|
|
} else {
|
|
shouldRepeat = false
|
|
}
|
|
} while shouldRepeat
|
|
|
|
XCTAssertEqual(posts.count, queriedPosts.count)
|
|
|
|
var previousRating: Double = 0
|
|
for post in posts {
|
|
guard let rating = post.rating else {
|
|
XCTFail("Rating should not be nil")
|
|
return
|
|
}
|
|
if rating < 2 {
|
|
XCTFail("Predicate is not working as expected")
|
|
}
|
|
if rating < previousRating {
|
|
XCTFail("ratings should be in ascending order")
|
|
} else {
|
|
previousRating = rating
|
|
}
|
|
}
|
|
}
|
|
|
|
/// DataStore without sync to cloud enabled.
|
|
///
|
|
/// - Given: DataStore is set up with models, and local store is populated with models
|
|
/// - When:
|
|
/// - ObserveQuery is called, add more models
|
|
/// - Then:
|
|
/// - The first snapshot will have initial models and may have additional models
|
|
/// - There may be subsequent snapshots based on how the items are batched
|
|
/// - The last snapshot will have a total of initial plus additional models
|
|
func testObserveQuery() async throws {
|
|
setUp(withModels: TestModelRegistration())
|
|
try await Amplify.DataStore.clear()
|
|
var snapshotCount = 0
|
|
let initialQueryComplete = asyncExpectation(description: "initial snapshot received")
|
|
let allSnapshotsReceived = asyncExpectation(description: "query snapshots received")
|
|
|
|
let subscription = Amplify.DataStore.observeQuery(for: Post.self)
|
|
let sink = Amplify.Publisher.create(subscription).sink { completed in
|
|
switch completed {
|
|
case .finished:
|
|
break
|
|
case .failure(let error):
|
|
XCTFail("\(error)")
|
|
}
|
|
} receiveValue: { querySnapshot in
|
|
snapshotCount += 1
|
|
if snapshotCount == 1 {
|
|
Task { await initialQueryComplete.fulfill() }
|
|
}
|
|
if querySnapshot.items.count == 15 {
|
|
Task { await allSnapshotsReceived.fulfill() }
|
|
}
|
|
}
|
|
await waitForExpectations([initialQueryComplete], timeout: 10)
|
|
_ = try await setUpLocalStore(numberOfPosts: 15)
|
|
await waitForExpectations([allSnapshotsReceived], timeout: 10)
|
|
XCTAssertTrue(snapshotCount >= 2)
|
|
sink.cancel()
|
|
}
|
|
|
|
func testDeleteModelTypeWithPredicate() async throws {
|
|
setUp(withModels: TestModelRegistration())
|
|
_ = try await setUpLocalStore(numberOfPosts: 5)
|
|
var posts = try await Amplify.DataStore.query(Post.self, where: Post.keys.status.eq(PostStatus.draft))
|
|
XCTAssertFalse(posts.isEmpty)
|
|
_ = try await Amplify.DataStore.delete(Post.self, where: Post.keys.status.eq(PostStatus.draft))
|
|
posts = try await Amplify.DataStore.query(Post.self, where: Post.keys.status.eq(PostStatus.draft))
|
|
XCTAssertEqual(posts.count, 0)
|
|
}
|
|
|
|
func testDeleteAll() async throws {
|
|
setUp(withModels: TestModelRegistration())
|
|
_ = try await setUpLocalStore(numberOfPosts: 5)
|
|
_ = try await Amplify.DataStore.delete(Post.self, where: QueryPredicateConstant.all)
|
|
let posts = try await Amplify.DataStore.query(Post.self)
|
|
XCTAssertEqual(posts.count, 0)
|
|
}
|
|
|
|
func setUpLocalStore(numberOfPosts: Int) async throws -> [Post] {
|
|
let posts = (0..<numberOfPosts).map {
|
|
Post(
|
|
title: "title_\($0)",
|
|
content: "content",
|
|
createdAt: .now(),
|
|
rating: Double(Int.random(in: 0 ... 5)),
|
|
status: .draft
|
|
)
|
|
}
|
|
|
|
for (index, post) in posts.enumerated() {
|
|
print("\(index) \(post.id)")
|
|
try await Amplify.DataStore.save(post)
|
|
}
|
|
|
|
return posts
|
|
}
|
|
|
|
func setUpLocalStoreForGroupedPredicateTest() async throws {
|
|
var savedPosts = [Post]()
|
|
savedPosts.append(Post(id: "1", title: "title1", content: "content1",
|
|
createdAt: .now(), rating: 1, status: .draft))
|
|
savedPosts.append(Post(id: "2", title: "title2", content: "content2",
|
|
createdAt: .now(), rating: 2, status: .private))
|
|
savedPosts.append(Post(id: "3", title: "title3", content: "content3",
|
|
createdAt: .now(), rating: 3, status: .draft))
|
|
savedPosts.append(Post(id: "4", title: "title4", content: "content4",
|
|
createdAt: .now(), rating: 4, status: .private))
|
|
for post in savedPosts {
|
|
_ = try await Amplify.DataStore.save(post)
|
|
}
|
|
}
|
|
}
|