Files
linsa-linsa-io/tests/jazz-stream-test.ts
Nikita 9c90b7db8d Add a new "test-jazz-stream" task to flow.toml for testing live stream recording flow, including setup instructions and verification steps.
Update CommentBox component to handle image uploads with validation, preview, and progress tracking, and to manage comments with Jazz container initialization.
2025-12-25 05:04:43 -08:00

195 lines
6.1 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Jazz Live Stream Recording Test
*
* This test:
* 1. Starts a local Jazz node server
* 2. Creates a ViewerAccount with Jazz
* 3. Simulates stream-guard POSTing video chunks
* 4. Tests the sync flow: API → Jazz FileStream
* 5. Verifies timeline visualization data
*/
import { co } from "jazz-tools"
import { ViewerAccount, StreamRecording, StreamRecordingList } from "../packages/web/src/lib/jazz/schema"
import { randomBytes } from "crypto"
const API_BASE = "http://localhost:3000"
const JAZZ_CLOUD_URL = "wss://cloud.jazz.tools/?key=jazz_cloud_demo"
async function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms))
}
async function testStreamRecording() {
console.log("🎷 [Test] Starting Jazz Live Stream Recording Test")
console.log("")
// Step 1: Create Jazz account
console.log("1⃣ Creating Jazz ViewerAccount...")
// Note: In a real test, we'd use jazz-tools to create an actual account
// For this test, we'll simulate the flow
const streamId = `test-stream-${Date.now()}`
const streamKey = `test-key-${randomBytes(8).toString("hex")}`
console.log(` Stream ID: ${streamId}`)
console.log(` Stream Key: ${streamKey}`)
console.log("")
// Step 2: Start recording via API
console.log("2⃣ Starting recording session...")
const startResponse = await fetch(`${API_BASE}/api/stream-recording?action=start`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
streamId,
title: "Test Live Stream",
startedAt: Date.now(),
streamKey,
metadata: {
width: 1920,
height: 1080,
fps: 30,
bitrate: 5000000,
},
}),
})
if (!startResponse.ok) {
throw new Error(`Failed to start recording: ${await startResponse.text()}`)
}
const startData = await startResponse.json()
console.log(` ✓ Recording started: ${JSON.stringify(startData)}`)
console.log("")
// Step 3: Upload video chunks
console.log("3⃣ Uploading video chunks...")
const numChunks = 5
for (let i = 0; i < numChunks; i++) {
// Create fake video chunk (256KB of random data)
const chunkData = randomBytes(256 * 1024)
const base64Data = chunkData.toString("base64")
const chunkResponse = await fetch(`${API_BASE}/api/stream-recording?action=chunk`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
streamId,
chunkIndex: i,
data: base64Data,
timestamp: Date.now() + (i * 1000), // 1 second apart
metadata: {
width: 1920,
height: 1080,
fps: 30,
bitrate: 5000000,
},
}),
})
if (!chunkResponse.ok) {
throw new Error(`Failed to upload chunk ${i}: ${await chunkResponse.text()}`)
}
const chunkData2 = await chunkResponse.json()
console.log(` ✓ Chunk ${i} uploaded (${Math.round(chunkData.length / 1024)}KB)`)
// Wait a bit between chunks to simulate real streaming
await sleep(100)
}
console.log("")
// Step 4: List recordings via API
console.log("4⃣ Fetching recordings from API...")
const listResponse = await fetch(`${API_BASE}/api/stream-recording`)
if (!listResponse.ok) {
throw new Error(`Failed to list recordings: ${await listResponse.text()}`)
}
const listData = await listResponse.json() as { recordings: any[] }
console.log(` ✓ Found ${listData.recordings.length} recording(s)`)
const ourRecording = listData.recordings.find(r => r.streamId === streamId)
if (!ourRecording) {
throw new Error("Our recording not found in list!")
}
console.log(` ✓ Recording found with ${ourRecording.chunks?.length || 0} chunks`)
console.log("")
// Step 5: Verify chunk files exist
console.log("5⃣ Verifying chunk files...")
const fs = await import("fs/promises")
const chunksDir = `/Users/nikiv/fork-i/garden-co/jazz/glide-storage/stream-recordings/${streamId}`
try {
const files = await fs.readdir(chunksDir)
const chunkFiles = files.filter(f => f.startsWith("chunk-"))
console.log(`${chunkFiles.length} chunk files found in ${chunksDir}`)
for (const file of chunkFiles.slice(0, 3)) {
const stats = await fs.stat(`${chunksDir}/${file}`)
console.log(` - ${file}: ${Math.round(stats.size / 1024)}KB`)
}
} catch (err) {
console.error(` ✗ Failed to read chunks directory: ${err}`)
}
console.log("")
// Step 6: End recording
console.log("6⃣ Ending recording session...")
const endResponse = await fetch(`${API_BASE}/api/stream-recording?action=end`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
streamId,
endedAt: Date.now(),
}),
})
if (!endResponse.ok) {
throw new Error(`Failed to end recording: ${await endResponse.text()}`)
}
const endData = await endResponse.json()
console.log(` ✓ Recording ended: ${JSON.stringify(endData)}`)
console.log("")
// Step 7: Summary
console.log("7⃣ Test Summary")
console.log(` Stream ID: ${streamId}`)
console.log(` Chunks uploaded: ${numChunks}`)
console.log(` Total data: ${Math.round((numChunks * 256))}KB`)
console.log(` Storage: /Users/nikiv/fork-i/garden-co/jazz/glide-storage/stream-recordings/${streamId}`)
console.log("")
// Step 8: Next steps
console.log("📝 Next Steps:")
console.log(" 1. Open Linsa at http://localhost:3000/streams")
console.log(" 2. The page will auto-sync this recording to Jazz FileStream")
console.log(" 3. Timeline will appear showing the 5 chunks")
console.log(" 4. Open Glide browser to see timeline on canvas")
console.log("")
console.log("✅ Test completed successfully!")
console.log("")
console.log("🎯 To see the timeline:")
console.log(" - Visit http://localhost:3000/streams")
console.log(" - Wait for auto-sync (5 seconds)")
console.log(" - Timeline will show the test stream")
console.log("")
}
// Run the test
testStreamRecording().catch((error) => {
console.error("")
console.error("❌ Test failed:", error)
console.error("")
process.exit(1)
})