benzcloud-server/internal/core/api/api_test.go

157 lines
5.0 KiB
Go

package api
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/benzjeremy/benzcloud-server/internal/core/auth"
"github.com/benzjeremy/benzcloud-server/internal/core/config"
"github.com/benzjeremy/benzcloud-server/internal/core/crypto"
"github.com/benzjeremy/benzcloud-server/internal/core/dns"
"github.com/benzjeremy/benzcloud-server/internal/core/drive"
"github.com/benzjeremy/benzcloud-server/internal/core/nebula"
"github.com/benzjeremy/benzcloud-server/internal/core/plugins"
)
func TestAPISetupAndPairing(t *testing.T) {
tempDir, err := os.MkdirTemp("", "benzcloud_api_test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tempDir)
cfg := config.DefaultConfig(tempDir)
salt, _ := crypto.GenerateSalt(16)
masterKey := crypto.DeriveKey("MasterTestKey123", salt)
authMgr, err := auth.NewManager(tempDir)
if err != nil {
t.Fatalf("auth.NewManager: %v", err)
}
dnsSrv := dns.NewServer("intern", "10.42.0.1", 15354)
nebulaMgr := nebula.NewManager(tempDir)
driveMgr, err := drive.NewDriveManager(tempDir, masterKey)
if err != nil {
t.Fatalf("drive.NewDriveManager: %v", err)
}
pluginMgr := plugins.NewManager(tempDir, "intern", "token123")
apiServer := NewServer(cfg, authMgr, dnsSrv, nebulaMgr, driveMgr, pluginMgr)
handler := apiServer.Handler()
// 1. Check /api/status before setup
rec := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/api/status", nil)
handler.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("Expected 200 on /api/status, got %d", rec.Code)
}
var statusResp map[string]interface{}
_ = json.NewDecoder(rec.Body).Decode(&statusResp)
if statusResp["setup_completed"] != false {
t.Fatal("Expected setup_completed to be false")
}
// 2. Perform /api/setup
setupPayload := map[string]string{
"base_domain": "benzjeremy.de",
"admin_username": "admin",
"admin_password": "SuperSecretAdminPassword2026!",
}
body, _ := json.Marshal(setupPayload)
rec = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodPost, "/api/setup", bytes.NewReader(body))
handler.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("Expected 200 on /api/setup, got %d: %s", rec.Code, rec.Body.String())
}
var setupResp map[string]interface{}
_ = json.NewDecoder(rec.Body).Decode(&setupResp)
adminToken := setupResp["session_token"].(string)
if cfg.BaseDomain != "benzjeremy.de" {
t.Fatalf("Expected base_domain benzjeremy.de, got %s", cfg.BaseDomain)
}
if !cfg.SetupCompleted {
t.Fatal("Expected SetupCompleted to be true")
}
// 3. Client-Kopplung via /api/pair
pairPayload := map[string]string{
"username": "admin",
"password": "SuperSecretAdminPassword2026!",
}
pairBody, _ := json.Marshal(pairPayload)
rec = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodPost, "/api/pair", bytes.NewReader(pairBody))
handler.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("Expected 200 on /api/pair, got %d: %s", rec.Code, rec.Body.String())
}
var pairResp map[string]interface{}
_ = json.NewDecoder(rec.Body).Decode(&pairResp)
if pairResp["status"] != "paired" {
t.Fatalf("Expected status paired, got %v", pairResp["status"])
}
if pairResp["client_config_yaml"] == "" {
t.Fatal("Expected non-empty client_config_yaml in pairing response")
}
// 4. Test Drive Upload & Download via API
fileContent := []byte("BenzCloud Enterprise Shared File via API")
rec = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodPost, "/api/drive/upload?path=report.txt", bytes.NewReader(fileContent))
req.Header.Set("Authorization", "Bearer "+adminToken)
handler.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("Drive upload failed with %d: %s", rec.Code, rec.Body.String())
}
rec = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodGet, "/api/drive/download?path=report.txt", nil)
req.Header.Set("Authorization", "Bearer "+adminToken)
handler.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("Drive download failed with %d: %s", rec.Code, rec.Body.String())
}
if !bytes.Equal(rec.Body.Bytes(), fileContent) {
t.Fatalf("Downloaded file mismatch: %s", rec.Body.String())
}
// 5. Test User Creation and Invariant Enforcement
userPayload := map[string]interface{}{
"username": "charlie",
"password": "CharliePassword2026!",
"permissions": map[string]bool{
"admin": false,
"drive": true,
"vpn": false, // Trying to revoke
"dns": false, // Trying to revoke
},
}
uBody, _ := json.Marshal(userPayload)
rec = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodPost, "/api/users", bytes.NewReader(uBody))
req.Header.Set("Authorization", "Bearer "+adminToken)
handler.ServeHTTP(rec, req)
if rec.Code != http.StatusCreated {
t.Fatalf("Create user failed: %d: %s", rec.Code, rec.Body.String())
}
var createdUser auth.User
_ = json.NewDecoder(rec.Body).Decode(&createdUser)
if !createdUser.Permissions.VPN || !createdUser.Permissions.DNS {
t.Fatal("CRITICAL INVARIANT VIOLATION: Created user must have VPN and DNS set to true!")
}
}