Skip to content

Commit

Permalink
Merge pull request #9 from roanutil/bugfix/create-returns-items-with-…
Browse files Browse the repository at this point in the history
…temporary-objectids

Bugfix/create returns items with temporary objectids
  • Loading branch information
roanutil authored Oct 28, 2022
2 parents c2a1f6f + 5fde769 commit 5298837
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 18 deletions.
16 changes: 13 additions & 3 deletions Sources/CoreDataRepository/CoreDataRepository+CRUD.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,15 @@ extension CoreDataRepository {
_ item: Model,
transactionAuthor: String? = nil
) async -> Result<Model, CoreDataRepositoryError> {
await context.performInScratchPad(schedule: .enqueued) { scratchPad in
await context.performInScratchPad(schedule: .enqueued) { [context] scratchPad in
scratchPad.transactionAuthor = transactionAuthor
let object = Model.RepoManaged(context: scratchPad)
object.create(from: item)
try scratchPad.save()
try context.performAndWait {
try context.save()
}
try scratchPad.obtainPermanentIDs(for: [object])
return object.asUnmanaged
}
}
Expand Down Expand Up @@ -68,12 +72,15 @@ extension CoreDataRepository {
with item: Model,
transactionAuthor _: String? = nil
) async -> Result<Model, CoreDataRepositoryError> {
await context.performInScratchPad(schedule: .enqueued) { scratchPad in
await context.performInScratchPad(schedule: .enqueued) { [context] scratchPad in
let id = try scratchPad.tryObjectId(from: url)
let object = try scratchPad.notDeletedObject(for: id)
let repoManaged: Model.RepoManaged = try object.asRepoManaged()
repoManaged.update(from: item)
try scratchPad.save()
try context.performAndWait {
try context.save()
}
return repoManaged.asUnmanaged
}
}
Expand All @@ -92,12 +99,15 @@ extension CoreDataRepository {
_ url: URL,
transactionAuthor _: String? = nil
) async -> Result<Void, CoreDataRepositoryError> {
await context.performInScratchPad(schedule: .enqueued) { scratchPad in
await context.performInScratchPad(schedule: .enqueued) { [context] scratchPad in
let id = try scratchPad.tryObjectId(from: url)
let object = try scratchPad.notDeletedObject(for: id)
object.prepareForDeletion()
scratchPad.delete(object)
try scratchPad.save()
try context.performAndWait {
try context.save()
}
return ()
}
}
Expand Down
4 changes: 4 additions & 0 deletions Tests/CoreDataRepositoryTests/BatchRepositoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ final class BatchRepositoryTests: CoreDataXCTestCase {
XCTAssertEqual(result.success.count, newMovies.count)
XCTAssertEqual(result.failed.count, 0)

for movie in result.success {
try await verify(movie)
}

try await repositoryContext().perform {
let data = try self.repositoryContext().fetch(fetchRequest)
XCTAssertEqual(
Expand Down
37 changes: 22 additions & 15 deletions Tests/CoreDataRepositoryTests/CRUDRepositoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@ final class CRUDRepositoryTests: CoreDataXCTestCase {
func testCreateSuccess() async throws {
let movie = Movie(id: UUID(), title: "Create Success", releaseDate: Date(), boxOffice: 100)
let result: Result<Movie, CoreDataRepositoryError> = try await repository().create(movie)
guard case var .success(resultMovie) = result else {
guard case let .success(resultMovie) = result else {
XCTFail("Not expecting a failed result")
return
}
var tempResultMovie = resultMovie
XCTAssertNotNil(tempResultMovie.url)
tempResultMovie.url = nil
XCTAssertNoDifference(tempResultMovie, movie)

XCTAssertNotNil(resultMovie.url)
resultMovie.url = nil
let diff = CustomDump.diff(resultMovie, movie)
XCTAssertNil(diff)
try await verify(resultMovie)
}

func testReadSuccess() async throws {
Expand All @@ -39,15 +40,18 @@ final class CRUDRepositoryTests: CoreDataXCTestCase {
let result: Result<Movie, CoreDataRepositoryError> = try await repository()
.read(try XCTUnwrap(createdMovie.url))

guard case var .success(resultMovie) = result else {
guard case let .success(resultMovie) = result else {
XCTFail("Not expecting a failed result")
return
}

XCTAssertNotNil(resultMovie.url)
resultMovie.url = nil
let diff = CustomDump.diff(resultMovie, movie)
XCTAssertNil(diff)
var tempResultMovie = resultMovie

XCTAssertNotNil(tempResultMovie.url)
tempResultMovie.url = nil
XCTAssertNoDifference(tempResultMovie, movie)

try await verify(resultMovie)
}

func testReadFailure() async throws {
Expand Down Expand Up @@ -91,15 +95,18 @@ final class CRUDRepositoryTests: CoreDataXCTestCase {
let result: Result<Movie, CoreDataRepositoryError> = try await repository()
.update(try XCTUnwrap(createdMovie.url), with: movie)

guard case var .success(resultMovie) = result else {
guard case let .success(resultMovie) = result else {
XCTFail("Not expecting a failed result")
return
}

XCTAssertNotNil(resultMovie.url)
resultMovie.url = nil
let diff = CustomDump.diff(resultMovie, movie)
XCTAssertNil(diff)
var tempResultMovie = resultMovie

XCTAssertNotNil(tempResultMovie.url)
tempResultMovie.url = nil
XCTAssertNoDifference(tempResultMovie, movie)

try await verify(resultMovie)
}

func testUpdateFailure() async throws {
Expand Down
37 changes: 37 additions & 0 deletions Tests/CoreDataRepositoryTests/CoreDataXCTestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import Combine
import CoreData
import CoreDataRepository
import CustomDump
import XCTest

class CoreDataXCTestCase: XCTestCase {
Expand Down Expand Up @@ -49,4 +50,40 @@ class CoreDataXCTestCase: XCTestCase {
_repository = nil
cancellables.forEach { $0.cancel() }
}

func verify<T>(_ item: T) async throws where T: UnmanagedModel {
guard let url = item.managedRepoUrl else {
XCTFail("Failed to verify item in store because it has no URL")
return
}

let context = try repositoryContext()
let coordinator = try container().persistentStoreCoordinator
context.performAndWait {
guard let objectID = coordinator.managedObjectID(forURIRepresentation: url) else {
XCTFail("Failed to verify item in store because no NSManagedObjectID found in viewContext from URL.")
return
}
var _object: NSManagedObject?
do {
_object = try context.existingObject(with: objectID)
} catch {
XCTFail(
"Failed to verify item in store because it was not found by its NSManagedObjectID. Error: \(error.localizedDescription)"
)
return
}

guard let object = _object else {
XCTFail("Failed to verify item in store because it was not found by its NSManagedObjectID")
return
}

guard let managedItem = object as? T.RepoManaged else {
XCTFail("Failed to verify item in store because it failed to cast to RepoManaged type.")
return
}
XCTAssertNoDifference(item, managedItem.asUnmanaged)
}
}
}

0 comments on commit 5298837

Please sign in to comment.