A Deno-compatible AT Protocol OAuth client that serves as a drop-in replacement for @atproto/oauth-client-node
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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