package api import ( "encoding/json" "io/fs" "net/http" "net/http/httptest" "os" "path/filepath" "testing" "testing/fstest" "forge-mesh/internal/config" "forge-mesh/internal/db" "forge-mesh/internal/forge" ) func TestHealthAndPublicRoutes(t *testing.T) { dir := t.TempDir() cfgPath := filepath.Join(dir, "config.json") writeTestConfig(t, cfgPath, dir) cfg, err := config.Load(cfgPath) if err != nil { t.Fatal(err) } if err := cfg.EnsureDataDirs(); err != nil { t.Fatal(err) } conn, err := db.Open(cfg.DatabasePath) if err != nil { t.Fatal(err) } defer conn.Close() kp, err := forge.LoadOrCreateKey(cfg.Forge.SigningKeyPath) if err != nil { t.Fatal(err) } tmplPath := filepath.Join("..", "..", "scripts", "install.sh.tpl") static := fstest.MapFS{"index.html": &fstest.MapFile{Data: []byte("")}} srv, err := NewServer(cfg, conn, static, "test", tmplPath, kp.PublicKeyHex()) if err != nil { t.Fatal(err) } ts := httptest.NewServer(srv.Handler()) defer ts.Close() resp, err := http.Get(ts.URL + "/api/v1/health") if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatalf("health: %d", resp.StatusCode) } resp, err = http.Get(ts.URL + "/install.sh") if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatalf("install.sh: %d", resp.StatusCode) } resp, err = http.Get(ts.URL + "/get") if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusFound { t.Fatalf("get redirect: %d", resp.StatusCode) } } func TestProtectedFleetRequiresAuth(t *testing.T) { dir := t.TempDir() cfgPath := filepath.Join(dir, "config.json") writeTestConfig(t, cfgPath, dir) cfg, _ := config.Load(cfgPath) _ = cfg.EnsureDataDirs() conn, _ := db.Open(cfg.DatabasePath) defer conn.Close() kp, _ := forge.LoadOrCreateKey(cfg.Forge.SigningKeyPath) tmplPath := filepath.Join("..", "..", "scripts", "install.sh.tpl") static := fstest.MapFS{"index.html": &fstest.MapFile{Data: []byte("ok")}} srv, err := NewServer(cfg, conn, static, "test", tmplPath, kp.PublicKeyHex()) if err != nil { t.Fatal(err) } ts := httptest.NewServer(srv.Handler()) defer ts.Close() resp, err := http.Get(ts.URL + "/api/v1/fleet/hosts") if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusUnauthorized { t.Fatalf("expected 401, got %d", resp.StatusCode) } req, _ := http.NewRequest(http.MethodGet, ts.URL+"/api/v1/fleet/hosts", nil) req.SetBasicAuth("admin", "changeme") resp, err = http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatalf("expected 200, got %d", resp.StatusCode) } var body map[string]any _ = json.NewDecoder(resp.Body).Decode(&body) if _, ok := body["hosts"]; !ok { t.Fatalf("expected hosts key in %v", body) } } func TestWSTicketFlow(t *testing.T) { dir := t.TempDir() cfgPath := filepath.Join(dir, "config.json") writeTestConfig(t, cfgPath, dir) cfg, _ := config.Load(cfgPath) _ = cfg.EnsureDataDirs() conn, _ := db.Open(cfg.DatabasePath) defer conn.Close() kp, _ := forge.LoadOrCreateKey(cfg.Forge.SigningKeyPath) tmplPath := filepath.Join("..", "..", "scripts", "install.sh.tpl") static := fs.FS(fstest.MapFS{"index.html": &fstest.MapFile{Data: []byte("ok")}}) srv, _ := NewServer(cfg, conn, static, "test", tmplPath, kp.PublicKeyHex()) ts := httptest.NewServer(srv.Handler()) defer ts.Close() req, _ := http.NewRequest(http.MethodPost, ts.URL+"/api/v1/ws/ticket", nil) req.SetBasicAuth("admin", "changeme") resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatalf("ticket: %d", resp.StatusCode) } var out struct { Ticket string `json:"ticket"` } if err := json.NewDecoder(resp.Body).Decode(&out); err != nil || out.Ticket == "" { t.Fatal("expected ticket") } } func writeTestConfig(t *testing.T, path, dir string) { t.Helper() content := `{ "listen_addr": ":0", "data_dir": "` + dir + `", "database_path": "` + filepath.Join(dir, "test.db") + `", "auth": { "basic_username": "admin", "basic_password": "changeme", "fleet_secret": "test-fleet-secret" }, "forge": { "signing_key_path": "` + filepath.Join(dir, "signing.key") + `", "artifacts_dir": "` + filepath.Join(dir, "artifacts") + `" } }` if err := os.WriteFile(path, []byte(content), 0o644); err != nil { t.Fatal(err) } }