Stalker Mock Server
A local mock implementation of the Stalker/Ministra portal API for development and end-to-end testing of IPTVnator. It has two separate test surfaces:
- the long-running generated catalog server on port
3210; - short-lived, stateful replay listeners used by Electron authentication E2E.
Overview
The mock server speaks the same portal.php HTTP protocol as a real Stalker
portal. Its scenario, IDs, and catalog structure are seeded from the connecting
MAC address. This means:
- The same MAC address stays consistent between reset/restart events and keeps the same seed-driven structure after regeneration.
- Ratings, some dates, and current-day EPG are volatile, so regenerated payloads are not byte-for-byte identical.
- Different MAC addresses have isolated state and seeded catalogs — use predefined scenario MACs for specific test conditions.
- Data is generated once per MAC on first request and cached until
/resetor process restart.
Quick Start
# Start the mock server (port 3210)
nx serve stalker-mock-server
# Or with file watching (auto-restarts on source changes)
nx run stalker-mock-server:serve-with-watch
# Or run both the mock server + Angular dev server in parallel
nx run-many --targets=serve --projects=stalker-mock-server,web
Then in IPTVnator, add a new Stalker portal:
- Portal URL:
http://localhost:3210/portal.php - MAC Address: one of the predefined scenarios below (or any MAC for auto-generated data)
Predefined Scenario MAC Addresses
| MAC Address | Scenario | Description |
|---|---|---|
00:1A:79:00:00:01 |
default | 8 categories per type, 40 items each — the balanced go-to for daily dev |
00:1A:79:FF:FF:FF |
large | 20 categories, 200 items each — stress-test pagination and virtual scroll |
00:1A:79:00:00:02 |
series-heavy | 15 series categories with 6 seasons × 10 episodes — test deep series navigation |
00:1A:79:00:00:03 |
minimal | 2 categories, 5 items — edge case testing (empty states, single items) |
00:1A:79:00:00:04 |
is-series | 60% of VOD items have is_series=1 — tests the Ministra lazy-season flow |
00:1A:79:00:00:05 |
embedded-series | 50% of VOD items have embedded series[] arrays — tests the embedded series flow |
00:1A:79:00:00:06 |
legacy-pagination | No get_all_channels support — tests the paginated get_ordered_list crawl fallback for the full ITV channel list |
00:1A:79:00:00:07 |
marketing-demo | 35 original poster movies with the newest 20 first — safe for screenshots and marketing |
<any other MAC> |
auto | MAC bytes choose the seed for an isolated generated catalog |
Configuration
| Environment Variable | Default | Description |
|---|---|---|
PORT |
3210 |
HTTP port the server listens on |
NODE_ENV |
development |
Node environment |
Utility Endpoints
| Endpoint | Method | Description |
|---|---|---|
/health |
GET |
Health check — returns { status: "ok" } |
/reset |
POST |
Clear all in-memory data and favorites (useful between test runs) |
API Coverage
All endpoints are served at GET /portal.php?action=<action>&... matching the real Stalker protocol:
| Action | Description |
|---|---|
handshake |
Returns a mock Bearer token |
do_auth |
Returns a mock user profile |
get_categories |
Category list filtered by type (itv/vod/series) |
get_genres |
Genre list (mirrors categories) |
get_ordered_list |
Paginated content list; if movie_id is present → returns seasons |
get_all_channels |
Complete ITV channel list in one response (type=itv only); excludes censored (adult) genres; disabled in the legacy-pagination scenario |
create_link |
Returns a real public HLS stream URL for playback |
favorites |
Add / remove / get favorites (in-memory, resets on restart) |
get_short_epg |
Current-and-upcoming EPG window for a channel (ch_id, size) |
get_epg_info |
Bulk EPG keyed by channel id for a requested period window |
Cover Images
Generated scenarios use Picsum Photos for cover images
and logos, so they need an internet connection to display artwork. The
marketing-demo scenario instead uses the committed, screenshot-safe poster
catalog shared with the Xtream mock. Stalker serves those PNGs itself from
/assets/marketing/poster/<slug>.png, so screenshots remain deterministic and
offline once the repository is checked out.
Stream URLs
create_link returns real public HLS test streams so video actually plays:
https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8https://playertest.longtailvideo.com/adaptive/oceans/oceans.m3u8https://playertest.longtailvideo.com/adaptive/bbbfull/bbbfull.m3u8
The stream chosen for a given item is deterministic based on the item's cmd string.
Using with Playwright E2E Tests
The Playwright config in apps/web-e2e/playwright.config.ts starts the mock server automatically alongside the Angular dev server when running e2e tests. See apps/web-e2e/src/stalker.e2e.ts for example stalker tests.
# Run all e2e tests (starts mock server automatically)
nx e2e web-e2e
# Or run only stalker-specific e2e tests
nx e2e web-e2e --grep "@stalker"
The test suite uses 00:1A:79:00:00:01 (default scenario) for most tests, and calls POST /reset in beforeEach to ensure a clean state between tests.
Stateful Authentication Replay
Authentication, redirects, cookies, response-classifier edge cases, refresh,
and custom endpoint layouts use fixtures under fixtures/replay/. These
fixtures are not served by the development listener on port 3210.
The Electron replay harness starts a capability-protected loopback control
plane, creates one allowlisted fixture run, and receives distinct ephemeral
listeners for every named origin. The Electron app receives only synthetic
portal inputs. Each run must be finalized for exact request cardinality and
terminal state, then disposed in finally.
Validate the complete committed corpus with:
pnpm run stalker:fixtures:validate
pnpm nx test stalker-mock-server
pnpm nx test stalker-fixture-tools
The local HAR converter is only a draft aid:
pnpm run stalker:fixtures:draft -- /absolute/input.har /absolute/output.json
It rejects repository inputs, unsafe files, unknown origins, oversized or
deep payloads, and secret-like evidence. Never commit real portal URLs,
credentials, account data, catalogs, artwork, or stream links. Review and
validate every generated draft before moving it into fixtures/replay/.
EPG Behavior
The mock server generates a 7-day EPG schedule for every ITV channel using 2-hour slots starting at the current UTC day boundary.
get_short_epgreturns the current program and upcoming items from that schedule, limited bysizeget_epg_inforeturns bulk data in the shape{ js: { data: Record<channelId, program[]> } }get_epg_infofilters the bulk response from the current UTC day start throughnow + period
Architecture
See docs/architecture/stalker-mock-server.md for full implementation details.
Project Structure
apps/stalker-mock-server/
├── fixtures/replay/ # Secret-scanned stateful fixtures
├── src/
│ ├── main.ts # Generated catalog CLI bootstrap
│ ├── app.ts # Import-safe Express app factory
│ └── app/
│ ├── replay/ # Isolated replay/control-plane runtime
│ ├── scenarios.ts # MAC → scenario config mapping
│ ├── data-generator.ts # Seeded faker data generation
│ ├── data-store.ts # Lazy per-MAC in-memory cache
│ ├── routes/
│ │ ├── portal.route.ts # /portal.php route
│ │ └── dispatch.ts # Shared Stalker action dispatcher
│ └── handlers/
│ ├── handshake.handler.ts
│ ├── do-auth.handler.ts
│ ├── get-categories.handler.ts
│ ├── get-ordered-list.handler.ts
│ ├── get-seasons.handler.ts
│ ├── create-link.handler.ts
│ ├── favorites.handler.ts
│ ├── get-epg-info.handler.ts
│ ├── get-short-epg.handler.ts
│ └── get-genres.handler.ts
├── project.json
├── tsconfig.json
└── README.md