Two xUnit projects, wired into Musify.slnx under a /Tests/ folder that
mirrors Core/ and Hosts/:
Musify.Domain.Tests Value-object logic (TrackAudio/TrackPictures'
IsProcessed/IsFailed/IsInProgress). Entities are
otherwise anemic EF models with no behavior to
test.
Musify.Application.Tests All ~45 Application handlers and services
(Albums, Tracks, PlayLists, Users, Mixes,
YouTube, plus TrackStreamIssuer,
UploadIntentValidator, YouTubeTrackProvisioner).
IDatabase exposes DbSet<T> directly, so handlers compose real LINQ queries
against it — that can't be expressed with a mock (no IQueryable provider
behind one). TestSupport/TestDatabase.cs is instead a real DbContext on a
private SQLite in-memory connection per test (fresh per test since xUnit
instantiates the test class per test method): fast, no external dependency,
and — unlike EF Core's InMemory provider — SQLite enforces real foreign keys
and supports transactions, which two handlers need. External dependencies
(IStorageService, IEventBus, IYouTubeMusicService, ...) are substituted with
NSubstitute.
TestSupport/TestEntities.cs and TestConfigurations.cs build fully-populated
domain entities and configuration POCOs with sensible defaults for every
required member, so no test method needs to fill in properties it doesn't
care about, and no builder ever hands back null.
Also, needed to make the Application layer itself testable without a real
Postgres or user-secrets:
- DatabaseDesignTimeFactory now falls back to the Database__ConnectionString
env var, and DesignSettings.json is optional — unblocks the `migrate`
one-shot container from deploy/compose.yml, which hit the same gap.
- Directory.Packages.props: xunit, xunit.runner.visualstudio, NSubstitute,
Microsoft.EntityFrameworkCore.Sqlite (pinned to the same 10.0.8 as the rest
of EF Core), Microsoft.NET.Test.Sdk, coverlet.collector. Also pins
SQLitePCLRaw.bundle_e_sqlite3 to 2.1.13 — EFCore.Sqlite 10.0.8 pulls 2.1.11
transitively, which has a known high-severity advisory (GHSA-2m69-gcr7-jv3q).
- backend/.dockerignore excludes Tests/: no application Dockerfile needs it
in its build context.
162 tests, all green: `dotnet test backend/Musify.slnx`.
Musify — Documentación
Musify es un backend de streaming de música: subir pistas, procesarlas (transcodificar el audio a .m4a y generar miniaturas), organizarlas en playlists y reproducirlas en streaming sin que los bytes pasen por la API.
Esta carpeta explica qué hace cada pieza, su propósito y por qué está diseñada así.
Índice
- architecture.md — Visión global: componentes, cómo encajan y los flujos principales.
- projects.md — Qué es y para qué sirve cada proyecto/solución.
- authentication.md — Zitadel (OIDC), validación de JWT, provisioning/sync de usuarios.
- storage.md — SeaweedFS (S3), estructura de claves, URLs prefirmadas, qué es público y qué privado.
- streaming.md — Streaming del audio (
.m4apor HTTP range) con stream-ticket (RS256) y el reverse proxy (StreamingGateway). - media-processing.md — El worker y los workflows de MassTransit (transcode de audio, miniaturas).
- development.md — Cómo levantar todo en local: contenedores, puertos y comandos.
- ../deploy/README.md — El deploy en detalle: compose, scripts y producción.
Mapa de componentes (alto nivel)
┌─────────────────────────────────────────────┐
Cliente ───► │ Musify.Api (:5111) API REST + emite tickets │
└─────────────────────────────────────────────┘
│ EF Core │ presigned (subidas) │ MassTransit (eventos)
▼ ▼ ▼
PostgreSQL SeaweedFS S3 (:8333) RabbitMQ ──► Worker (procesa)
(metadatos) (objetos) audio→.m4a, imágenes
▲
Cliente ───► StreamingGateway (:8081) ┘ sirve audio .m4a (valida ticket, proxy al filer)
Login: Cliente ◄──► Zitadel (:8080, OIDC) → Musify.Api valida el JWT
Stack
- .NET 10, C#. Minimal APIs (
Musify.Api), Worker Service (Musify.Worker), ASP.NET + YARP (Musify.StreamingGateway). - CQRS con [Mediator] (source-generator) y resultados con ErrorOr.
- EF Core 10 + PostgreSQL (metadatos).
- MassTransit 8 + RabbitMQ (procesado asíncrono con routing slips/sagas).
- SeaweedFS (almacenamiento de objetos compatible con S3, vía AWSSDK.S3).
- ffmpeg (transcode de audio a AAC/
.m4a), SixLabors.ImageSharp (miniaturas). - Zitadel (OIDC/OAuth2) para identidad.
Estructura del repositorio
Un solo repo: backend/ (los seis proyectos .NET), web-player/ (cliente web
SvelteKit), deploy/ (stacks de Docker Compose y scripts) y docs/.