Live video on the AT Protocol
1import { bytesToMultibase } from "@atproto/crypto";
2import { ChildProcess } from "child_process";
3import fs from "fs/promises";
4import os from "os";
5import path from "path";
6import { privateKeyToAccount } from "viem/accounts";
7import getEnv from "../env";
8import makeNode from "../node";
9import { playbackTest } from "./playback-test";
10import { resumeLoopTest } from "./resume-loop-test";
11import { serverRestartTest } from "./server-restart-test";
12import { E2ETest, TestEnv } from "./test-env";
13import { randomPort } from "./util";
14
15const allTests: Record<string, E2ETest> = {
16 playback: playbackTest,
17 // sync: syncTest,
18 resume: resumeLoopTest,
19 serverRestart: serverRestartTest,
20};
21
22export const allTestNames = Object.keys(allTests);
23
24const series = process.env.STREAMPLACE_TEST_SERIES === "true";
25
26export default async function runTests(
27 tests: string[],
28 duration: string,
29 privateKey: `0x${string}`,
30): Promise<boolean> {
31 const testsToRun = [];
32 for (const test of tests) {
33 if (!allTests[test]) {
34 throw new Error(`Test ${test} not found`);
35 }
36 testsToRun.push(test);
37 }
38 try {
39 const results: string[] = [];
40 const funcs: (() => Promise<void>)[] = [];
41 for (const testName of testsToRun) {
42 const test = allTests[testName];
43 console.log(`============ running test ${testName} ============`);
44 let testProc: ChildProcess | undefined;
45 const testFunc = ((test) => {
46 return async () => {
47 try {
48 const { skipNode } = getEnv();
49 const hexKey = privateKey.slice(2); // Remove 0x prefix
50 const exportedKey = new Uint8Array(
51 hexKey.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)),
52 );
53 const multibaseKey = bytesToMultibase(exportedKey, "base58btc");
54 const account = privateKeyToAccount(privateKey);
55 const tmpDir = await fs.mkdtemp(
56 path.join(os.tmpdir(), "streamplace-test-"),
57 );
58
59 let testEnv: TestEnv = {
60 addr: "http://127.0.0.1:38080",
61 internalAddr: "http://127.0.0.1:39090",
62 privateKey: privateKey,
63 publicAddress: account.address.toLowerCase(),
64 testDuration: parseInt(duration),
65 multibaseKey,
66 env: {},
67 };
68 if (!skipNode) {
69 testEnv.env = {
70 SP_HTTP_ADDR: `127.0.0.1:${randomPort()}`,
71 SP_HTTP_INTERNAL_ADDR: `127.0.0.1:${randomPort()}`,
72 SP_DATA_DIR: tmpDir,
73 };
74 }
75 if (test.setup) {
76 testEnv = await test.setup(testEnv);
77 }
78 if (!skipNode) {
79 const { addr, internalAddr, proc } = await makeNode({
80 env: testEnv.env,
81 autoQuit: false,
82 });
83 testEnv.addr = addr;
84 testEnv.internalAddr = internalAddr;
85 testProc = proc;
86 }
87 const result = await test.test(testEnv);
88 results.push(result);
89 } catch (e) {
90 console.error("error running test", e.message);
91 results.push(e.message);
92 } finally {
93 if (testProc) {
94 testProc.kill("SIGTERM");
95 }
96 }
97 };
98 })(test);
99 funcs.push(testFunc);
100 }
101 if (series) {
102 for (const func of funcs) {
103 await func();
104 }
105 } else {
106 await Promise.all(funcs.map((f) => f()));
107 }
108 const failures = results.filter((r) => r !== null);
109 if (failures.length > 0) {
110 console.error("tests failed", failures.join(", "));
111 return false;
112 }
113 return true;
114 } catch (e) {
115 console.error("error running tests", e.message);
116 return false;
117 }
118}