Self-hosted multi-platform broadcast newsroom for activist collectives: publishing pipeline with group approval and DST-proof scheduling, TweetDeck-style column wall with hostile-content-safe rendering, live control room with Auto Director, bridged chat governance with polls and quorum, distributed sandboxed transcode workers, encrypted content-addressed archive, monthly analytics. MIT licensed. See README.md and docs/.
60 lines
2.4 KiB
TypeScript
60 lines
2.4 KiB
TypeScript
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
import { S3Storage } from "./storage.js";
|
|
|
|
/**
|
|
* Live roundtrip against any S3-compatible endpoint (S3_ENDPOINT env;
|
|
* dev = MinIO from infra compose). Skipped without env — the pure key-scheme
|
|
* suite in storage.test.ts always runs.
|
|
*/
|
|
const cfg = process.env.S3_ENDPOINT;
|
|
|
|
describe.skipIf(cfg === undefined)("S3 storage (live endpoint)", () => {
|
|
let storage: S3Storage;
|
|
|
|
beforeAll(() => {
|
|
storage = new S3Storage({
|
|
endpoint: cfg,
|
|
region: process.env.S3_REGION ?? "us-east-1",
|
|
accessKeyId: process.env.S3_ACCESS_KEY ?? "tactical-media-broadcast-hub",
|
|
secretAccessKey: process.env.S3_SECRET_KEY ?? "tactical-media-broadcast-hub_dev",
|
|
bucket: process.env.S3_BUCKET ?? "tactical-media-broadcast-hub-media-test",
|
|
});
|
|
});
|
|
|
|
afterAll(async () => {
|
|
// bucket intentionally retained for inspection; content is content-addressed test bytes
|
|
void storage;
|
|
});
|
|
|
|
it("put → get roundtrips bytes", async () => {
|
|
const payload = Buffer.from(`qa-roundtrip-${Date.now()}`);
|
|
const stored = await storage.putContent(payload, "image/png");
|
|
expect(stored.deduplicated).toBe(false);
|
|
const back = await storage.getObject(stored.key);
|
|
expect(back.equals(payload)).toBe(true);
|
|
});
|
|
|
|
it("identical bytes deduplicate (second put is a no-op)", async () => {
|
|
const payload = Buffer.from("dedupe-me-identical");
|
|
const first = await storage.putContent(payload, "video/mp4");
|
|
const second = await storage.putContent(Buffer.from("dedupe-me-identical"), "video/mp4");
|
|
expect(first.deduplicated).toBe(false);
|
|
expect(second.deduplicated).toBe(true);
|
|
expect(second.key).toBe(first.key);
|
|
expect(await storage.hasContent(second.hash, "video/mp4")).toBe(true);
|
|
});
|
|
|
|
it("same bytes, different content-type land on different keys", async () => {
|
|
const payload = Buffer.from("multi-format");
|
|
const asPng = await storage.putContent(payload, "image/png");
|
|
const asMp4 = await storage.putContent(payload, "video/mp4");
|
|
expect(asPng.key).not.toBe(asMp4.key);
|
|
});
|
|
|
|
it("presigns a GET url", async () => {
|
|
const stored = await storage.putContent(Buffer.from("presign-me"), "image/png");
|
|
const url = await storage.presign(stored.key, 60);
|
|
expect(url).toContain(encodeURIComponent(stored.key).split("%2F")[1] ?? stored.key.slice(7));
|
|
expect(url.startsWith("http")).toBe(true);
|
|
});
|
|
});
|