Repository navigation
Expand file tree
/
Copy pathguide-offline-first.test.ts
More file actions
142 lines (117 loc) · 4.12 KB
/
Copy pathguide-offline-first.test.ts
File metadata and controls
142 lines (117 loc) · 4.12 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
import { afterEach, describe, expect, it } from "vitest"
import { configure, type SolidObjectsRuntime } from "../src/runtime.js"
import { sqlite } from "../src/database/sqlite.js"
import { Inspection } from "../examples/guides/offline-first/server-inspection.js"
import { handleInspectionSync } from "../examples/guides/offline-first/sync-route.js"
const runtimes: SolidObjectsRuntime[] = []
afterEach(async () => {
for (const runtime of runtimes.splice(0)) await runtime.close()
})
async function serverRuntime(): Promise<SolidObjectsRuntime> {
const runtime = configure({
database: sqlite({ path: ":memory:" }),
authorizeQuery: () => true,
})
runtimes.push(runtime)
runtime.register(Inspection)
await runtime.install()
return runtime
}
const envelope = {
effectId: "effect-1",
actorType: "Inspection",
actorId: "site-12",
operation: "record",
arguments: { findingId: "finding-1", note: "Crack in the north wall" },
}
function syncRequest({ device, body }: { device: string | null; body: unknown }): Request {
const headers = new Headers({ "content-type": "application/json" })
if (device) headers.set("x-device", device)
return new Request("https://example.test/inspections/sync", {
method: "POST",
headers,
body: JSON.stringify(body),
})
}
async function canWrite({
request,
envelope: received,
}: {
request: Request
envelope: { actorId: string }
}): Promise<boolean> {
return request.headers.get("x-device") === "tablet-7" && received.actorId === "site-12"
}
async function findings(runtime: SolidObjectsRuntime): Promise<readonly unknown[]> {
await runtime.testing.drain({ roles: ["actors"] })
return (await runtime.ref(Inspection, "site-12").snapshot()).findings
}
describe("the inspection sync route", () => {
it("refuses a device that it cannot authorize", async () => {
const runtime = await serverRuntime()
const response = await handleInspectionSync({
request: syncRequest({ device: null, body: envelope }),
runtime,
canWrite,
})
expect(response.status).toBe(403)
expect(await findings(runtime)).toEqual([])
})
it("applies a write from an authorized device", async () => {
const runtime = await serverRuntime()
const response = await handleInspectionSync({
request: syncRequest({ device: "tablet-7", body: envelope }),
runtime,
canWrite,
})
expect(response.status).toBe(200)
expect(await findings(runtime)).toEqual([
{ findingId: "finding-1", note: "Crack in the north wall" },
])
})
it("applies a repeated write once", async () => {
const runtime = await serverRuntime()
await handleInspectionSync({
request: syncRequest({ device: "tablet-7", body: envelope }),
runtime,
canWrite,
})
const repeated = await handleInspectionSync({
request: syncRequest({ device: "tablet-7", body: envelope }),
runtime,
canWrite,
})
expect(repeated.status).toBe(200)
expect(await findings(runtime)).toHaveLength(1)
})
it("answers 422 for a write that can never apply", async () => {
const runtime = await serverRuntime()
const malformed = { ...envelope, operation: "" }
const response = await handleInspectionSync({
request: syncRequest({ device: "tablet-7", body: malformed }),
runtime,
canWrite,
})
expect(response.status).toBe(422)
})
it("answers 422 for a write to an actor type that the server removed", async () => {
const runtime = await serverRuntime()
const removed = { ...envelope, actorType: "RemovedInspection" }
const response = await handleInspectionSync({
request: syncRequest({ device: "tablet-7", body: removed }),
runtime,
canWrite,
})
expect(response.status).toBe(422)
})
it("answers 422 for a write to an operation that the server removed", async () => {
const runtime = await serverRuntime()
const removed = { ...envelope, operation: "removedOperation" }
const response = await handleInspectionSync({
request: syncRequest({ device: "tablet-7", body: removed }),
runtime,
canWrite,
})
expect(response.status).toBe(422)
})
})