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 211 lines 7.8 kB view raw
1/** 2 * @fileoverview Tests for Session class 3 */ 4 5import { assertEquals } from "@std/assert"; 6import { Session, type SessionData } from "../src/session.ts"; 7 8// Helper to create test session data 9function createTestSessionData(overrides: Partial<SessionData> = {}): SessionData { 10 return { 11 did: "did:plc:test123", 12 handle: "test.bsky.social", 13 pdsUrl: "https://test.bsky.social", 14 accessToken: "test_access_token", 15 refreshToken: "test_refresh_token", 16 dpopPrivateKeyJWK: { 17 kty: "EC", 18 crv: "P-256", 19 x: "test_x_value", 20 y: "test_y_value", 21 d: "test_d_value", 22 }, 23 dpopPublicKeyJWK: { 24 kty: "EC", 25 crv: "P-256", 26 x: "test_x_value", 27 y: "test_y_value", 28 }, 29 tokenExpiresAt: Date.now() + (60 * 60 * 1000), // 1 hour from now 30 ...overrides, 31 }; 32} 33 34Deno.test("Session - Constructor and Basic Properties", async (t) => { 35 const sessionData = createTestSessionData(); 36 const session = new Session(sessionData); 37 38 await t.step("should expose basic properties", () => { 39 assertEquals(session.did, "did:plc:test123"); 40 assertEquals(session.handle, "test.bsky.social"); 41 assertEquals(session.pdsUrl, "https://test.bsky.social"); 42 assertEquals(session.accessToken, "test_access_token"); 43 assertEquals(session.refreshToken, "test_refresh_token"); 44 }); 45 46 await t.step("should expose OAuthSession interface properties", () => { 47 assertEquals(session.sub, "did:plc:test123"); // same as DID 48 assertEquals(session.aud, "https://test.bsky.social"); // same as pdsUrl 49 }); 50}); 51 52Deno.test("Session - Expiration Logic", async (t) => { 53 await t.step("should not be expired for future tokens", () => { 54 const futureTime = Date.now() + (60 * 60 * 1000); // 1 hour from now 55 const sessionData = createTestSessionData({ tokenExpiresAt: futureTime }); 56 const session = new Session(sessionData); 57 58 assertEquals(session.isExpired, false); 59 }); 60 61 await t.step("should be expired for past tokens", () => { 62 const pastTime = Date.now() - (60 * 60 * 1000); // 1 hour ago 63 const sessionData = createTestSessionData({ tokenExpiresAt: pastTime }); 64 const session = new Session(sessionData); 65 66 assertEquals(session.isExpired, true); 67 }); 68 69 await t.step("should be expired for tokens expiring within 5 minutes", () => { 70 const soonTime = Date.now() + (2 * 60 * 1000); // 2 minutes from now (within 5min buffer) 71 const sessionData = createTestSessionData({ tokenExpiresAt: soonTime }); 72 const session = new Session(sessionData); 73 74 assertEquals(session.isExpired, true); 75 }); 76 77 await t.step("should not be expired for tokens expiring after 5 minutes", () => { 78 const laterTime = Date.now() + (10 * 60 * 1000); // 10 minutes from now (after 5min buffer) 79 const sessionData = createTestSessionData({ tokenExpiresAt: laterTime }); 80 const session = new Session(sessionData); 81 82 assertEquals(session.isExpired, false); 83 }); 84}); 85 86Deno.test("Session - Time Until Expiry", async (t) => { 87 await t.step("should calculate correct time until expiry", () => { 88 const futureTime = Date.now() + (30 * 60 * 1000); // 30 minutes from now 89 const sessionData = createTestSessionData({ tokenExpiresAt: futureTime }); 90 const session = new Session(sessionData); 91 92 const timeUntilExpiry = session.timeUntilExpiry; 93 // Allow small variance for test execution time 94 assertEquals(timeUntilExpiry > (29 * 60 * 1000), true); 95 assertEquals(timeUntilExpiry <= (30 * 60 * 1000), true); 96 }); 97 98 await t.step("should return 0 for expired tokens", () => { 99 const pastTime = Date.now() - (60 * 60 * 1000); // 1 hour ago 100 const sessionData = createTestSessionData({ tokenExpiresAt: pastTime }); 101 const session = new Session(sessionData); 102 103 assertEquals(session.timeUntilExpiry, 0); 104 }); 105}); 106 107Deno.test("Session - Serialization", async (t) => { 108 const originalData = createTestSessionData(); 109 const session = new Session(originalData); 110 111 await t.step("toJSON should return session data", () => { 112 const jsonData = session.toJSON(); 113 assertEquals(jsonData, originalData); 114 }); 115 116 await t.step("fromJSON should create identical session", () => { 117 const jsonData = session.toJSON(); 118 const restoredSession = Session.fromJSON(jsonData); 119 120 assertEquals(restoredSession.did, session.did); 121 assertEquals(restoredSession.handle, session.handle); 122 assertEquals(restoredSession.pdsUrl, session.pdsUrl); 123 assertEquals(restoredSession.accessToken, session.accessToken); 124 assertEquals(restoredSession.refreshToken, session.refreshToken); 125 assertEquals(restoredSession.isExpired, session.isExpired); 126 }); 127 128 await t.step("round-trip serialization should preserve all data", () => { 129 const restoredSession = Session.fromJSON(session.toJSON()); 130 assertEquals(restoredSession.toJSON(), originalData); 131 }); 132}); 133 134Deno.test("Session - Token Updates", async (t) => { 135 const sessionData = createTestSessionData(); 136 const session = new Session(sessionData); 137 const originalRefreshToken = session.refreshToken; 138 const originalExpiry = session.timeUntilExpiry; 139 140 await t.step("updateTokens should update access token and expiry", () => { 141 const newTokens = { 142 accessToken: "new_access_token", 143 expiresIn: 7200, // 2 hours (longer than original 1 hour) 144 }; 145 146 session.updateTokens(newTokens); 147 148 assertEquals(session.accessToken, "new_access_token"); 149 assertEquals(session.refreshToken, originalRefreshToken); // Should remain unchanged 150 151 // New expiry should be roughly 2 hours from now (longer than original) 152 const newExpiry = session.timeUntilExpiry; 153 assertEquals(newExpiry > originalExpiry, true); 154 assertEquals(newExpiry > (110 * 60 * 1000), true); // At least 110 minutes 155 assertEquals(newExpiry <= (120 * 60 * 1000), true); // At most 120 minutes 156 }); 157 158 await t.step("updateTokens should update refresh token when provided", () => { 159 const newTokens = { 160 accessToken: "newer_access_token", 161 refreshToken: "new_refresh_token", 162 expiresIn: 1800, // 30 minutes 163 }; 164 165 session.updateTokens(newTokens); 166 167 assertEquals(session.accessToken, "newer_access_token"); 168 assertEquals(session.refreshToken, "new_refresh_token"); 169 170 // Expiry should be roughly 30 minutes from now 171 const newExpiry = session.timeUntilExpiry; 172 assertEquals(newExpiry > (25 * 60 * 1000), true); // At least 25 minutes 173 assertEquals(newExpiry <= (30 * 60 * 1000), true); // At most 30 minutes 174 }); 175}); 176 177Deno.test("Session - Edge Cases", async (t) => { 178 await t.step("should handle zero expiry time", () => { 179 const sessionData = createTestSessionData({ tokenExpiresAt: 0 }); 180 const session = new Session(sessionData); 181 182 assertEquals(session.isExpired, true); 183 assertEquals(session.timeUntilExpiry, 0); 184 }); 185 186 await t.step("should handle very large expiry time", () => { 187 const farFuture = Date.now() + (365 * 24 * 60 * 60 * 1000); // 1 year from now 188 const sessionData = createTestSessionData({ tokenExpiresAt: farFuture }); 189 const session = new Session(sessionData); 190 191 assertEquals(session.isExpired, false); 192 assertEquals(session.timeUntilExpiry > (364 * 24 * 60 * 60 * 1000), true); 193 }); 194 195 await t.step("should handle minimal session data", () => { 196 const minimalData = createTestSessionData({ 197 did: "did:minimal", 198 handle: "minimal.test", 199 pdsUrl: "https://minimal.test", 200 accessToken: "min_access", 201 refreshToken: "min_refresh", 202 }); 203 const session = new Session(minimalData); 204 205 assertEquals(session.did, "did:minimal"); 206 assertEquals(session.handle, "minimal.test"); 207 assertEquals(session.pdsUrl, "https://minimal.test"); 208 assertEquals(session.accessToken, "min_access"); 209 assertEquals(session.refreshToken, "min_refresh"); 210 }); 211});