Expand test coverage across server, agent, and web; fix bugs found during audit.
Adds hundreds of unit/integration/e2e tests, fixes WS bcrypt auth, config merge, fleet analytics, agent schedule/log tail, and documents stale PROBLEMS items. Updates PROBLEMS.md, README, and test scripts; ignores local spread-kits and coverage dirs.
This commit is contained in:
@@ -86,7 +86,9 @@ func TestBasicAuthMiddlewareHealthPublic(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasicAuthMiddlewareBuildDownloadPublic(t *testing.T) {
|
||||
// TestBasicAuthMiddlewareBuildDownloadRequiresAuth verifies that build download
|
||||
// routes are no longer publicly accessible — they require fleet secret or Basic Auth.
|
||||
func TestBasicAuthMiddlewareBuildDownloadRequiresAuth(t *testing.T) {
|
||||
resetAuthState(t)
|
||||
h := basicAuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
@@ -95,12 +97,25 @@ func TestBasicAuthMiddlewareBuildDownloadPublic(t *testing.T) {
|
||||
"/api/v1/builds/abc/download",
|
||||
"/api/v1/builds/abc/artifact/worker.exe",
|
||||
}
|
||||
for _, path := range paths {
|
||||
req := httptest.NewRequest(http.MethodGet, path, nil)
|
||||
for _, p := range paths {
|
||||
req := httptest.NewRequest(http.MethodGet, p, nil)
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusUnauthorized {
|
||||
t.Fatalf("%s without auth should be 401, got %d", p, rec.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// Fleet secret should grant access.
|
||||
SetAgentPathSecret("test-secret-abc")
|
||||
t.Cleanup(func() { SetAgentPathSecret("") })
|
||||
for _, p := range paths {
|
||||
req := httptest.NewRequest(http.MethodGet, p, nil)
|
||||
req.Header.Set("X-Fleet-Secret", "test-secret-abc")
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("%s should be public, got %d", path, rec.Code)
|
||||
t.Fatalf("%s with fleet secret should be 200, got %d", p, rec.Code)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,7 +227,7 @@ func TestBasicAuthMiddlewareAgentPathFleetSecret(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRouterPostUsersValidation(t *testing.T) {
|
||||
router, _ := newTestRouter(t)
|
||||
router, _, _, _ := newTestRouter(t)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/users", bytes.NewReader([]byte(`{}`)))
|
||||
req.SetBasicAuth(testAuthUser, testAuthPass)
|
||||
@@ -224,7 +239,7 @@ func TestRouterPostUsersValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRouterPostUsersSuccess(t *testing.T) {
|
||||
router, dataDir := newTestRouter(t)
|
||||
router, _, _, dataDir := newTestRouter(t)
|
||||
|
||||
body, _ := json.Marshal(map[string]string{"username": "newop", "password": "newpass"})
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/users", bytes.NewReader(body))
|
||||
@@ -250,7 +265,7 @@ func TestRouterPostUsersSuccess(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRouterRotateSecretNotConfigured(t *testing.T) {
|
||||
router, _ := newTestRouter(t)
|
||||
router, _, _, _ := newTestRouter(t)
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/server/rotate-secret", nil)
|
||||
req.SetBasicAuth(testAuthUser, testAuthPass)
|
||||
rec := httptest.NewRecorder()
|
||||
@@ -261,7 +276,7 @@ func TestRouterRotateSecretNotConfigured(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRouterRotateSecretSuccess(t *testing.T) {
|
||||
router, _ := newTestRouter(t)
|
||||
router, _, _, _ := newTestRouter(t)
|
||||
SetRotateSecretFn(func() (string, error) {
|
||||
return "new-secret-token-xyz", nil
|
||||
})
|
||||
@@ -284,7 +299,7 @@ func TestRouterRotateSecretSuccess(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRouterBuilderCancelNotFound(t *testing.T) {
|
||||
router, _ := newTestRouter(t)
|
||||
router, _, _, _ := newTestRouter(t)
|
||||
req := httptest.NewRequest(http.MethodDelete, "/api/v1/builder/cancel/missing-token", nil)
|
||||
req.SetBasicAuth(testAuthUser, testAuthPass)
|
||||
rec := httptest.NewRecorder()
|
||||
@@ -294,7 +309,10 @@ func TestRouterBuilderCancelNotFound(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouterBuildDownloadNoAuth(t *testing.T) {
|
||||
// TestRouterBuildDownloadAuth verifies that build download routes require either
|
||||
// the fleet secret (X-Fleet-Secret header) or Basic Auth — they are no longer
|
||||
// publicly accessible without credentials.
|
||||
func TestRouterBuildDownloadAuth(t *testing.T) {
|
||||
dataDir := t.TempDir()
|
||||
seedTestUsers(t, dataDir)
|
||||
database, err := db.New(dataDir)
|
||||
@@ -319,25 +337,50 @@ func TestRouterBuildDownloadNoAuth(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
const testSecret = "test-fleet-secret-12345"
|
||||
SetAgentPathSecret(testSecret)
|
||||
t.Cleanup(func() { SetAgentPathSecret("") })
|
||||
|
||||
wsHub := NewWSHub(database)
|
||||
cfg := &mockConfigProvider{}
|
||||
configHandler := NewConfigHandler(database, cfg)
|
||||
configHandler := NewConfigHandler(cfg)
|
||||
aiHandler := NewAIHandler(database)
|
||||
fleetHandler := NewFleetHandler(database, wsHub, aiHandler, nil, nil, pool.Config{})
|
||||
builderHandler := builder.NewHandler(database, dataDir, "", dataDir)
|
||||
blueprintHandler := NewBlueprintHandler(dataDir)
|
||||
router := NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, NewDropperHandler(database, nil), "", dataDir, nil)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/builds/"+buildID+"/download", nil)
|
||||
dlURL := "/api/v1/builds/" + buildID + "/download"
|
||||
|
||||
// 1. Unauthenticated → 401.
|
||||
req := httptest.NewRequest(http.MethodGet, dlURL, nil)
|
||||
rec := httptest.NewRecorder()
|
||||
router.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusUnauthorized {
|
||||
t.Fatalf("unauthenticated download should be 401, got %d", rec.Code)
|
||||
}
|
||||
|
||||
// 2. Valid fleet secret → 200.
|
||||
req = httptest.NewRequest(http.MethodGet, dlURL, nil)
|
||||
req.Header.Set("X-Fleet-Secret", testSecret)
|
||||
rec = httptest.NewRecorder()
|
||||
router.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("download should be public, got %d body=%s", rec.Code, rec.Body.String())
|
||||
t.Fatalf("fleet-secret download should be 200, got %d body=%s", rec.Code, rec.Body.String())
|
||||
}
|
||||
|
||||
// 3. Basic Auth → 200.
|
||||
req = httptest.NewRequest(http.MethodGet, dlURL, nil)
|
||||
req.SetBasicAuth("testuser", "testpass")
|
||||
rec = httptest.NewRecorder()
|
||||
router.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("basic-auth download should be 200, got %d body=%s", rec.Code, rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouterDropperInstallScriptsPublic(t *testing.T) {
|
||||
router, _ := newTestRouter(t)
|
||||
router, _, _, _ := newTestRouter(t)
|
||||
for _, path := range []string{"/install.sh", "/install.ps1"} {
|
||||
req := httptest.NewRequest(http.MethodGet, path, nil)
|
||||
req.Host = "forge.local:8989"
|
||||
@@ -353,7 +396,7 @@ func TestRouterDropperInstallScriptsPublic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRouterSPAFallbackUnknownRoute(t *testing.T) {
|
||||
router, _ := newTestRouter(t)
|
||||
router, _, _, _ := newTestRouter(t)
|
||||
req := httptest.NewRequest(http.MethodGet, "/unknown-dashboard-route", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
router.ServeHTTP(rec, req)
|
||||
@@ -376,7 +419,7 @@ func TestRouterNoWebRootFallback(t *testing.T) {
|
||||
|
||||
wsHub := NewWSHub(database)
|
||||
cfg := &mockConfigProvider{}
|
||||
configHandler := NewConfigHandler(database, cfg)
|
||||
configHandler := NewConfigHandler(cfg)
|
||||
aiHandler := NewAIHandler(database)
|
||||
fleetHandler := NewFleetHandler(database, wsHub, aiHandler, nil, nil, pool.Config{})
|
||||
builderHandler := builder.NewHandler(database, dataDir, "", dataDir)
|
||||
|
||||
Reference in New Issue
Block a user