mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
audit(tests): fix skipped auth tests and produce audit report
- Refactored `tests/auth-service.test.mjs` to use `node:test` API so it is detected by the runner. - Removed console noise from `tests/watchHistory/watch-history-telemetry.test.mjs`. - Generated daily audit report `test-audit-report-2026-02-12.md`. - Updated test logs and tracking artifacts. Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
68b583fb9b
commit
83499ae5c4
7 files changed
+5759
-312
No files matched your search
+4
-13
@@ -1,14 +1,5 @@
|
|||||||
# Context: bitvid-perf-agent
|
# Context
|
||||||
|
This audit verifies the quality and coverage of the bitvid test suite.
|
||||||
|
Date: 2026-02-12
|
||||||
|
Agent: bitvid-test-audit-agent
|
||||||
|
|
||||||
## Mission
|
|
||||||
Daily, measurable improvement of app responsiveness and documentation accuracy.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
- **Repo:** `PR0M3TH3AN/bitvid` (unstable)
|
|
||||||
- **Focus:**
|
|
||||||
- P0: Fix unbounded concurrency in background tasks.
|
|
||||||
- Docs: Ensure `/content` upload guides match runtime behavior.
|
|
||||||
|
|
||||||
## Current Task (Run 1)
|
|
||||||
- Fix unbounded concurrency in `js/nostr/commentEvents.js`.
|
|
||||||
- Fix outdated CORS port in `content/docs/guides/upload-content.md`.
|
|
||||||
+2
-8
@@ -1,9 +1,3 @@
|
|||||||
# Decisions Log
|
# Decisions
|
||||||
|
- Using built-in Node.js test runner as discovered in scripts.
|
||||||
|
|
||||||
## Performance
|
|
||||||
- **2026-02-xx:** Use `pMap` with `RELAY_BACKGROUND_CONCURRENCY` (3) for comment publishing and fetching.
|
|
||||||
- **Rationale:** `Promise.all` floods the network when users have many relays. Bounding concurrency prevents resource exhaustion.
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
- **2026-02-xx:** Standardize local dev port to 3000 in docs.
|
|
||||||
- **Rationale:** Matches `npm start` default and `README.md`.
|
|
||||||
+5452
-5
File diff suppressed because it is too large.
Load diff
@@ -1,5 +1,7 @@
|
|||||||
# TODO
|
# Test Audit To-Do
|
||||||
|
- [ ] Run initial test suite
|
||||||
|
- [ ] Collect coverage
|
||||||
|
- [ ] Identify flaky tests
|
||||||
|
- [ ] Audit critical files
|
||||||
|
- [ ] Fix P0 issues
|
||||||
|
|
||||||
- [ ] Fix unbounded concurrency in `js/nostr/commentEvents.js` using `pMap`.
|
|
||||||
- [ ] Update CORS port in `content/docs/guides/upload-content.md` to 3000.
|
|
||||||
- [ ] Run `npm run lint` and `npm run test:unit` to verify.
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# Test Audit Report - 2026-02-12
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
The unit test suite passed successfully (680 tests). A critical issue where `tests/auth-service.test.mjs` was skipped (resulting in 10% coverage for `authService.js`) was identified and fixed, raising coverage to 62%. Several critical production files remain with low coverage.
|
||||||
|
|
||||||
|
## Test Run Details
|
||||||
|
* **Command:** `node --import ./tests/test-helpers/setup-localstorage.mjs --test --experimental-test-coverage tests/**/*.test.mjs`
|
||||||
|
* **Result:** 680 passed, 0 failed.
|
||||||
|
* **Duration:** ~42s
|
||||||
|
|
||||||
|
## Critical Coverage Gaps
|
||||||
|
The following critical files have low coverage (< 50%):
|
||||||
|
|
||||||
|
| File | Line Coverage | Function Coverage | Impact |
|
||||||
|
|------|--------------|-------------------|--------|
|
||||||
|
| `js/webtorrent.js` | 12.25% | 4.00% | **Critical** (Playback core) |
|
||||||
|
| `js/userBlocks.js` | 18.92% | 26.47% | **High** (Moderation) |
|
||||||
|
| `js/nostr/nip46Client.js` | 27.42% | 44.59% | **High** (Remote Signing) |
|
||||||
|
| `js/views/VideoListView.js` | 48.81% | 36.04% | **Medium** (Main Feed UI) |
|
||||||
|
|
||||||
|
## Fixed Issues
|
||||||
|
1. **Auth Service Tests Skipped:** `tests/auth-service.test.mjs` was using raw async blocks without `node:test` imports, causing the runner to skip it.
|
||||||
|
* **Fix:** Refactored to use `describe`/`test` from `node:test`.
|
||||||
|
* **Result:** `js/services/authService.js` coverage improved from 10% to 62%.
|
||||||
|
2. **Console Noise:** Removed a leftover `console.log` in `tests/watchHistory/watch-history-telemetry.test.mjs`.
|
||||||
|
|
||||||
|
## Static Analysis Findings
|
||||||
|
* **Time Dependency:** Extensive use of `setTimeout` found in `tests/nostr/client.test.mjs`, `tests/moderation/video-card.test.mjs`, and others. These are potential sources of flakiness.
|
||||||
|
* **Skipped Tests:** `tests/nostr/sessionActor.test.mjs` skips a test when WebCrypto is unavailable.
|
||||||
|
* **Network Calls:** No unmocked external network calls detected in unit tests (mocks appear to be used).
|
||||||
|
|
||||||
|
## Recommendations
|
||||||
|
1. **Prioritize Webtorrent Tests:** Add unit tests for `js/webtorrent.js` to cover the torrent client wrapper logic.
|
||||||
|
2. **Moderation Coverage:** Expand `tests/user-blocks.test.mjs` (if it exists) or create it to cover `js/userBlocks.js`.
|
||||||
|
3. **Refactor Timeouts:** Replace `await new Promise(r => setTimeout(r, N))` with deterministic `waitFor` helpers in `tests/nostr/client.test.mjs` to reduce flakiness risk.
|
||||||
+260
-281
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import "./test-helpers/setup-localstorage.mjs";
|
import "./test-helpers/setup-localstorage.mjs";
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import { test, describe, beforeEach } from "node:test";
|
||||||
|
|
||||||
import AuthService from "../js/services/authService.js";
|
import AuthService from "../js/services/authService.js";
|
||||||
import {
|
import {
|
||||||
@@ -70,162 +71,134 @@ function resetState() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await (async () => {
|
describe("AuthService", () => {
|
||||||
resetState();
|
beforeEach(resetState);
|
||||||
setPubkey(SAMPLE_PUBKEY);
|
|
||||||
|
|
||||||
const calls = [];
|
test("applyPostLoginState success flow", async () => {
|
||||||
let resolveBlocks;
|
setPubkey(SAMPLE_PUBKEY);
|
||||||
|
|
||||||
const service = new AuthService({
|
const calls = [];
|
||||||
userBlocks: {
|
let resolveBlocks;
|
||||||
loadBlocks: () => {
|
|
||||||
calls.push("blocks:start");
|
const service = new AuthService({
|
||||||
return new Promise((resolve) => {
|
userBlocks: {
|
||||||
resolveBlocks = resolve;
|
loadBlocks: () => {
|
||||||
});
|
calls.push("blocks:start");
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolveBlocks = resolve;
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
relayManager: {
|
||||||
relayManager: {
|
loadRelayList: () => {
|
||||||
loadRelayList: () => {
|
calls.push("relays:start");
|
||||||
calls.push("relays:start");
|
return Promise.resolve("relays");
|
||||||
return Promise.resolve("relays");
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
|
|
||||||
|
service.loadOwnProfile = () => {
|
||||||
|
calls.push("profile:start");
|
||||||
|
return Promise.resolve({ name: "Test User" });
|
||||||
|
};
|
||||||
|
|
||||||
|
const promise = service.applyPostLoginState();
|
||||||
|
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||||
|
|
||||||
|
assert.deepEqual(calls, ["relays:start", "blocks:start", "profile:start"]);
|
||||||
|
|
||||||
|
resolveBlocks();
|
||||||
|
|
||||||
|
const result = await promise;
|
||||||
|
const detail = result.detail;
|
||||||
|
await result.completionPromise;
|
||||||
|
|
||||||
|
assert.equal(detail.pubkey, SAMPLE_PUBKEY);
|
||||||
|
assert.equal(detail.blocksLoaded, true);
|
||||||
|
assert.equal(detail.relaysLoaded, true);
|
||||||
|
assert.deepEqual(detail.profile, { name: "Test User" });
|
||||||
});
|
});
|
||||||
|
|
||||||
service.loadOwnProfile = () => {
|
test("applyPostLoginState with deferBlocks: true", async () => {
|
||||||
calls.push("profile:start");
|
setPubkey(SAMPLE_PUBKEY);
|
||||||
return Promise.resolve({ name: "Test User" });
|
|
||||||
};
|
|
||||||
|
|
||||||
const promise = service.applyPostLoginState();
|
const calls = [];
|
||||||
|
// We leave resolveBlocks undefined/pending forever to simulate slow/hanging blocks
|
||||||
|
let resolveBlocks;
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
const service = new AuthService({
|
||||||
|
userBlocks: {
|
||||||
assert.deepEqual(calls, ["relays:start", "blocks:start", "profile:start"]);
|
loadBlocks: () => {
|
||||||
|
calls.push("blocks:start");
|
||||||
resolveBlocks();
|
return new Promise((resolve) => {
|
||||||
|
resolveBlocks = resolve;
|
||||||
const result = await promise;
|
});
|
||||||
const detail = result.detail;
|
},
|
||||||
await result.completionPromise;
|
|
||||||
|
|
||||||
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 calls = [];
|
|
||||||
// We leave resolveBlocks undefined/pending forever to simulate slow/hanging blocks
|
|
||||||
let resolveBlocks;
|
|
||||||
|
|
||||||
const service = new AuthService({
|
|
||||||
userBlocks: {
|
|
||||||
loadBlocks: () => {
|
|
||||||
calls.push("blocks:start");
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
resolveBlocks = resolve;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
relayManager: {
|
||||||
relayManager: {
|
loadRelayList: () => {
|
||||||
loadRelayList: () => {
|
calls.push("relays:start");
|
||||||
calls.push("relays:start");
|
return Promise.resolve("relays");
|
||||||
return Promise.resolve("relays");
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
|
|
||||||
|
service.loadOwnProfile = () => {
|
||||||
|
calls.push("profile:start");
|
||||||
|
return Promise.resolve({ name: "Test User" });
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test deferBlocks: true
|
||||||
|
const promise = service.applyPostLoginState({ deferBlocks: true });
|
||||||
|
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||||
|
|
||||||
|
// Blocks should NOT be started if deferred
|
||||||
|
// Wait, deferBlocks just means it's not awaited in completionPromise?
|
||||||
|
// No, checking code:
|
||||||
|
// if (!deferBlocks) { const blocksOperation = this.createBlocksLoadOperation... }
|
||||||
|
// So it shouldn't even be called/pushed to concurrentOps.
|
||||||
|
|
||||||
|
assert.deepEqual(calls, ["relays:start", "profile:start"]);
|
||||||
|
|
||||||
|
const result = await promise;
|
||||||
|
// This should resolve even if blocks are not loaded
|
||||||
|
await result.completionPromise;
|
||||||
|
|
||||||
|
assert.equal(result.detail.blocksLoaded, null);
|
||||||
|
assert.equal(result.detail.relaysLoaded, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
service.loadOwnProfile = () => {
|
test("applyPostLoginState partial failure", async () => {
|
||||||
calls.push("profile:start");
|
setPubkey(SAMPLE_PUBKEY);
|
||||||
return Promise.resolve({ name: "Test User" });
|
|
||||||
};
|
|
||||||
|
|
||||||
// Test deferBlocks: true
|
const logs = [];
|
||||||
const promise = service.applyPostLoginState({ deferBlocks: true });
|
const service = new AuthService({
|
||||||
|
userBlocks: {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
loadBlocks: () => {
|
||||||
|
throw new Error("blocks failed synchronously");
|
||||||
// Blocks should NOT be started if deferred
|
},
|
||||||
// Wait, deferBlocks just means it's not awaited in completionPromise?
|
|
||||||
// No, checking code:
|
|
||||||
// if (!deferBlocks) { const blocksOperation = this.createBlocksLoadOperation... }
|
|
||||||
// So it shouldn't even be called/pushed to concurrentOps.
|
|
||||||
|
|
||||||
assert.deepEqual(calls, ["relays:start", "profile:start"]);
|
|
||||||
|
|
||||||
const result = await promise;
|
|
||||||
// This should resolve even if blocks are not loaded
|
|
||||||
await result.completionPromise;
|
|
||||||
|
|
||||||
assert.equal(result.detail.blocksLoaded, null);
|
|
||||||
assert.equal(result.detail.relaysLoaded, true);
|
|
||||||
})();
|
|
||||||
|
|
||||||
await (async () => {
|
|
||||||
resetState();
|
|
||||||
setPubkey(SAMPLE_PUBKEY);
|
|
||||||
|
|
||||||
const logs = [];
|
|
||||||
const service = new AuthService({
|
|
||||||
userBlocks: {
|
|
||||||
loadBlocks: () => {
|
|
||||||
throw new Error("blocks failed synchronously");
|
|
||||||
},
|
},
|
||||||
},
|
relayManager: {
|
||||||
relayManager: {
|
loadRelayList: () => Promise.reject(new Error("relays failed")),
|
||||||
loadRelayList: () => Promise.reject(new Error("relays failed")),
|
},
|
||||||
},
|
});
|
||||||
});
|
|
||||||
|
|
||||||
service.log = (message, error) => {
|
service.log = (message, error) => {
|
||||||
logs.push({ message, error });
|
logs.push({ message, error });
|
||||||
};
|
};
|
||||||
|
|
||||||
service.loadOwnProfile = () => Promise.reject(new Error("profile failed"));
|
service.loadOwnProfile = () => Promise.reject(new Error("profile failed"));
|
||||||
|
|
||||||
const result = service.applyPostLoginState();
|
const result = service.applyPostLoginState();
|
||||||
const detail = result.detail;
|
const detail = result.detail;
|
||||||
await result.completionPromise;
|
await result.completionPromise;
|
||||||
|
|
||||||
assert.equal(detail.pubkey, SAMPLE_PUBKEY);
|
assert.equal(detail.pubkey, SAMPLE_PUBKEY);
|
||||||
assert.equal(detail.blocksLoaded, false);
|
assert.equal(detail.blocksLoaded, false);
|
||||||
assert.equal(detail.relaysLoaded, false);
|
assert.equal(detail.relaysLoaded, false);
|
||||||
assert.deepEqual(detail.profile, {
|
assert.deepEqual(detail.profile, {
|
||||||
name: "Unknown",
|
|
||||||
picture: "assets/svg/default-profile.svg",
|
|
||||||
about: "",
|
|
||||||
website: "",
|
|
||||||
banner: "",
|
|
||||||
lud16: "",
|
|
||||||
lud06: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
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 result = service.applyPostLoginState();
|
|
||||||
const detail = result.detail;
|
|
||||||
await result.completionPromise;
|
|
||||||
|
|
||||||
assert.deepEqual(detail, {
|
|
||||||
pubkey: null,
|
|
||||||
blocksLoaded: false,
|
|
||||||
relaysLoaded: false,
|
|
||||||
profile: {
|
|
||||||
name: "Unknown",
|
name: "Unknown",
|
||||||
picture: "assets/svg/default-profile.svg",
|
picture: "assets/svg/default-profile.svg",
|
||||||
about: "",
|
about: "",
|
||||||
@@ -233,170 +206,176 @@ await (async () => {
|
|||||||
banner: "",
|
banner: "",
|
||||||
lud16: "",
|
lud16: "",
|
||||||
lud06: "",
|
lud06: "",
|
||||||
},
|
});
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
await (async () => {
|
assert.equal(logs.length, 3);
|
||||||
resetState();
|
assert(logs.some(({ message }) => message.includes("block list")));
|
||||||
|
assert(logs.some(({ message }) => message.includes("relay list")));
|
||||||
const service = new AuthService();
|
assert(logs.some(({ message }) => message.includes("own profile")));
|
||||||
const postLogin = {
|
|
||||||
pubkey: SAMPLE_PUBKEY,
|
|
||||||
blocksLoaded: true,
|
|
||||||
relaysLoaded: false,
|
|
||||||
profile: { name: "Stub" },
|
|
||||||
};
|
|
||||||
|
|
||||||
service.applyPostLoginState = () => ({
|
|
||||||
detail: postLogin,
|
|
||||||
completionPromise: Promise.resolve(postLogin),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const detail = await service.login(SAMPLE_PUBKEY);
|
test("applyPostLoginState without pubkey", async () => {
|
||||||
|
const service = new AuthService();
|
||||||
|
const result = service.applyPostLoginState();
|
||||||
|
const detail = result.detail;
|
||||||
|
await result.completionPromise;
|
||||||
|
|
||||||
await detail.postLoginPromise;
|
assert.deepEqual(detail, {
|
||||||
|
pubkey: null,
|
||||||
assert.equal(detail.postLogin, postLogin);
|
blocksLoaded: false,
|
||||||
assert.deepEqual(detail.postLogin, postLogin);
|
relaysLoaded: false,
|
||||||
})();
|
profile: {
|
||||||
|
name: "Unknown",
|
||||||
await (async () => {
|
picture: "assets/svg/default-profile.svg",
|
||||||
resetState();
|
about: "",
|
||||||
|
website: "",
|
||||||
const adminCheckCalls = [];
|
banner: "",
|
||||||
const service = new AuthService({
|
lud16: "",
|
||||||
accessControl: {
|
lud06: "",
|
||||||
isLockdownActive: () => true,
|
|
||||||
isAdminEditor: (npub) => {
|
|
||||||
adminCheckCalls.push(npub);
|
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let error;
|
test("login mocks postLogin flow", async () => {
|
||||||
try {
|
const service = new AuthService();
|
||||||
await service.login(SAMPLE_PUBKEY);
|
const postLogin = {
|
||||||
} catch (err) {
|
pubkey: SAMPLE_PUBKEY,
|
||||||
error = err;
|
blocksLoaded: true,
|
||||||
}
|
relaysLoaded: false,
|
||||||
|
profile: { name: "Stub" },
|
||||||
assert(error instanceof Error);
|
|
||||||
assert.equal(error.code, "site-lockdown");
|
|
||||||
assert.match(error.message, /locked down/i);
|
|
||||||
assert.equal(adminCheckCalls.length, 1);
|
|
||||||
assert.equal(typeof adminCheckCalls[0], "string");
|
|
||||||
assert(adminCheckCalls[0].length > 0);
|
|
||||||
})();
|
|
||||||
|
|
||||||
await (async () => {
|
|
||||||
resetState();
|
|
||||||
|
|
||||||
const service = new AuthService({
|
|
||||||
accessControl: {
|
|
||||||
isLockdownActive: () => true,
|
|
||||||
isAdminEditor: () => true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const detail = await service.login(SAMPLE_PUBKEY);
|
|
||||||
|
|
||||||
assert.ok(detail);
|
|
||||||
assert.equal(detail.pubkey, SAMPLE_PUBKEY);
|
|
||||||
})();
|
|
||||||
|
|
||||||
// === Tests for loadOwnProfile logic ===
|
|
||||||
|
|
||||||
await (async () => {
|
|
||||||
resetState();
|
|
||||||
const PUBKEY = SAMPLE_PUBKEY;
|
|
||||||
|
|
||||||
// Test Fast Relay Success
|
|
||||||
{
|
|
||||||
const mockNostrClient = {
|
|
||||||
relays: ["wss://fast1.com", "wss://fast2.com", "wss://slow.com"],
|
|
||||||
pool: {
|
|
||||||
list: async (relays) => {
|
|
||||||
const url = relays[0];
|
|
||||||
if (url === "wss://fast1.com") {
|
|
||||||
return [{
|
|
||||||
pubkey: PUBKEY,
|
|
||||||
created_at: 100,
|
|
||||||
content: JSON.stringify({ name: "Fast User" })
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
return new Promise(() => {}); // Hang others
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleEvent: () => {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const service = new AuthService({ nostrClient: mockNostrClient });
|
service.applyPostLoginState = () => ({
|
||||||
// Override setProfileCacheEntry to avoid side effects or errors from real implementation if not needed,
|
detail: postLogin,
|
||||||
// but here we just let it run. However, the service needs nostrClient.pool
|
completionPromise: Promise.resolve(postLogin),
|
||||||
|
});
|
||||||
|
|
||||||
// We need to make sure setProfileCacheEntry doesn't fail.
|
const detail = await service.login(SAMPLE_PUBKEY);
|
||||||
// It imports from cache.js which uses profileCache.
|
|
||||||
// profileCache is imported from state/profileCache.js.
|
|
||||||
|
|
||||||
// The previous tests didn't seem to mock setProfileCacheEntry.
|
await detail.postLoginPromise;
|
||||||
|
|
||||||
const profile = await service.loadOwnProfile(PUBKEY);
|
assert.equal(detail.postLogin, postLogin);
|
||||||
assert.equal(profile.name, "Fast User");
|
assert.deepEqual(detail.postLogin, postLogin);
|
||||||
}
|
});
|
||||||
|
|
||||||
// Test All Relays Fail
|
test("login fails on lockdown", async () => {
|
||||||
{
|
const adminCheckCalls = [];
|
||||||
const mockNostrClient = {
|
const service = new AuthService({
|
||||||
relays: ["wss://fail1.com", "wss://fail2.com"],
|
accessControl: {
|
||||||
pool: {
|
isLockdownActive: () => true,
|
||||||
list: async () => {
|
isAdminEditor: (npub) => {
|
||||||
throw new Error("Failed");
|
adminCheckCalls.push(npub);
|
||||||
}
|
return false;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
handleEvent: () => {}
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const service = new AuthService({ nostrClient: mockNostrClient });
|
let error;
|
||||||
const profile = await service.loadOwnProfile(PUBKEY);
|
try {
|
||||||
assert.equal(profile.name, "Unknown");
|
await service.login(SAMPLE_PUBKEY);
|
||||||
}
|
} catch (err) {
|
||||||
|
error = err;
|
||||||
|
}
|
||||||
|
|
||||||
// Test Timeout Fallback
|
assert(error instanceof Error);
|
||||||
{
|
assert.equal(error.code, "site-lockdown");
|
||||||
let backgroundResolve;
|
assert.match(error.message, /locked down/i);
|
||||||
const backgroundPromise = new Promise(r => backgroundResolve = r);
|
assert.equal(adminCheckCalls.length, 1);
|
||||||
|
assert.equal(typeof adminCheckCalls[0], "string");
|
||||||
|
assert(adminCheckCalls[0].length > 0);
|
||||||
|
});
|
||||||
|
|
||||||
const mockNostrClient = {
|
test("login succeeds for admin on lockdown", async () => {
|
||||||
relays: ["wss://fast-fail.com", "wss://fast-fail2.com", "wss://background-ok.com", "wss://background-ok-2.com"],
|
const service = new AuthService({
|
||||||
|
accessControl: {
|
||||||
|
isLockdownActive: () => true,
|
||||||
|
isAdminEditor: () => true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const detail = await service.login(SAMPLE_PUBKEY);
|
||||||
|
|
||||||
|
assert.ok(detail);
|
||||||
|
assert.equal(detail.pubkey, SAMPLE_PUBKEY);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("loadOwnProfile", () => {
|
||||||
|
const PUBKEY = SAMPLE_PUBKEY;
|
||||||
|
|
||||||
|
test("Fast Relay Success", async () => {
|
||||||
|
const mockNostrClient = {
|
||||||
|
relays: ["wss://fast1.com", "wss://fast2.com", "wss://slow.com"],
|
||||||
pool: {
|
pool: {
|
||||||
list: async (relays) => {
|
list: async (relays) => {
|
||||||
const url = relays[0];
|
const url = relays[0];
|
||||||
if (url.includes("fast")) {
|
if (url === "wss://fast1.com") {
|
||||||
// Simulate timeout or error
|
return [{
|
||||||
throw new Error("Fast fail");
|
pubkey: PUBKEY,
|
||||||
}
|
created_at: 100,
|
||||||
if (url.includes("background")) {
|
content: JSON.stringify({ name: "Fast User" })
|
||||||
await backgroundPromise;
|
}];
|
||||||
return [{
|
|
||||||
pubkey: PUBKEY,
|
|
||||||
created_at: 200,
|
|
||||||
content: JSON.stringify({ name: "Background User" })
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
return new Promise(() => {}); // Hang others
|
||||||
|
}
|
||||||
},
|
},
|
||||||
handleEvent: () => {}
|
handleEvent: () => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
const service = new AuthService({ nostrClient: mockNostrClient });
|
const service = new AuthService({ nostrClient: mockNostrClient });
|
||||||
|
const profile = await service.loadOwnProfile(PUBKEY);
|
||||||
|
assert.equal(profile.name, "Fast User");
|
||||||
|
});
|
||||||
|
|
||||||
const profile = await service.loadOwnProfile(PUBKEY);
|
test("All Relays Fail", async () => {
|
||||||
assert.equal(profile.name, "Unknown");
|
const mockNostrClient = {
|
||||||
|
relays: ["wss://fail1.com", "wss://fail2.com"],
|
||||||
|
pool: {
|
||||||
|
list: async () => {
|
||||||
|
throw new Error("Failed");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleEvent: () => {}
|
||||||
|
};
|
||||||
|
|
||||||
// We resolve background promise to let background logic finish eventually (though test finishes here)
|
const service = new AuthService({ nostrClient: mockNostrClient });
|
||||||
backgroundResolve();
|
const profile = await service.loadOwnProfile(PUBKEY);
|
||||||
}
|
assert.equal(profile.name, "Unknown");
|
||||||
})();
|
});
|
||||||
|
|
||||||
|
test("Timeout Fallback", async () => {
|
||||||
|
let backgroundResolve;
|
||||||
|
const backgroundPromise = new Promise(r => backgroundResolve = r);
|
||||||
|
|
||||||
|
const mockNostrClient = {
|
||||||
|
relays: ["wss://fast-fail.com", "wss://fast-fail2.com", "wss://background-ok.com", "wss://background-ok-2.com"],
|
||||||
|
pool: {
|
||||||
|
list: async (relays) => {
|
||||||
|
const url = relays[0];
|
||||||
|
if (url.includes("fast")) {
|
||||||
|
// Simulate timeout or error
|
||||||
|
throw new Error("Fast fail");
|
||||||
|
}
|
||||||
|
if (url.includes("background")) {
|
||||||
|
await backgroundPromise;
|
||||||
|
return [{
|
||||||
|
pubkey: PUBKEY,
|
||||||
|
created_at: 200,
|
||||||
|
content: JSON.stringify({ name: "Background User" })
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleEvent: () => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const service = new AuthService({ nostrClient: mockNostrClient });
|
||||||
|
|
||||||
|
const profile = await service.loadOwnProfile(PUBKEY);
|
||||||
|
assert.equal(profile.name, "Unknown");
|
||||||
|
|
||||||
|
// We resolve background promise to let background logic finish eventually (though test finishes here)
|
||||||
|
backgroundResolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -221,4 +221,3 @@ await testPreparePlaybackLoggingPublishesOnce();
|
|||||||
await testPreparePlaybackLoggingIgnoresMetadata();
|
await testPreparePlaybackLoggingIgnoresMetadata();
|
||||||
await testCancelPlaybackLoggingFlushes();
|
await testCancelPlaybackLoggingFlushes();
|
||||||
|
|
||||||
console.log("watch-history-telemetry tests passed");
|
|
||||||
Reference in new issue
Block a user