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
|
import std/[json, os, sequtils, strutils, tables, unittest]
import ../src/freeqsay
const didA = "did:plc:ewvi7nmzuoqusbcablsm7c4h"
let fixtures = parseFile(currentSourcePath().parentDir / ".." /
"fixtures" / "avatar-conformance.json")
proc stubGet(did: string): HttpGet =
## Test hook standing in for the network: always answers with `did`.
result = proc (url: string): HttpResponse {.gcsafe.} =
HttpResponse(status: 200, body: %*{"did": did})
suite "sha256":
test "vectors":
check toHex(digest("")) ==
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
check toHex(digest("abc")) ==
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
# RFC 4231 test case 1
check toHex(hmac(repeat(0x0b'u8, 20), toBytes("Hi There"))) ==
"b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"
suite "avatar-v1 conformance":
test "fixture replays":
check fixtures.len > 0
for f in fixtures:
let
did = f["did"].getStr
av = deriveAvatar(did)
checkpoint did
check f["canonical_seed"].getStr == toHex(canonicalSeed(did))
for key, want in f["expected_traits"]:
check av.traits[key] == want.getStr
check f["sprite_hash"].getStr == spriteHash(av)
suite "balloons":
test "wraps long lines":
let lines = wrapText("one two three four five", 10)
check lines.allIt(it.len <= 10)
check "one" in lines.join(" ")
test "single-line speech balloon":
let b = makeBalloon("hi")
check "< hi" in b
let top = b.splitLines[0]
check top.startsWith(" ") and top[1 .. ^1].allIt(it == '_')
test "multi-line balloon":
let b = makeBalloon("hello there friend", width = 6)
check "/ " in b
check "\\ " in b
test "thought bubble":
check "( hmm )" in makeBalloon("hmm", think = true)
suite "balloons preserve ANSI shape":
let
esc = "\x1b"
art = didToAnsi(didA, scale = 1)
lines = art.splitLines
test "escape sequences cost no columns":
check visibleWidth(esc & "[38;2;1;2;3mabc" & esc & "[0m") == 3
check lines.allIt(visibleWidth(it) == 16)
test "art lines survive a balloon verbatim":
let
b = makeBalloon(art, width = 40)
body = b.splitLines[1 .. ^2]
# every row is padded to one width, so the right border lines up
let borders = body.mapIt(it.filterIt(it in {'|', '/', '\\'}).len)
check borders.deduplicate.len == 1
check body.len == lines.len
for i, l in lines:
check l in body[i]
test "indentation is not collapsed":
check wrapText(" indented", 40) == @[" indented"]
suite "say":
test "balloon + character for handle + message":
let said = freeqsay("alice.test", "hello world", scale = 1,
resolveOpts = ResolveOpts(httpGet: stubGet(didA)))
check said.did == didA
check said.text == "hello world"
check "< hello world >" in said.output
check "\\" in said.output
check "\x1b[" in said.output
test "raw DID needs no network":
check "< yo >" in freeqsay(didA, "yo", scale = 1).output
suite "resolving":
test "handle normalization":
check normalizeHandle("@Jay.Bsky.Team") == "jay.bsky.team"
check isDid(didA)
check not isDid("jay.bsky.team")
test "DIDs pass through without a request":
check resolveIdentity(didA).did == didA
test "handles resolve through the endpoint":
var seen = ""
let get = proc (url: string): HttpResponse {.gcsafe.} =
{.cast(gcsafe).}: seen = url
HttpResponse(status: 200, body: %*{"did": didA})
check resolveHandle("@example.bsky.social", ResolveOpts(httpGet: get)) == didA
check "handle=example.bsky.social" in seen
test "a dead transport says so instead of blaming the handle":
let get = proc (url: string): HttpResponse {.gcsafe.} =
HttpResponse(status: 0, error: "no TLS here")
expect IOError:
discard resolveHandle("alice.test", ResolveOpts(httpGet: get))
suite "ansi":
test "truecolor escapes":
check "\x1b[" in didToAnsi(didA, scale = 1)
test "handle->ansi matches did->ansi after resolve":
let id = resolveIdentity("alice.test", ResolveOpts(httpGet: stubGet(didA)))
check didToAnsi(didA, scale = 1) == didToAnsi(id.did, scale = 1)
suite "png":
test "signature":
let bs = didToPng(didA, scale = 2)
check bs[0 .. 1] == @[137'u8, 80'u8]
check bs[12 .. 15] == toBytes("IHDR")
check bs[^4 .. ^1] == @[0xae'u8, 0x42, 0x60, 0x82] # IEND crc
|