Tactical Media Broadcast Hub — initial release
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/.
This commit is contained in:
commit
aa5e262f3a
880 changed files with 111042 additions and 0 deletions
60
apps/api/src/storage/storage.integration.test.ts
Normal file
60
apps/api/src/storage/storage.integration.test.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
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);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue