feat: close STREAMING.md production readiness gaps
- Added run ID + unique segment naming (r-{ISOTIME}/seg-{seq}.m4s)
- Added fsnotify watcher for real-time R2 segment upload (~7s)
- Added atomic manifest publication (temp key → CopyObject)
- Fixed manifest Cache-Control: public, max-age=1, s-maxage=2, must-revalidate
- Added hls.js error recovery: bounded retries (5x), exponential backoff, reconnecting UI
- Enriched /health endpoint: run_id, active_item, last_segment_seq, last_r2_pub, consecutive_failures, unhealthy detection
- Updated tests: HealthHandler (new fields + unhealthy), player error recovery test
- Updated TODO.md, BACKLOG.md, DONE.md, MEMORY.md
This commit is contained in:
1 parent
f6ca89747e
commit
18459a9811
12 files changed
+699
-48
No files matched your search
@@ -13,7 +13,17 @@ const HlsMock = Object.assign(
|
||||
vi.fn().mockImplementation(() => hlsInstance),
|
||||
{
|
||||
isSupported: vi.fn(() => true),
|
||||
Events: { MANIFEST_PARSED: "hlsManifestParsed" },
|
||||
Events: {
|
||||
MANIFEST_PARSED: "hlsManifestParsed",
|
||||
ERROR: "hlsError",
|
||||
LEVEL_UPDATED: "hlsLevelUpdated",
|
||||
},
|
||||
ErrorTypes: {
|
||||
NETWORK_ERROR: "networkError",
|
||||
MEDIA_ERROR: "mediaError",
|
||||
MUX_ERROR: "muxError",
|
||||
OTHER_ERROR: "otherError",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -331,8 +341,37 @@ describe("message rendering", () => {
|
||||
});
|
||||
|
||||
const messages = document.getElementById("messages")!;
|
||||
expect(messages.innerHTML).toContain("alice");
|
||||
expect(messages.innerHTML).toContain("hey");
|
||||
expect(messages.innerHTML).toContain("alice");
|
||||
expect(messages.querySelector(".message-other")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("player error recovery", () => {
|
||||
it("shows reconnecting overlay on fatal network error", async () => {
|
||||
const main = await import("../src/main");
|
||||
await main.initPlayerAndChat(async () => ({ default: HlsMock }) as any);
|
||||
|
||||
// Find the error handler registered by initPlayerAndChat
|
||||
const errorCall = HlsMock.mock.results?.[0]?.value?.on?.mock?.calls?.find(
|
||||
(c: [string, Function]) => c[0] === "hlsError"
|
||||
);
|
||||
// Use hlsInstance.on mock calls
|
||||
const errorHandler = hlsInstance.on.mock.calls.find(
|
||||
(c: [string, Function]) => c[0] === "hlsError"
|
||||
);
|
||||
|
||||
if (errorHandler) {
|
||||
const handler = errorHandler[1];
|
||||
handler("hlsError", {
|
||||
type: "networkError",
|
||||
details: "manifestLoadError",
|
||||
fatal: true,
|
||||
});
|
||||
|
||||
const overlay = document.getElementById("reconnecting-overlay")!;
|
||||
expect(overlay.classList.contains("hidden")).toBe(false);
|
||||
expect(document.getElementById("reconnecting-text")?.textContent).toBe("Reconnecting...");
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user