A Deno-compatible AT Protocol OAuth client that serves as a drop-in replacement for @atproto/oauth-client-node
1import { assertEquals, assertInstanceOf } from "@std/assert";
2import {
3 AuthorizationError,
4 DPoPError,
5 HandleResolutionError,
6 InvalidHandleError,
7 InvalidStateError,
8 OAuthError,
9 PDSDiscoveryError,
10 SessionError,
11 TokenExchangeError,
12} from "../src/errors.ts";
13
14Deno.test("OAuthError", async (t) => {
15 await t.step("should create basic error with message", () => {
16 const error = new OAuthError("Test message");
17 assertEquals(error.message, "Test message");
18 assertEquals(error.name, "OAuthError");
19 assertEquals(error.cause, undefined);
20 });
21
22 await t.step("should create error with cause", () => {
23 const cause = new Error("Original error");
24 const error = new OAuthError("Test message", cause);
25 assertEquals(error.message, "Test message");
26 assertEquals(error.name, "OAuthError");
27 assertEquals(error.cause, cause);
28 });
29
30 await t.step("should be instance of Error", () => {
31 const error = new OAuthError("Test message");
32 assertInstanceOf(error, Error);
33 assertInstanceOf(error, OAuthError);
34 });
35});
36
37Deno.test("InvalidHandleError", async (t) => {
38 await t.step("should create error with handle in message", () => {
39 const error = new InvalidHandleError("invalid.handle");
40 assertEquals(error.message, "Invalid AT Protocol handle: invalid.handle");
41 assertEquals(error.name, "InvalidHandleError");
42 });
43
44 await t.step("should be instance of OAuthError", () => {
45 const error = new InvalidHandleError("invalid.handle");
46 assertInstanceOf(error, OAuthError);
47 assertInstanceOf(error, InvalidHandleError);
48 });
49});
50
51Deno.test("HandleResolutionError", async (t) => {
52 await t.step("should create error with handle in message", () => {
53 const error = new HandleResolutionError("test.handle");
54 assertEquals(error.message, "Failed to resolve handle test.handle to DID and PDS");
55 assertEquals(error.name, "HandleResolutionError");
56 assertEquals(error.cause, undefined);
57 });
58
59 await t.step("should create error with cause", () => {
60 const cause = new Error("Network error");
61 const error = new HandleResolutionError("test.handle", cause);
62 assertEquals(error.message, "Failed to resolve handle test.handle to DID and PDS");
63 assertEquals(error.name, "HandleResolutionError");
64 assertEquals(error.cause, cause);
65 });
66
67 await t.step("should be instance of OAuthError", () => {
68 const error = new HandleResolutionError("test.handle");
69 assertInstanceOf(error, OAuthError);
70 assertInstanceOf(error, HandleResolutionError);
71 });
72});
73
74Deno.test("PDSDiscoveryError", async (t) => {
75 await t.step("should create error with PDS URL in message", () => {
76 const error = new PDSDiscoveryError("https://example.com");
77 assertEquals(error.message, "Failed to discover OAuth endpoints for PDS: https://example.com");
78 assertEquals(error.name, "PDSDiscoveryError");
79 assertEquals(error.cause, undefined);
80 });
81
82 await t.step("should create error with cause", () => {
83 const cause = new Error("Discovery failed");
84 const error = new PDSDiscoveryError("https://example.com", cause);
85 assertEquals(error.message, "Failed to discover OAuth endpoints for PDS: https://example.com");
86 assertEquals(error.name, "PDSDiscoveryError");
87 assertEquals(error.cause, cause);
88 });
89
90 await t.step("should be instance of OAuthError", () => {
91 const error = new PDSDiscoveryError("https://example.com");
92 assertInstanceOf(error, OAuthError);
93 assertInstanceOf(error, PDSDiscoveryError);
94 });
95});
96
97Deno.test("TokenExchangeError", async (t) => {
98 await t.step("should create error with message", () => {
99 const error = new TokenExchangeError("Invalid grant");
100 assertEquals(error.message, "Token exchange failed: Invalid grant");
101 assertEquals(error.name, "TokenExchangeError");
102 assertEquals(error.errorCode, undefined);
103 assertEquals(error.cause, undefined);
104 });
105
106 await t.step("should create error with error code", () => {
107 const error = new TokenExchangeError("Invalid grant", "invalid_grant");
108 assertEquals(error.message, "Token exchange failed: Invalid grant");
109 assertEquals(error.name, "TokenExchangeError");
110 assertEquals(error.errorCode, "invalid_grant");
111 assertEquals(error.cause, undefined);
112 });
113
114 await t.step("should create error with error code and cause", () => {
115 const cause = new Error("HTTP 400");
116 const error = new TokenExchangeError("Invalid grant", "invalid_grant", cause);
117 assertEquals(error.message, "Token exchange failed: Invalid grant");
118 assertEquals(error.name, "TokenExchangeError");
119 assertEquals(error.errorCode, "invalid_grant");
120 assertEquals(error.cause, cause);
121 });
122
123 await t.step("should be instance of OAuthError", () => {
124 const error = new TokenExchangeError("Invalid grant");
125 assertInstanceOf(error, OAuthError);
126 assertInstanceOf(error, TokenExchangeError);
127 });
128});
129
130Deno.test("DPoPError", async (t) => {
131 await t.step("should create error with message", () => {
132 const error = new DPoPError("Key generation failed");
133 assertEquals(error.message, "DPoP operation failed: Key generation failed");
134 assertEquals(error.name, "DPoPError");
135 assertEquals(error.cause, undefined);
136 });
137
138 await t.step("should create error with cause", () => {
139 const cause = new Error("Crypto error");
140 const error = new DPoPError("Key generation failed", cause);
141 assertEquals(error.message, "DPoP operation failed: Key generation failed");
142 assertEquals(error.name, "DPoPError");
143 assertEquals(error.cause, cause);
144 });
145
146 await t.step("should be instance of OAuthError", () => {
147 const error = new DPoPError("Key generation failed");
148 assertInstanceOf(error, OAuthError);
149 assertInstanceOf(error, DPoPError);
150 });
151});
152
153Deno.test("SessionError", async (t) => {
154 await t.step("should create error with message", () => {
155 const error = new SessionError("Invalid session data");
156 assertEquals(error.message, "Session error: Invalid session data");
157 assertEquals(error.name, "SessionError");
158 assertEquals(error.cause, undefined);
159 });
160
161 await t.step("should create error with cause", () => {
162 const cause = new Error("Serialization failed");
163 const error = new SessionError("Invalid session data", cause);
164 assertEquals(error.message, "Session error: Invalid session data");
165 assertEquals(error.name, "SessionError");
166 assertEquals(error.cause, cause);
167 });
168
169 await t.step("should be instance of OAuthError", () => {
170 const error = new SessionError("Invalid session data");
171 assertInstanceOf(error, OAuthError);
172 assertInstanceOf(error, SessionError);
173 });
174});
175
176Deno.test("InvalidStateError", async (t) => {
177 await t.step("should create error with fixed message", () => {
178 const error = new InvalidStateError();
179 assertEquals(error.message, "Invalid or expired OAuth state parameter");
180 assertEquals(error.name, "InvalidStateError");
181 assertEquals(error.cause, undefined);
182 });
183
184 await t.step("should be instance of OAuthError", () => {
185 const error = new InvalidStateError();
186 assertInstanceOf(error, OAuthError);
187 assertInstanceOf(error, InvalidStateError);
188 });
189});
190
191Deno.test("AuthorizationError", async (t) => {
192 await t.step("should create error with error code only", () => {
193 const error = new AuthorizationError("access_denied");
194 assertEquals(error.message, "Authorization failed: access_denied");
195 assertEquals(error.name, "AuthorizationError");
196 });
197
198 await t.step("should create error with error code and description", () => {
199 const error = new AuthorizationError("access_denied", "User denied the request");
200 assertEquals(error.message, "Authorization failed: access_denied - User denied the request");
201 assertEquals(error.name, "AuthorizationError");
202 });
203
204 await t.step("should be instance of OAuthError", () => {
205 const error = new AuthorizationError("access_denied");
206 assertInstanceOf(error, OAuthError);
207 assertInstanceOf(error, AuthorizationError);
208 });
209});