init
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
// Copyright (C) 2026 Dcntrlized
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestRoutesConstruct builds exactly the wiring main() builds and fails on a
|
||||
// panic. http.ServeMux panics at registration time when the same pattern is
|
||||
// registered twice, which kills the process before it binds :8080 — and
|
||||
// go build/vet/gofmt/go test cannot see it unless a test constructs the mux.
|
||||
// This test exists because a duplicate /auth/logout registration shipped and
|
||||
// only a real launch caught it.
|
||||
func TestRoutesConstruct(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Fatalf("route registration panicked — the service would not boot: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
apiMux := newAPIMux()
|
||||
if apiMux == nil {
|
||||
t.Fatal("newAPIMux returned nil")
|
||||
}
|
||||
outer := newOuterMux(apiMux)
|
||||
if outer == nil {
|
||||
t.Fatal("newOuterMux returned nil")
|
||||
}
|
||||
}
|
||||
|
||||
// TestWebSocketPatternIsFree proves main.go leaves /ws/alarm to the realtime
|
||||
// layer: registering it on the outer mux must not conflict with anything
|
||||
// main.go registered (realtime.Register does exactly this at boot).
|
||||
func TestWebSocketPatternIsFree(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Fatalf("/ws/alarm conflicts with a pattern registered by main.go: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
outer := newOuterMux(newAPIMux())
|
||||
outer.Handle("/ws/alarm", http.NotFoundHandler())
|
||||
}
|
||||
|
||||
// TestManifestThroughOuterChain serves GET /_manifest through the real outer
|
||||
// mux (logging + CORS chain included), which is what a caller hits.
|
||||
func TestManifestThroughOuterChain(t *testing.T) {
|
||||
outer := newOuterMux(newAPIMux())
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
outer.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/_manifest", nil))
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("GET /_manifest = %d, want 200 (body: %s)", rec.Code, rec.Body.String())
|
||||
}
|
||||
if ct := rec.Header().Get("Content-Type"); !strings.Contains(ct, "application/json") {
|
||||
t.Fatalf("GET /_manifest Content-Type = %q, want application/json", ct)
|
||||
}
|
||||
if body := rec.Body.String(); !strings.Contains(body, `"name": "Alarm"`) && !strings.Contains(body, `"name":"Alarm"`) {
|
||||
t.Fatalf("GET /_manifest body does not name the service: %s", body)
|
||||
}
|
||||
}
|
||||
|
||||
// TestLogoutServesBothMethods pins the method-aware registration: GET (bodyless
|
||||
// no-op) and POST (revoke the refresh token) must both be routed, and any other
|
||||
// verb must be rejected with 405. The handler proxies to dcntrlized-auth, so the
|
||||
// requests carry a short deadline: the assertion is about routing, and the test
|
||||
// must not depend on (or wait for) a live auth service.
|
||||
func TestLogoutServesBothMethods(t *testing.T) {
|
||||
outer := newOuterMux(newAPIMux())
|
||||
|
||||
do := func(method string) int {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
||||
defer cancel()
|
||||
req := httptest.NewRequest(method, "/auth/logout", nil).WithContext(ctx)
|
||||
rec := httptest.NewRecorder()
|
||||
outer.ServeHTTP(rec, req)
|
||||
return rec.Code
|
||||
}
|
||||
|
||||
for _, method := range []string{http.MethodGet, http.MethodPost} {
|
||||
if code := do(method); code == http.StatusMethodNotAllowed || code == http.StatusNotFound {
|
||||
t.Fatalf("%s /auth/logout = %d, want the method to be routed", method, code)
|
||||
}
|
||||
}
|
||||
|
||||
if code := do(http.MethodPut); code != http.StatusMethodNotAllowed {
|
||||
t.Fatalf("PUT /auth/logout = %d, want 405", code)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user