import type { MediaInferenceProvider } from "@rakazo/adapter-kit"; import type { MediaActor, MediaIngestService, MediaMarketingService, MediaPrivacyService, MediaRenderService, MediaServiceDeps, MediaServices, } from "@rakazo/adapters"; import type { PlatformProfile } from "@rakazo/contracts"; import { mediaRpcShapes } from "@rakazo/contracts"; import type { MediaRepos, PrismaClient } from "@rakazo/db"; /* * In-memory stand-ins for the media service slices, for API tests only. They * record what the router hands them and do the minimum through the real * repositories; nothing here renders, detects, or publishes. */ export interface MediaServiceCall { service: "ingest" | "privacy" | "render" | "marketing"; method: string; actor: MediaActor; input: unknown; } export interface FakeMediaServicesOptions { repos: MediaRepos; mediaRoot: string; prisma?: PrismaClient; inference?: MediaInferenceProvider | null; profiles?: PlatformProfile[]; now?: () => Date; /** Render id → absolute output path the fake render service reports. */ renderOutputs?: Map; onUploadCompleted?: ( actor: MediaActor, input: { assetId: string; tempPath: string; sha256: string; fileName: string }, ) => Promise; } export function fakeMediaServices(options: FakeMediaServicesOptions): { services: MediaServices; calls: MediaServiceCall[]; } { const calls: MediaServiceCall[] = []; const { repos } = options; const record = ( service: MediaServiceCall["service"], method: string, actor: MediaActor, input: unknown, ) => { calls.push({ service, method, actor: { spaceId: actor.spaceId, userId: actor.userId }, input }); }; const ingest: MediaIngestService = { async pushItems(actor, input) { record("ingest", "pushItems", actor, input); return { created: 0, updated: 0, unchanged: 0, deleted: 0 }; }, async requestSync(actor, sourceId) { record("ingest", "requestSync", actor, sourceId); }, async uploadCompleted(actor, input) { record("ingest", "uploadCompleted", actor, input); await options.onUploadCompleted?.(actor, input); }, async recluster(actor) { record("ingest", "recluster", actor, null); }, async removeSource(actor, sourceId) { record("ingest", "removeSource", actor, sourceId); await repos.removeSource(actor.spaceId, sourceId); }, }; const privacy: MediaPrivacyService = { async prepareSources() { throw new Error("prepareSources is not part of the API fakes"); }, async gate() { throw new Error("gate is not part of the API fakes"); }, async createMark(actor, input) { record("privacy", "createMark", actor, input); return repos.createMark(actor.spaceId, input); }, async updateMark(actor, input) { record("privacy", "updateMark", actor, input); const { markId, ...patch } = input; return repos.updateMark(actor.spaceId, markId, patch); }, async removePerson(actor, personId) { record("privacy", "removePerson", actor, personId); await repos.removePersonCompletely(actor.spaceId, personId); }, }; const render: MediaRenderService = { async start(actor, input) { record("render", "start", actor, input); return repos.createRender(actor.spaceId, { ...input, createdBy: actor.userId }); }, async cancel(actor, renderId) { record("render", "cancel", actor, renderId); return repos.cancelRender(actor.spaceId, renderId); }, async outputPath(actor, renderId, fileName) { record("render", "outputPath", actor, { renderId, fileName }); const file = options.renderOutputs?.get(renderId); return file?.endsWith(`/${fileName}`) ? file : null; }, }; const marketing: MediaMarketingService = { async autoPlan(actor, input) { record("marketing", "autoPlan", actor, input); return []; }, async submitPlan(actor, input) { record("marketing", "submitPlan", actor, input); return { ok: false, errors: [] }; }, async introVariants(actor, input) { record("marketing", "introVariants", actor, input); return []; }, async thumbnails(actor, input) { record("marketing", "thumbnails", actor, input); return []; }, async copy(actor, input) { record("marketing", "copy", actor, input); const render = await repos.getRender(actor.spaceId, input.renderId); return { profileId: render.profileId, language: input.language, title: "Title", hook: "Hook", caption: "Caption", hashtags: [], issues: [], }; }, async createPublication(actor, input) { record("marketing", "createPublication", actor, input); return repos.createPublication(actor.spaceId, actor.userId, { ...input, trackingUrl: null, scheduledAt: input.scheduledAt ? new Date(input.scheduledAt) : null, }); }, async bundle(actor, publicationId) { record("marketing", "bundle", actor, publicationId); return repos.markPublicationBundled(actor.spaceId, publicationId, null); }, async cancelPublication(actor, publicationId) { record("marketing", "cancelPublication", actor, publicationId); return repos.cancelPublication(actor.spaceId, publicationId); }, async importMetricsCsv(actor, input) { record("marketing", "importMetricsCsv", actor, input); return { imported: 0, skipped: [] }; }, }; const deps = { prisma: options.prisma, repos, jobs: { publish: async () => undefined }, inference: options.inference ?? null, renderer: { id: "fake", hardwareEncoders: async () => ["h264_fake"], render: async () => { throw new Error("render is not part of the API fakes"); }, }, motion: { id: "fake", templates: () => [], render: async () => ({ path: "", hasAlpha: true }) }, publisher: { id: "fake", publish: async () => ({ status: "bundled", externalUrl: null }) }, shortener: null, sources: {}, mediaRoot: options.mediaRoot, profiles: async () => ({ profiles: new Map( (options.profiles ?? []).map((profile) => [ profile.id, { profile, origin: "builtin" as const }, ]), ), errors: [], }), artifacts: {}, model: { generate: async () => null }, publish: async () => undefined, now: options.now ?? (() => new Date()), } as unknown as MediaServiceDeps; return { services: { deps, ingest, privacy, render, marketing }, calls }; } /** Every RPC path of the media contract, e.g. "sources/list". */ export function mediaProcedurePaths(): string[] { const paths: string[] = []; const walk = (node: Record, prefix: string[]) => { for (const [key, value] of Object.entries(node)) { const child = value as Record; if ("input" in child && "output" in child) paths.push([...prefix, key].join("/")); else walk(child, [...prefix, key]); } }; walk(mediaRpcShapes, []); return paths; } export interface MediaSampleIds { sourceId: string; assetId: string; secondAssetId: string; segmentId: string; trackId: string; personId: string; markId: string; collectionId: string; brandKitId: string; projectId: string; planId: string; renderId: string; publicationId: string; mappingId: string; uploadId: string; } /** * How a procedure must treat another space's ids: refuse them as not found, * answer only from the caller's own space, or be covered by a dedicated test. */ export type ForeignExpectation = "not_found" | "scoped" | "dedicated"; /** A valid input for every media procedure, built from one space's ids. */ export function sampleMediaInputs( ids: MediaSampleIds, kit: unknown, ): Record { const at = "2026-09-01T10:00:00.000Z"; return { capabilities: { input: {}, foreign: "scoped" }, "sources/list": { input: {}, foreign: "scoped" }, "sources/createFolder": { input: { label: "Footage", path: "/" }, foreign: "dedicated" }, "sources/registerClient": { input: { kind: "ios_library", label: "Phone", deviceId: "device-sample" }, foreign: "scoped", }, "sources/pushItems": { input: { sourceId: ids.sourceId, items: [], syncToken: null, complete: true }, foreign: "not_found", }, "sources/sync": { input: { sourceId: ids.sourceId }, foreign: "not_found" }, "sources/remove": { input: { sourceId: ids.sourceId }, foreign: "not_found" }, "assets/search": { input: {}, foreign: "scoped" }, "assets/get": { input: { assetId: ids.assetId }, foreign: "not_found" }, "assets/setPaidOnly": { input: { segmentIds: [ids.segmentId], paidOnly: true }, foreign: "not_found", }, "assets/requestUpload": { input: { assetIds: [ids.assetId] }, foreign: "not_found" }, "uploads/begin": { input: { assetId: ids.secondAssetId, sizeBytes: 10, sha256: "a".repeat(64), fileName: "clip.mp4", }, foreign: "not_found", }, "uploads/status": { input: { uploadId: ids.uploadId }, foreign: "not_found" }, "uploads/complete": { input: { uploadId: ids.uploadId }, foreign: "not_found" }, "collections/list": { input: {}, foreign: "scoped" }, "collections/get": { input: { collectionId: ids.collectionId }, foreign: "not_found" }, "collections/rename": { input: { collectionId: ids.collectionId, title: "Renamed" }, foreign: "not_found", }, "collections/merge": { input: { collectionIds: [ids.collectionId, `${ids.collectionId}x`] }, foreign: "not_found", }, "collections/split": { input: { collectionId: ids.collectionId, atAssetId: ids.assetId }, foreign: "not_found", }, "collections/createManual": { input: { title: "Picks", assetIds: [ids.assetId] }, foreign: "not_found", }, "collections/recluster": { input: {}, foreign: "scoped" }, "people/list": { input: {}, foreign: "scoped" }, "people/update": { input: { personId: ids.personId, displayName: "X" }, foreign: "not_found" }, "people/merge": { input: { personIds: [ids.personId, `${ids.personId}x`] }, foreign: "not_found", }, "people/assignTrack": { input: { trackId: ids.trackId, personId: null }, foreign: "not_found" }, "people/recordConsent": { input: { personId: ids.personId, consentArtifactId: null, consentRecordedAt: at, ageVerificationMethod: "platform_verified", ageVerifiedAt: at, }, foreign: "not_found", }, "people/remove": { input: { personId: ids.personId }, foreign: "not_found" }, "marks/list": { input: { assetId: ids.assetId }, foreign: "scoped" }, "marks/create": { input: { kind: "tattoo", assetId: ids.assetId, prompt: { tMs: 0 }, treatment: "blur" }, foreign: "not_found", }, "marks/update": { input: { markId: ids.markId, label: "x" }, foreign: "not_found" }, "marks/remove": { input: { markId: ids.markId }, foreign: "not_found" }, "brandKits/list": { input: {}, foreign: "scoped" }, "brandKits/upsert": { input: { id: ids.brandKitId, kit }, foreign: "not_found" }, "brandKits/remove": { input: { id: ids.brandKitId }, foreign: "not_found" }, "brandKits/templates": { input: {}, foreign: "scoped" }, "brandKits/introVariants": { input: { id: ids.brandKitId }, foreign: "not_found" }, "profiles/list": { input: {}, foreign: "scoped" }, "profiles/get": { input: { profileId: "example-short-vertical" }, foreign: "scoped" }, "projects/list": { input: {}, foreign: "scoped" }, "projects/get": { input: { projectId: ids.projectId }, foreign: "not_found" }, "projects/create": { input: { title: "Teaser", goal: "teaser", collectionIds: [ids.collectionId], profileIds: ["example-short-vertical"], }, foreign: "not_found", }, "projects/update": { input: { projectId: ids.projectId, title: "X" }, foreign: "not_found" }, "projects/remove": { input: { projectId: ids.projectId }, foreign: "not_found" }, "projects/autoPlan": { input: { projectId: ids.projectId }, foreign: "not_found" }, "plans/submit": { input: { projectId: ids.projectId, edl: {} }, foreign: "not_found" }, "renders/start": { input: { planId: ids.planId, quality: "draft" }, foreign: "not_found" }, "renders/get": { input: { renderId: ids.renderId }, foreign: "not_found" }, "renders/cancel": { input: { renderId: ids.renderId }, foreign: "not_found" }, "renders/thumbnails": { input: { renderId: ids.renderId }, foreign: "not_found" }, "marketing/copy": { input: { renderId: ids.renderId }, foreign: "not_found" }, "publications/list": { input: { projectId: ids.projectId }, foreign: "scoped" }, "publications/create": { input: { renderId: ids.renderId }, foreign: "not_found" }, "publications/bundle": { input: { publicationId: ids.publicationId }, foreign: "not_found" }, "publications/markPublished": { input: { publicationId: ids.publicationId, externalUrl: null }, foreign: "not_found", }, "publications/cancel": { input: { publicationId: ids.publicationId }, foreign: "not_found" }, "metrics/mappings": { input: {}, foreign: "scoped" }, "metrics/saveMapping": { input: { id: ids.mappingId, name: "Imported", columns: { publication: "id", observedAt: "date" }, publicationKey: "id", }, foreign: "not_found", }, "metrics/importCsv": { input: { mappingId: ids.mappingId, csv: "id,date" }, foreign: "not_found", }, "metrics/record": { input: { publicationId: ids.publicationId, observedAt: at, views: 1, clicks: 0, conversions: 0, revenueCents: 0, currency: "EUR", }, foreign: "not_found", }, "metrics/funnel": { input: { projectId: ids.projectId }, foreign: "not_found" }, "metrics/segments": { input: { projectId: ids.projectId }, foreign: "not_found" }, "metrics/resetTeaserModel": { input: {}, foreign: "scoped" }, }; }