summaryrefslogtreecommitdiff
path: root/player-server/internal/api/server_test.go
blob: 89bc6c69df6899af0b4f9c4502dacbaeeab88fcd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package api

import (
	"log/slog"
	"testing"

	"codeberg.org/snonux/player/internal"
	"codeberg.org/snonux/player/internal/service"
)

// TestNewServerWithLogger_ErrorsOnNilConfig verifies that the constructor
// returns an error (rather than panicking) when deps.Config is nil. The
// caller in cmd/player/main.go relies on this to fail gracefully.
func TestNewServerWithLogger_ErrorsOnNilConfig(t *testing.T) {
	srv, err := NewServerWithLogger(ServerDeps{
		Config:   nil,
		StaticFS: nil,
	}, slog.Default())
	if err == nil {
		t.Fatal("expected error for nil Config, got nil")
	}
	if srv != nil {
		t.Fatalf("expected nil Server on error, got %v", srv)
	}
}

// TestNewServerWithLogger_ErrorsOnNilMediaStreamer verifies that the
// constructor refuses to build a Server when deps.MediaStreamer is nil.
// serveFileResult used to fall back to a default streamer at request time,
// which silently hid wiring mistakes and violated DIP. Construction now
// fails fast, mirroring the explicit-deps pattern in podcast/auth services.
func TestNewServerWithLogger_ErrorsOnNilMediaStreamer(t *testing.T) {
	srv, err := NewServerWithLogger(ServerDeps{
		Config:        &internal.Config{},
		MediaStreamer: nil,
	}, slog.Default())
	if err == nil {
		t.Fatal("expected error for nil MediaStreamer, got nil")
	}
	if srv != nil {
		t.Fatalf("expected nil Server on error, got %v", srv)
	}
}

// TestNewServerWithLogger_SucceedsWithMediaStreamer is a happy-path sanity
// check ensuring the new MediaStreamer validation does not reject valid
// dependency sets.
func TestNewServerWithLogger_SucceedsWithMediaStreamer(t *testing.T) {
	srv, err := NewServerWithLogger(ServerDeps{
		Config:        &internal.Config{},
		MediaStreamer: service.NewMediaStreamer(nil),
	}, slog.Default())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if srv == nil {
		t.Fatal("expected non-nil Server")
	}
}

// TestServer_PublicRouteRegistry verifies that the public-route registry on
// the Middleware is populated by Server.routes() — i.e. the new "register
// at declaration time" mechanism actually wires every previously-hardcoded
// public path. If a route is added in server.go without using the
// handlePublic* helpers, this test catches the regression before the
// silent-401/redirect bug bites users in production.
func TestServer_PublicRouteRegistry(t *testing.T) {
	srv, err := NewServerWithLogger(ServerDeps{
		Config:        &internal.Config{},
		MediaStreamer: service.NewMediaStreamer(nil),
	}, slog.Default())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	// Exact paths that must be public for bootstrap/login/probes to work
	// before any user exists.
	wantExact := []string{
		"/bootstrap.html", "/api/bootstrap", "/api/v1/auth/bootstrap",
		"/login.html", "/api/login", "/api/v1/auth/login",
		"/healthz", "/readyz",
		"/favicon.svg", "/favicon.ico", "/logo.svg", "/logo.png",
		"/manifest.json", "/sw.js",
	}
	for _, p := range wantExact {
		if !srv.mw.isPublic(p) {
			t.Errorf("expected %q to be public, but isPublic returned false", p)
		}
	}

	// Prefixed routes: static asset trees and dynamic share URLs.
	wantPrefixed := []string{
		"/css/site.css",
		"/js/app.js",
		"/images/logo.png",
		"/s/abcdef",
		"/s/abcdef/stream",
	}
	for _, p := range wantPrefixed {
		if !srv.mw.isPublic(p) {
			t.Errorf("expected %q to be public via prefix, but isPublic returned false", p)
		}
	}

	// Negative: an arbitrary protected path must NOT be public.
	if srv.mw.isPublic("/api/media") {
		t.Error("/api/media should not be public")
	}
	if srv.mw.isPublic("/") {
		t.Error("/ should not be public")
	}
}