diff --git a/.forgejo/workflows/publish-docker.yml b/.forgejo/workflows/publish-docker.yml index 7946cb8..f536d91 100644 --- a/.forgejo/workflows/publish-docker.yml +++ b/.forgejo/workflows/publish-docker.yml @@ -17,8 +17,13 @@ # read:package (Forgejo -> Settings -> Applications), le jeton # automatique de l'Action n'a pas ce droit. # -# Pas de job de test : ce projet n'a pas de suite de tests pour l'instant -# (contrairement à mediaDrop) - à ajouter ici le jour où il y en a une. +# Deux jobs : +# - test : typecheck + tests serveur (node:test, voir server/test/) et +# typecheck + build client, dans un conteneur node:20-alpine +# (docker:27-cli n'a pas Node, donc un conteneur séparé). +# - publish : build + push l'image Docker, seulement si `test` passe +# (needs: test) - clone les sources une seconde fois dans son +# propre conteneur, les deux jobs ne partagent pas de filesystem. name: Publier l'image Docker @@ -37,7 +42,49 @@ env: IMAGE: git.dev.kyuno.fr/${{ github.repository_owner }}/watchparty jobs: + test: + runs-on: ubuntu-latest + container: + image: node:20-alpine + steps: + # node:20-alpine n'a pas git préinstallé - même approche de clone + # manuel que le job publish (pas d'actions/checkout, action JS qui a + # ses propres soucis dans les conteneurs Alpine minimalistes ici). + - name: Récupérer les sources + run: | + set -eu + apk add --no-cache git + git init -q + git remote add origin "${{ github.server_url }}/${{ github.repository }}.git" + git fetch -q --depth 1 origin "${{ github.ref_name }}" + git checkout -q FETCH_HEAD + + - name: Serveur - installer les dépendances + working-directory: server + run: npm ci + + - name: Serveur - vérifier les types (src/ + test/) + working-directory: server + run: npm run typecheck + + - name: Serveur - lancer la suite de tests + working-directory: server + run: npm test + + - name: Client - installer les dépendances + working-directory: client + run: npm ci + + - name: Client - vérifier les types + working-directory: client + run: npm run typecheck + + - name: Client - build (esbuild) + working-directory: client + run: npm run build + publish: + needs: test runs-on: ubuntu-latest container: image: docker:27-cli diff --git a/README.md b/README.md index 43ced21..50d3a3c 100644 --- a/README.md +++ b/README.md @@ -59,15 +59,20 @@ albums/tracks, `PlaybackInfo`'s media streams) and actual HLS playback (video or audio) are still unverified. The TypeScript rewrite itself is separately verified: both projects -type-check in strict mode, the compiled server passes the same -unit/integration tests the pre-rewrite JS did (run against the compiled -output, not the source), and the compiled+bundled client was driven -through every page and both players end to end (mocked network/Socket.IO) -in a real browser. **Not verified**: an actual `docker build` of the new -multi-stage Dockerfile - this dev environment has no Docker - so treat the -first real build as the thing to double-check, even though it mirrors -exactly the `npm run build` commands already confirmed working locally -for both projects. +type-check in strict mode, the server has a real automated test suite +(`server/test/*.test.ts`, Node's built-in test runner, see Running +locally below) covering the party lifecycle, the music queue's +timer/vote/pause behavior, the socket protocol, and the catalog/streaming +overrides, and the compiled+bundled client was driven through every page +and both players end to end (mocked network/Socket.IO) in a real browser. +CI (Forgejo Actions) runs that same typecheck+test+build sequence on +every push and gates the Docker image publish on it passing — see +[docs/architecture.md](docs/architecture.md#continuous-integration). +**Not verified**: an actual `docker build` of the new multi-stage +Dockerfile - this dev environment has no Docker - so treat the first real +build as the thing to double-check, even though it mirrors exactly the +`npm run build` commands already confirmed working locally for both +projects. ## Project layout @@ -150,6 +155,16 @@ For active development, `client`'s `npm run watch` rebuilds on save, and `tsx` (no separate compile step needed while iterating) - run both in separate terminals. +Server tests (`server/test/*.test.ts`, Node's built-in test runner, no +extra dependencies or compile step): + +```bash +cd server && npm test +``` + +`server`'s `npm run typecheck` also covers `server/test/`, not just `src/` +(see `tsconfig.test.json`). + ## Deploying Build the Docker image from the **repo root** (it needs both `server/` and @@ -184,6 +199,8 @@ Then put it behind your existing reverse proxy like everything else - [x] Full TypeScript rewrite (client + server), component-style client views (.ts/.html/.css each), server-side singleton services per feature — see docs/architecture.md +- [x] Automated server test suite (`node:test`) + CI job gating the Docker + publish on typecheck+test+build passing — see docs/architecture.md - [ ] Try a full party end-to-end with two real accounts (still the main gap) - [ ] Tune transcoding target / support direct play when possible diff --git a/docs/architecture.md b/docs/architecture.md index f70b01b..548ab50 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1057,16 +1057,36 @@ which `esbuild` would do even with type errors present, since it transpiles without checking; the client's own separate `tsc --noEmit` step is what actually guards against that gap. -Server: every unit/integration test written earlier for the JS version +Server: every unit/integration check written earlier for the JS version (party lifecycle logging, the music queue's timer/vote/pause behavior including real `setTimeout`-based auto-advance, the socket-level integration through `SocketGateway`+`PartyService`+`MusicQueueService` together, the library-type filter, the playback-override-forcing logic) -was re-run against the **compiled** `server/dist` output, not the -TypeScript source - every one passed unchanged (same assertions, same -counts). The compiled server was also actually started (`node dist/index.js`) -and confirmed listening cleanly, both directly and via `tsx` for the dev -path. +was first re-run ad hoc against the **compiled** `server/dist` output to +confirm the port itself changed nothing behaviorally, then formalized +into a real, committed suite: `server/test/*.test.ts` (one file per +service - `musicQueueService`, `partyService`, `socketGateway`, +`catalogService`), using Node's built-in `node:test` + `node:assert/strict` +(zero new dependencies) and run straight against the TypeScript source via +`tsx --test` (`npm test`) - no compile step, no separate test-runner +dependency. A dedicated `tsconfig.test.json` (extends the base config, +overrides `rootDir` to `.` and adds `test/**/*.ts` to `include`) lets +`npm run typecheck` cover the test files too without pulling them into +`dist/` when `npm run build` (plain `tsc`, still scoped to `src/`) runs. +The compiled server was also actually started (`node dist/index.js`) and +confirmed listening cleanly, both directly and via `tsx` for the dev path. + +### Continuous integration + +`.forgejo/workflows/publish-docker.yml` gained a `test` job that runs +before `publish` (`needs: test`): server `npm run typecheck` + `npm test`, +then client `npm run typecheck` + `npm run build`. It runs in a +`node:20-alpine` container - a separate container from `publish`'s +`docker:27-cli`, since that one has no Node at all (that's also why both +jobs clone sources by hand with `git init`/`fetch`/`checkout` rather than +`actions/checkout`, which is a JS action and needs Node to run). The two +jobs don't share a filesystem, so each clones independently; `publish` +only starts once `test` succeeds. Client: since esbuild bundles everything into one `app.js` (no more individually-importable view modules the way separate `