Refactor post-login state loading and add tests

This commit is contained in:
thePR0M3TH3AN committed 2025-10-09 13:27:45 -04:00
1 parent c092e10188
commit 599c40c3cc
2 files changed
+243 -36

No files matched your search

+66 -36
View File
@@ -395,43 +395,73 @@ export default class AuthService {
async applyPostLoginState() {
const activePubkey = this.normalizeHexPubkey(getPubkey()) || getPubkey();
if (!activePubkey) {
return { pubkey: null };
}
let blocksLoaded = false;
if (this.userBlocks && typeof this.userBlocks.loadBlocks === "function") {
try {
await this.userBlocks.loadBlocks(activePubkey);
blocksLoaded = true;
} catch (error) {
this.log("[AuthService] Failed to load block list", error);
}
}
let relaysLoaded = false;
if (this.relayManager && typeof this.relayManager.loadRelayList === "function") {
try {
await this.relayManager.loadRelayList(activePubkey);
relaysLoaded = true;
} catch (error) {
this.log("[AuthService] Failed to load relay list", error);
}
}
let profile = null;
try {
profile = await this.loadOwnProfile(activePubkey);
} catch (error) {
this.log("[AuthService] Failed to load own profile", error);
}
return {
pubkey: activePubkey,
blocksLoaded,
relaysLoaded,
profile,
const detail = {
pubkey: activePubkey || null,
blocksLoaded: false,
relaysLoaded: false,
profile: null,
};
if (!activePubkey) {
return detail;
}
const schedule = (callback) => Promise.resolve().then(() => callback());
const operations = [];
if (this.userBlocks && typeof this.userBlocks.loadBlocks === "function") {
operations.push({
name: "blocksLoaded",
promise: schedule(() => this.userBlocks.loadBlocks(activePubkey)),
onFulfilled: () => true,
onRejected: (error) => {
this.log("[AuthService] Failed to load block list", error);
return false;
},
});
}
if (this.relayManager && typeof this.relayManager.loadRelayList === "function") {
operations.push({
name: "relaysLoaded",
promise: schedule(() => this.relayManager.loadRelayList(activePubkey)),
onFulfilled: () => true,
onRejected: (error) => {
this.log("[AuthService] Failed to load relay list", error);
return false;
},
});
}
operations.push({
name: "profile",
promise: schedule(() => this.loadOwnProfile(activePubkey)),
onFulfilled: (value) => value,
onRejected: (error) => {
this.log("[AuthService] Failed to load own profile", error);
return null;
},
});
const settled = await Promise.allSettled(
operations.map((operation) => operation.promise)
);
settled.forEach((result, index) => {
const operation = operations[index];
if (!operation) {
return;
}
if (result.status === "fulfilled") {
detail[operation.name] = operation.onFulfilled(result.value);
} else {
detail[operation.name] = operation.onRejected(result.reason);
}
});
return detail;
}
async logout() {
+177
View File
@@ -0,0 +1,177 @@
// Run with: node tests/auth-service.test.mjs
import "./test-helpers/setup-localstorage.mjs";
import assert from "node:assert/strict";
import AuthService from "../js/services/authService.js";
import {
setPubkey,
setCurrentUserNpub,
} from "../js/state/appState.js";
import {
mutateSavedProfiles,
setActiveProfilePubkey,
getProfileCacheMap,
} from "../js/state/cache.js";
if (typeof globalThis.window === "undefined") {
globalThis.window = {};
}
if (!globalThis.window.NostrTools) {
globalThis.window.NostrTools = {};
}
if (!globalThis.window.NostrTools.nip19) {
globalThis.window.NostrTools.nip19 = {
decode(value) {
if (typeof value !== "string") {
throw new Error("invalid value");
}
const trimmed = value.trim();
if (!trimmed) {
throw new Error("empty value");
}
if (trimmed.startsWith("npub1")) {
return { type: "npub", data: trimmed.slice(5) || "" };
}
return { type: "hex", data: trimmed };
},
npubEncode(hex) {
if (typeof hex !== "string") {
throw new Error("invalid hex");
}
const trimmed = hex.trim();
if (!trimmed) {
throw new Error("empty hex");
}
return `npub1${trimmed}`;
},
};
}
const SAMPLE_PUBKEY = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
function resetState() {
setPubkey(null);
setCurrentUserNpub(null);
setActiveProfilePubkey(null, { persist: false });
mutateSavedProfiles(() => [], { persist: false, persistActive: false });
const cache = getProfileCacheMap();
if (cache && typeof cache.clear === "function") {
cache.clear();
}
}
await (async () => {
resetState();
setPubkey(SAMPLE_PUBKEY);
const calls = [];
let resolveBlocks;
const service = new AuthService({
userBlocks: {
loadBlocks: () => {
calls.push("blocks:start");
return new Promise((resolve) => {
resolveBlocks = resolve;
});
},
},
relayManager: {
loadRelayList: () => {
calls.push("relays:start");
return Promise.resolve("relays");
},
},
});
service.loadOwnProfile = () => {
calls.push("profile:start");
return Promise.resolve({ name: "Test User" });
};
const promise = service.applyPostLoginState();
await Promise.resolve();
assert.deepEqual(calls, ["blocks:start", "relays:start", "profile:start"]);
resolveBlocks();
const detail = await promise;
assert.equal(detail.pubkey, SAMPLE_PUBKEY);
assert.equal(detail.blocksLoaded, true);
assert.equal(detail.relaysLoaded, true);
assert.deepEqual(detail.profile, { name: "Test User" });
})();
await (async () => {
resetState();
setPubkey(SAMPLE_PUBKEY);
const logs = [];
const service = new AuthService({
userBlocks: {
loadBlocks: () => {
throw new Error("blocks failed synchronously");
},
},
relayManager: {
loadRelayList: () => Promise.reject(new Error("relays failed")),
},
});
service.log = (message, error) => {
logs.push({ message, error });
};
service.loadOwnProfile = () => Promise.reject(new Error("profile failed"));
const detail = await service.applyPostLoginState();
assert.equal(detail.pubkey, SAMPLE_PUBKEY);
assert.equal(detail.blocksLoaded, false);
assert.equal(detail.relaysLoaded, false);
assert.equal(detail.profile, null);
assert.equal(logs.length, 3);
assert(logs.some(({ message }) => message.includes("block list")));
assert(logs.some(({ message }) => message.includes("relay list")));
assert(logs.some(({ message }) => message.includes("own profile")));
})();
await (async () => {
resetState();
const service = new AuthService();
const detail = await service.applyPostLoginState();
assert.deepEqual(detail, {
pubkey: null,
blocksLoaded: false,
relaysLoaded: false,
profile: null,
});
})();
await (async () => {
resetState();
const service = new AuthService();
const postLogin = {
pubkey: SAMPLE_PUBKEY,
blocksLoaded: true,
relaysLoaded: false,
profile: { name: "Stub" },
};
service.applyPostLoginState = async () => postLogin;
const detail = await service.login(SAMPLE_PUBKEY);
assert.equal(detail.postLogin, postLogin);
assert.deepEqual(detail.postLogin, postLogin);
})();