an atproto pds written in F# (.NET 9) 馃
pds
fsharp
giraffe
dotnet
atproto
1锘縨odule Tests
2
3open Xunit
4open PDSharp.Core.Models
5open PDSharp.Core.Config
6open PDSharp.Core.Crypto
7open PDSharp.Core
8open PDSharp.Core.DidResolver
9open Org.BouncyCastle.Utilities.Encoders
10open System.Text
11open System.Text.Json
12open Org.BouncyCastle.Math
13
14[<Fact>]
15let ``Can instantiate AppConfig`` () =
16 let config = {
17 PublicUrl = "https://example.com"
18 DidHost = "did:web:example.com"
19 JwtSecret = "test-secret-key-for-testing-only"
20 SqliteConnectionString = "Data Source=:memory:"
21 DisableWalAutoCheckpoint = false
22 BlobStore = Disk "blobs"
23 }
24
25 Assert.Equal("did:web:example.com", config.DidHost)
26
27[<Fact>]
28let ``CID TryParse roundtrip`` () =
29 let hash = Crypto.sha256Str "test-data"
30 let cid = Cid.FromHash hash
31 let cidStr = cid.ToString()
32
33 match Cid.TryParse cidStr with
34 | Some parsed -> Assert.Equal<byte[]>(cid.Bytes, parsed.Bytes)
35 | None -> Assert.Fail "TryParse should succeed for valid CID"
36
37[<Fact>]
38let ``CID TryParse returns None for invalid`` () =
39 Assert.True(Cid.TryParse("invalid").IsNone)
40 Assert.True(Cid.TryParse("").IsNone)
41 Assert.True(Cid.TryParse("btooshort").IsNone)
42
43[<Fact>]
44let ``Can instantiate DescribeServerResponse`` () =
45 let response = {
46 availableUserDomains = [ "example.com" ]
47 did = "did:web:example.com"
48 inviteCodeRequired = true
49 }
50
51 Assert.Equal("did:web:example.com", response.did)
52 Assert.Equal(1, response.availableUserDomains.Length)
53
54[<Fact>]
55let ``SHA-256 Hashing correct`` () =
56 let input = "hello world"
57 let hash = sha256Str input
58 let expected = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
59 let actual = Hex.ToHexString(hash)
60 Assert.Equal(expected, actual)
61
62[<Fact>]
63let ``ECDSA P-256 Sign and Verify`` () =
64 let keyPair = generateKey P256
65 let data = Encoding.UTF8.GetBytes("test message")
66 let hash = sha256 data
67 let signature = sign keyPair hash
68 Assert.True(signature.Length = 64, "Signature should be 64 bytes (R|S)")
69
70 let valid = verify keyPair hash signature
71 Assert.True(valid, "Signature verification failed")
72
73[<Fact>]
74let ``ECDSA K-256 Sign and Verify`` () =
75 let keyPair = generateKey K256
76 let data = Encoding.UTF8.GetBytes("test k256")
77 let hash = sha256 data
78 let signature = sign keyPair hash
79 Assert.True(signature.Length = 64, "Signature should be 64 bytes")
80
81 let valid = verify keyPair hash signature
82 Assert.True(valid, "Signature verification failed")
83
84[<Fact>]
85let ``Low-S Enforcement Logic`` () =
86 let n =
87 BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16) // secp256k1 N
88
89 let halfN = n.ShiftRight(1)
90 let highS = halfN.Add(BigInteger.One)
91
92 let lowS = enforceLowS highS n
93 Assert.True(lowS.CompareTo halfN <= 0, "S value should be <= N/2")
94 Assert.Equal(n.Subtract highS, lowS)
95
96[<Fact>]
97let ``DidDocument JSON deserialization`` () =
98 let json =
99 """{
100 "id": "did:web:example.com",
101 "verificationMethod": [{
102 "id": "did:web:example.com#atproto",
103 "type": "Multikey",
104 "controller": "did:web:example.com",
105 "publicKeyMultibase": "zQ3sh..."
106 }]
107 }"""
108
109 let doc =
110 JsonSerializer.Deserialize<DidDocument>(json, JsonSerializerOptions(PropertyNameCaseInsensitive = true))
111
112 Assert.Equal("did:web:example.com", doc.Id)
113 Assert.Single doc.VerificationMethod |> ignore
114 Assert.Equal("Multikey", doc.VerificationMethod.Head.Type)
115
116[<Fact>]
117let ``CID Generation from Hash`` () =
118 let hash =
119 Hex.Decode "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
120
121 let cid = Cid.FromHash hash
122 Assert.Equal<byte>(0x01uy, cid.Bytes.[0])
123 Assert.Equal<byte>(0x71uy, cid.Bytes.[1])
124 Assert.Equal<byte>(0x12uy, cid.Bytes.[2])
125 Assert.Equal<byte>(0x20uy, cid.Bytes.[3])
126
127[<Fact>]
128let ``DAG-CBOR Canonical Sorting`` () =
129 let m = Map.ofList [ "b", box 1; "a", box 2 ]
130 let encoded = DagCbor.encode m
131 let hex = Hex.ToHexString encoded
132 Assert.Equal("a2616102616201", hex)
133
134[<Fact>]
135let ``DAG-CBOR Sorting Length vs Bytes`` () =
136 let m = Map.ofList [ "aa", box 1; "b", box 2 ]
137 let encoded = DagCbor.encode m
138 let hex = Hex.ToHexString encoded
139 Assert.Equal("a261620262616101", hex)