| 230 |
230
|
|
end
|
| 231 |
231
|
|
|
| 232 |
232
|
|
describe "reap_expired/1" do
|
| 233 |
|
- |
test "elapsed authority is expired and the thread it fenced is closed" do
|
| 234 |
|
- |
user = owner("reaped")
|
| 235 |
|
- |
{:ok, live} = Threads.open(user, "Still working")
|
| 236 |
|
- |
{:ok, live, _live_grant, _live_token} = Threads.mint_grant(live)
|
|
233
|
+ |
test "a thread's authority has no clock, so waiting does not end it" do
|
|
234
|
+ |
# The behaviour this replaces: an hour passed, the grant expired, the
|
|
235
|
+ |
# open thread it fenced was closed as `authority_expired`, and a coding
|
|
236
|
+ |
# session that was mid-sentence was told to start a new one.
|
|
237
|
+ |
user = owner("no-clock")
|
|
238
|
+ |
{:ok, thread} = Threads.open(user, "Still working")
|
|
239
|
+ |
{:ok, thread, grant, token} = Threads.mint_grant(thread)
|
| 237 |
240
|
|
|
| 238 |
|
- |
elapsed_ttl()
|
| 239 |
|
- |
{:ok, lapsed} = Threads.open(user, "Abandoned")
|
| 240 |
|
- |
{:ok, lapsed, lapsed_grant, lapsed_token} = Threads.mint_grant(lapsed)
|
|
241
|
+ |
assert is_nil(Repo.get!(Grant, grant.id).expires_at)
|
| 241 |
242
|
|
|
| 242 |
|
- |
assert {1, 1} = Threads.reap_expired(user)
|
|
243
|
+ |
# However long the reaper is run, and whenever.
|
|
244
|
+ |
assert {0, 0} = Threads.reap_expired(user)
|
|
245
|
+ |
assert {0, 0} = Threads.reap_expired(user)
|
| 243 |
246
|
|
|
| 244 |
|
- |
assert Repo.get!(Grant, lapsed_grant.id).status == "expired"
|
| 245 |
|
- |
assert {:error, :grant_expired} = Inference.resolve(lapsed_token)
|
|
247
|
+ |
assert Repo.get!(Grant, grant.id).status == "active"
|
|
248
|
+ |
assert Repo.get!(Thread, thread.id).status == "open"
|
|
249
|
+ |
assert {:ok, _resolved} = Inference.resolve(token)
|
|
250
|
+ |
end
|
| 246 |
251
|
|
|
| 247 |
|
- |
reaped = Repo.get!(Thread, lapsed.id)
|
| 248 |
|
- |
assert reaped.status == "failed"
|
| 249 |
|
- |
assert reaped.error_code == "authority_expired"
|
| 250 |
|
- |
assert reaped.report_digest =~ ~r/\Asha256:[0-9a-f]{64}\z/
|
|
252
|
+ |
test "a grant that does carry a deadline is still retired when it passes" do
|
|
253
|
+ |
# Not every grant is a thread's. A computer-bound delegation keeps its
|
|
254
|
+ |
# clock, where the deadline is a security bound rather than a
|
|
255
|
+ |
# convenience, and this is the reader that enforces it.
|
|
256
|
+ |
user = owner("reaped")
|
|
257
|
+ |
elapsed_ttl()
|
|
258
|
+ |
{:ok, thread} = Threads.open(user, "Deadline")
|
|
259
|
+ |
{:ok, _thread, grant, token} = Threads.mint_grant(thread)
|
| 251 |
260
|
|
|
| 252 |
|
- |
# A thread whose clock has not run out is untouched.
|
| 253 |
|
- |
assert Repo.get!(Thread, live.id).status == "open"
|
|
261
|
+ |
assert {1, 1} = Threads.reap_expired(user)
|
|
262
|
+ |
assert Repo.get!(Grant, grant.id).status == "expired"
|
|
263
|
+ |
assert {:error, :grant_expired} = Inference.resolve(token)
|
| 254 |
264
|
|
end
|
| 255 |
265
|
|
|
| 256 |
|
- |
test "a thread that has never minted authority is not reaped" do
|
| 257 |
|
- |
user = owner("never-minted")
|
| 258 |
|
- |
{:ok, thread} = Threads.open(user, "No authority yet")
|
|
266
|
+ |
test "a thread left holding no authority is closed as spent, never as expired" do
|
|
267
|
+ |
# The slot has to come back: nothing is coming to renew a grant, and an
|
|
268
|
+ |
# open thread that can never work again would hold the ceiling forever.
|
|
269
|
+ |
# What changed is the reason it is closed for — the budget ran out, which
|
|
270
|
+ |
# is true, rather than a clock, which no longer exists.
|
|
271
|
+ |
user = owner("left-open")
|
|
272
|
+ |
elapsed_ttl()
|
|
273
|
+ |
{:ok, thread} = Threads.open(user, "Deadline")
|
|
274
|
+ |
{:ok, thread, _grant, _token} = Threads.mint_grant(thread)
|
| 259 |
275
|
|
|
| 260 |
|
- |
assert {0, 0} = Threads.reap_expired(user)
|
| 261 |
|
- |
assert Repo.get!(Thread, thread.id).status == "open"
|
|
276
|
+ |
assert {1, 1} = Threads.reap_expired(user)
|
|
277
|
+ |
|
|
278
|
+ |
reaped = Repo.get!(Thread, thread.id)
|
|
279
|
+ |
assert reaped.status == "failed"
|
|
280
|
+ |
assert reaped.error_code == "authority_spent"
|
|
281
|
+ |
refute reaped.report =~ "expired"
|
| 262 |
282
|
|
end
|
| 263 |
283
|
|
|
| 264 |
284
|
|
test "reaping is idempotent" do
|
| 265 |
285
|
|
user = owner("reap-twice")
|
| 266 |
286
|
|
elapsed_ttl()
|
| 267 |
|
- |
{:ok, thread} = Threads.open(user, "Abandoned")
|
|
287
|
+ |
{:ok, thread} = Threads.open(user, "Deadline")
|
| 268 |
288
|
|
{:ok, _thread, _grant, _token} = Threads.mint_grant(thread)
|
| 269 |
289
|
|
|
| 270 |
290
|
|
assert {1, 1} = Threads.reap_expired(user)
|