/
IriLockerSpec.scala
168 lines (129 loc) · 5.47 KB
/
IriLockerSpec.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package org.knora.webapi.responders
import java.util.UUID
import org.knora.webapi.{ApplicationLockException, IRI}
import org.scalatest.{Matchers, WordSpec}
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
/**
* Tests [[IriLocker]].
*/
class IriLockerSpec extends WordSpec with Matchers {
import scala.concurrent.ExecutionContext.Implicits.global
val SUCCESS = "success"
val FAILURE = "failure"
"IriLocker" should {
"not allow a request to acquire a lock when another request already has it" in {
def runLongTask(): Future[String] = Future {
Thread.sleep(16000)
SUCCESS
}
def runShortTask(): Future[String] = Future(SUCCESS)
val testIri: IRI = "http://example.org/test1"
val firstApiRequestID = UUID.randomUUID
IriLocker.runWithIriLock(
apiRequestID = firstApiRequestID,
iri = testIri,
task = () => runLongTask()
)
// Wait a bit to allow the first request to get the lock.
Thread.sleep(200)
val secondApiRequestID = UUID.randomUUID
val secondTaskResultFuture = IriLocker.runWithIriLock(
apiRequestID = secondApiRequestID,
iri = testIri,
task = () => runShortTask()
)
val secondTaskFailedWithLockTimeout = try {
Await.result(secondTaskResultFuture, 20.seconds)
false
} catch {
case ale: ApplicationLockException => true
}
assert(secondTaskFailedWithLockTimeout, "Second task did not get a lock timeout")
}
"provide reentrant locks" in {
def runRecursiveTask(iri: IRI, apiRequestID: UUID, count: Int): Future[String] = {
if (count > 0) {
IriLocker.runWithIriLock(
apiRequestID = apiRequestID,
iri = iri,
task = () => runRecursiveTask(iri, apiRequestID, count - 1)
)
} else {
Future(SUCCESS)
}
}
val testIri: IRI = "http://example.org/test2"
val firstApiRequestID = UUID.randomUUID
val firstTestResult = Await.result(runRecursiveTask(testIri, firstApiRequestID, 3), 1.second)
assert(firstTestResult == SUCCESS)
val secondApiRequestID = UUID.randomUUID
val secondTestResult = Await.result(runRecursiveTask(testIri, secondApiRequestID, 3), 1.second)
assert(secondTestResult == SUCCESS)
}
"release a lock when a task returns a failed future" in {
// If succeed is true, returns a successful future, otherwise returns a failed future.
def runTask(succeed: Boolean): Future[String] = Future {
if (succeed) {
SUCCESS
} else {
throw new Exception(FAILURE)
}
}
val testIri: IRI = "http://example.org/test3"
val firstApiRequestID = UUID.randomUUID
val firstTaskResultFuture = IriLocker.runWithIriLock(
apiRequestID = firstApiRequestID,
iri = testIri,
task = () => runTask(false)
)
val firstTaskFailed = try {
Await.result(firstTaskResultFuture, 1.second)
false
} catch {
case e: Exception => true
}
assert(firstTaskFailed, "First task did not fail")
val secondApiRequestID = UUID.randomUUID
val secondTaskResultFuture = IriLocker.runWithIriLock(
apiRequestID = secondApiRequestID,
iri = testIri,
task = () => runTask(true)
)
val secondTaskResult = Await.result(secondTaskResultFuture, 1.second)
assert(secondTaskResult == SUCCESS, "Second task did not succeed")
}
"release a lock when a task throws an exception instead of returning a future" in {
// If succeed is true, returns a successful future, otherwise throws an exception.
def runTask(succeed: Boolean): Future[String] = {
if (succeed) {
Future(SUCCESS)
} else {
throw new Exception(FAILURE)
}
}
val testIri: IRI = "http://example.org/test4"
val firstApiRequestID = UUID.randomUUID
val firstTaskResultFuture = IriLocker.runWithIriLock(
apiRequestID = firstApiRequestID,
iri = testIri,
task = () => runTask(false)
)
val firstTaskFailed = try {
Await.result(firstTaskResultFuture, 1.second)
false
} catch {
case e: Exception => true
}
assert(firstTaskFailed, "First task did not fail")
val secondApiRequestID = UUID.randomUUID
val secondTaskResultFuture = IriLocker.runWithIriLock(
apiRequestID = secondApiRequestID,
iri = testIri,
task = () => runTask(true)
)
val secondTaskResult = Await.result(secondTaskResultFuture, 1.second)
assert(secondTaskResult == SUCCESS, "Second task did not succeed")
}
}
}