an atproto pds written in F# (.NET 9) 馃
pds
fsharp
giraffe
dotnet
atproto
1module Auth.Tests
2
3open Xunit
4open PDSharp.Core.Auth
5
6[<Fact>]
7let ``Password hashing produces salt$hash format`` () =
8 let hash = hashPassword "mypassword"
9 Assert.Contains("$", hash)
10 let parts = hash.Split('$')
11 Assert.Equal(2, parts.Length)
12
13[<Fact>]
14let ``Password verification succeeds for correct password`` () =
15 let hash = hashPassword "mypassword"
16 Assert.True(verifyPassword "mypassword" hash)
17
18[<Fact>]
19let ``Password verification fails for wrong password`` () =
20 let hash = hashPassword "mypassword"
21 Assert.False(verifyPassword "wrongpassword" hash)
22
23[<Fact>]
24let ``Password verification fails for invalid hash format`` () =
25 Assert.False(verifyPassword "password" "invalidhash")
26 Assert.False(verifyPassword "password" "")
27
28[<Fact>]
29let ``JWT access token creation and validation`` () =
30 let secret = "test-secret-key-minimum-32-chars!"
31 let did = "did:web:test.example"
32
33 let token = createAccessToken secret did
34
35 let parts = token.Split('.')
36 Assert.Equal(3, parts.Length)
37
38 match validateToken secret token with
39 | Valid(extractedDid, tokenType, _) ->
40 Assert.Equal(did, extractedDid)
41 Assert.Equal(Access, tokenType)
42 | Invalid reason -> Assert.Fail $"Token should be valid, got: {reason}"
43
44[<Fact>]
45let ``JWT refresh token has correct type`` () =
46 let secret = "test-secret-key-minimum-32-chars!"
47 let did = "did:web:test.example"
48
49 let token = createRefreshToken secret did
50
51 match validateToken secret token with
52 | Valid(_, tokenType, _) -> Assert.Equal(Refresh, tokenType)
53 | Invalid reason -> Assert.Fail $"Token should be valid, got: {reason}"
54
55[<Fact>]
56let ``JWT validation fails with wrong secret`` () =
57 let secret = "test-secret-key-minimum-32-chars!"
58 let wrongSecret = "wrong-secret-key-minimum-32-chars!"
59 let did = "did:web:test.example"
60
61 let token = createAccessToken secret did
62
63 match validateToken wrongSecret token with
64 | Invalid _ -> Assert.True(true)
65 | Valid _ -> Assert.Fail "Token should be invalid with wrong secret"
66
67[<Fact>]
68let ``Account creation and lookup by handle`` () =
69 resetAccounts ()
70
71 match createAccount "test.user" "password123" (Some "test@example.com") with
72 | Error msg -> Assert.Fail msg
73 | Ok account ->
74 Assert.Equal("test.user", account.Handle)
75 Assert.Equal("did:web:test.user", account.Did)
76 Assert.Equal(Some "test@example.com", account.Email)
77
78 match getAccountByHandle "test.user" with
79 | None -> Assert.Fail "Account should be found"
80 | Some found -> Assert.Equal(account.Did, found.Did)
81
82[<Fact>]
83let ``Account creation fails for duplicate handle`` () =
84 resetAccounts ()
85
86 createAccount "duplicate.user" "password" None |> ignore
87
88 match createAccount "duplicate.user" "password2" None with
89 | Error msg -> Assert.Contains("already", msg.ToLower())
90 | Ok _ -> Assert.Fail "Should fail for duplicate handle"
91
92[<Fact>]
93let ``Account lookup by DID`` () =
94 resetAccounts ()
95
96 match createAccount "did.user" "password123" None with
97 | Error msg -> Assert.Fail msg
98 | Ok account ->
99 match getAccountByDid account.Did with
100 | None -> Assert.Fail "Account should be found by DID"
101 | Some found -> Assert.Equal(account.Handle, found.Handle)