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:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-12 14:36:45 +00:00
1 parent 68b583fb9b
commit 83499ae5c4
7 files changed
+5759 -312

No files matched your search

+4 -13
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+6 -4
View File
@@ -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.
+35
View File
@@ -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
View File
@@ -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");