forked from OurHike/OurHike
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbackgroundStatus.test.ts
More file actions
258 lines (214 loc) · 8.33 KB
/
Copy pathbackgroundStatus.test.ts
File metadata and controls
258 lines (214 loc) · 8.33 KB
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
import { describe, it, expect } from 'vitest'
import { combineBackgroundStatus, type ArchiveState } from './backgroundStatus'
// The background is one thing to a hiker and several archives in the store
// (#192), and this is where the two meet. What matters in every case below is
// that the sentence the screen ends up showing is TRUE of the whole
// background - not of whichever archive happened to be first.
const MB = 1_000_000
const absent = (sizeBytes: number): ArchiveState => ({
status: { state: 'not-downloaded' },
sizeBytes,
})
const done = (sizeBytes: number, completedAt = new Date('2026-08-01T08:00:00Z')) => ({
status: { state: 'downloaded' as const, totalBytes: sizeBytes, completedAt },
sizeBytes,
})
const transferring = (received: number, sizeBytes: number): ArchiveState => ({
status: { state: 'downloading', receivedBytes: received, totalBytes: sizeBytes },
sizeBytes,
})
const stopped = (received: number, sizeBytes: number): ArchiveState => ({
status: { state: 'failed', receivedBytes: received, totalBytes: sizeBytes },
sizeBytes,
})
const evicted = (
sizeBytes: number,
completedAt: Date | null = new Date('2026-07-20T08:00:00Z'),
): ArchiveState => ({
status: { state: 'evicted', completedAt },
sizeBytes,
})
describe('one archive', () => {
it('is handed straight back, which is every case the shipped app has', () => {
// Only the raster sheet is published today (#185/#186 are the other two),
// so the app always combines exactly one status.
const only = stopped(100 * MB, 314 * MB)
expect(combineBackgroundStatus([only])).toEqual(only.status)
})
it('says nothing is downloaded when there is nothing to combine', () => {
expect(combineBackgroundStatus([])).toEqual({ state: 'not-downloaded' })
})
})
describe('several archives, one answer', () => {
it('adds up a transfer across all of them', () => {
const combined = combineBackgroundStatus([
transferring(100 * MB, 314 * MB),
absent(480 * MB),
])
// The untouched archive contributes its published size to the total, so
// the figure does not grow as each piece begins - a download that keeps
// getting bigger reads as one that will never end.
expect(combined).toEqual({
state: 'downloading',
receivedBytes: 100 * MB,
totalBytes: 794 * MB,
})
})
it('is downloaded only when every archive is', () => {
const combined = combineBackgroundStatus([done(314 * MB), done(480 * MB)])
expect(combined).toEqual({
state: 'downloaded',
totalBytes: 794 * MB,
completedAt: new Date('2026-08-01T08:00:00Z'),
})
})
it('dates a finished background by the last piece to land', () => {
const combined = combineBackgroundStatus([
done(314 * MB, new Date('2026-07-01T08:00:00Z')),
done(480 * MB, new Date('2026-08-01T08:00:00Z')),
])
expect(combined).toMatchObject({ completedAt: new Date('2026-08-01T08:00:00Z') })
})
it('offers to carry on when one archive finished and another never started', () => {
// Not "downloaded" - that would promise terrain a hiker does not have -
// and not "not downloaded", which would offer to re-fetch bytes already
// on the phone. Partly here, and resumable, is the true answer.
const combined = combineBackgroundStatus([done(314 * MB), absent(480 * MB)])
expect(combined).toEqual({
state: 'failed',
receivedBytes: 314 * MB,
totalBytes: 794 * MB,
})
})
it('counts what is really here when one archive stopped partway', () => {
const combined = combineBackgroundStatus([
done(314 * MB),
stopped(100 * MB, 480 * MB),
])
expect(combined).toEqual({
state: 'failed',
receivedBytes: 414 * MB,
totalBytes: 794 * MB,
})
})
it('is not downloaded when none of it is here', () => {
expect(combineBackgroundStatus([absent(314 * MB), absent(480 * MB)])).toEqual({
state: 'not-downloaded',
})
})
})
describe('precedence, ordered by what a hiker needs to know', () => {
it('reports a live transfer over everything else', () => {
const combined = combineBackgroundStatus([
evicted(314 * MB),
transferring(50 * MB, 480 * MB),
])
expect(combined.state).toBe('downloading')
})
it('says the phone removed part of the map before saying part is missing', () => {
// #190's sentence explains WHY something is gone, and "some of it is
// missing" does not. A hiker who is told only the second re-downloads
// and watches it vanish again.
const combined = combineBackgroundStatus([evicted(314 * MB), done(480 * MB)])
expect(combined).toEqual({
state: 'evicted',
completedAt: new Date('2026-08-01T08:00:00Z'),
})
})
it('still says it when the completion date did not survive', () => {
const combined = combineBackgroundStatus([evicted(314 * MB, null), absent(480 * MB)])
expect(combined).toEqual({ state: 'evicted', completedAt: null })
})
})
describe('checking bytes already held (#197)', () => {
const checking = (checkedBytes: number, totalBytes: number): ArchiveState => ({
status: { state: 'checking', checkedBytes, totalBytes },
sizeBytes: totalBytes,
})
it('reports the check when that is what the phone is doing', () => {
const combined = combineBackgroundStatus([checking(40 * MB, 100 * MB)])
expect(combined).toEqual({
state: 'checking',
checkedBytes: 40 * MB,
totalBytes: 100 * MB,
})
})
it('adds up only the archives actually being checked', () => {
const combined = combineBackgroundStatus([
checking(40 * MB, 100 * MB),
checking(10 * MB, 50 * MB),
absent(200 * MB),
])
expect(combined).toEqual({
state: 'checking',
checkedBytes: 50 * MB,
totalBytes: 150 * MB,
})
})
it('yields to a live transfer, which is the more informative figure', () => {
const combined = combineBackgroundStatus([
checking(40 * MB, 100 * MB),
transferring(10 * MB, 200 * MB),
])
expect(combined.state).toBe('downloading')
})
it('outranks a resting state - the phone is already busy doing the thing', () => {
// Offering "Resume" underneath a check invites a second tap on work
// that is already under way.
expect(
combineBackgroundStatus([checking(1 * MB, 10 * MB), stopped(5 * MB, 50 * MB)])
.state,
).toBe('checking')
expect(
combineBackgroundStatus([checking(1 * MB, 10 * MB), evicted(50 * MB)]).state,
).toBe('checking')
})
})
describe('a refused archive, kept out of the Resume slot (#238)', () => {
const refused = (sizeBytes: number): ArchiveState => ({
status: { state: 'hash-mismatch' },
sizeBytes,
})
const checking = (checkedBytes: number, totalBytes: number): ArchiveState => ({
status: { state: 'checking', checkedBytes, totalBytes },
sizeBytes: totalBytes,
})
it('is handed straight back alone', () => {
expect(combineBackgroundStatus([refused(314 * MB)])).toEqual({
state: 'hash-mismatch',
})
})
it('outranks a downloaded sibling - "Stopped at X of Y" would be false of it', () => {
// Before this state existed, a mismatched archive read as absent and the
// combine offered Resume beside finished siblings: a promise to carry on
// from bytes that were deliberately not kept.
const combined = combineBackgroundStatus([refused(480 * MB), done(314 * MB)])
expect(combined).toEqual({ state: 'hash-mismatch' })
})
it('outranks an eviction, which explains an older loss than this one', () => {
expect(combineBackgroundStatus([refused(480 * MB), evicted(314 * MB)]).state).toBe(
'hash-mismatch',
)
})
it('yields to a live transfer and to checking - work in progress wins', () => {
expect(
combineBackgroundStatus([refused(480 * MB), transferring(10 * MB, 314 * MB)]).state,
).toBe('downloading')
expect(
combineBackgroundStatus([refused(480 * MB), checking(1 * MB, 314 * MB)]).state,
).toBe('checking')
})
it('owes its whole published size to a combined transfer total', () => {
// Nothing was kept, so its contribution is an absent archive's: zero
// received, full size still to come.
const combined = combineBackgroundStatus([
transferring(100 * MB, 314 * MB),
refused(480 * MB),
])
expect(combined).toEqual({
state: 'downloading',
receivedBytes: 100 * MB,
totalBytes: 794 * MB,
})
})
})