Skip to content

Commit 8e7d557

Browse files
committed
test: 웹페이지 썸네일 저장 트랜잭션 테스트 추가
1 parent bfb23d2 commit 8e7d557

1 file changed

Lines changed: 59 additions & 13 deletions

File tree

Application/DevLogPersistence/Tests/Persistence/WebPageImageStoreImplTests.swift

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,28 +52,74 @@ struct WebPageImageStoreImplTests {
5252
#expect(directorySize == 0)
5353
}
5454

55-
@Test("동시 저장 요청은 요청 순서대로 같은 파일을 갱신한다")
56-
func 동시_저장_요청은_요청_순서대로_같은_파일을_갱신한다() async throws {
55+
@Test("같은 store를 공유하는 객체의 저장은 트랜잭션 단위로 반영된다")
56+
func 같은_store를_공유하는_객체의_저장은_트랜잭션_단위로_반영된다() async throws {
5757
let store = WebPageImageStoreImpl()
58+
let firstClient = WebPageImageStoreClient(store: store)
59+
let secondClient = WebPageImageStoreClient(store: store)
5860
try await store.clearDirectory()
5961
let url = try #require(URL(string: "https://example.com/\(UUID().uuidString)"))
60-
let firstData = Data(repeating: 1, count: 64 * 1024 * 1024)
61-
let secondData = Data("latest".utf8)
62+
let largeData = Data(repeating: 1, count: 64 * 1024 * 1024)
63+
let smallData = Data("latest".utf8)
64+
let startedAt = ContinuousClock.now
6265

63-
let firstSaveTask = Task {
64-
try await store.saveImage(firstData, for: url)
66+
// 동일한 Impl 인스턴스를 공유하는 두 객체가 접근하는 형태의 결과 기반 테스트다.
67+
// 첫 번째 큰 저장 작업이 먼저 큐에 들어갈 시간을 준 뒤 두 번째 작은 저장을 요청하고,
68+
// 최종 파일이 작은 데이터라면 앞 작업 전체가 끝난 뒤 뒤 작업이 반영된 것으로 본다.
69+
// 각 작업의 완료 시점은 호출자 관점에서 saveImage await가 반환된 시점으로 기록한다.
70+
let largeSaveTask = Task {
71+
try await firstClient.saveImage(largeData, for: url, name: "large", since: startedAt)
6572
}
6673
try await Task.sleep(nanoseconds: 10_000_000)
67-
let secondSaveTask = Task {
68-
try await store.saveImage(secondData, for: url)
69-
}
7074

71-
_ = try await firstSaveTask.value
72-
let fileURL = try await secondSaveTask.value
73-
let savedData = try Data(contentsOf: fileURL)
75+
let smallSaveMeasurement = try await secondClient.saveImage(smallData, for: url, name: "small", since: startedAt)
76+
let largeSaveMeasurement = try await largeSaveTask.value
77+
let savedData = try Data(contentsOf: smallSaveMeasurement.fileURL)
78+
79+
print(saveSummary(largeSaveMeasurement))
80+
print(saveSummary(smallSaveMeasurement))
7481

75-
#expect(savedData == secondData)
82+
#expect(savedData == smallData)
7683

7784
try await store.clearDirectory()
7885
}
7986
}
87+
88+
private struct WebPageImageStoreClient {
89+
let store: WebPageImageStoreImpl
90+
91+
func saveImage(
92+
_ data: Data,
93+
for url: URL,
94+
name: String,
95+
since startedAt: ContinuousClock.Instant
96+
) async throws -> WebPageImageStoreSaveMeasurement {
97+
let requestedAt = startedAt.duration(to: .now)
98+
let fileURL = try await store.saveImage(data, for: url)
99+
let finishedAt = startedAt.duration(to: .now)
100+
101+
return WebPageImageStoreSaveMeasurement(
102+
name: name,
103+
fileURL: fileURL,
104+
requestedAt: requestedAt,
105+
finishedAt: finishedAt
106+
)
107+
}
108+
}
109+
110+
private struct WebPageImageStoreSaveMeasurement {
111+
let name: String
112+
let fileURL: URL
113+
let requestedAt: Duration
114+
let finishedAt: Duration
115+
}
116+
117+
private func saveSummary(_ measurement: WebPageImageStoreSaveMeasurement) -> String {
118+
"\(measurement.name) save requested: \(millisecondsString(measurement.requestedAt))ms, finished: \(millisecondsString(measurement.finishedAt))ms"
119+
}
120+
121+
private func millisecondsString(_ duration: Duration) -> String {
122+
let components = duration.components
123+
let milliseconds = Double(components.seconds) * 1_000 + Double(components.attoseconds) / 1_000_000_000_000_000
124+
return String(format: "%.3f", milliseconds)
125+
}

0 commit comments

Comments
 (0)