refactor: aufgeraeumt — Pakete hinten, Widgets vorne
Go: store, auth, weather und config sind eigene Pakete unter internal/. Vorher lagen fuenf Dateien in einem Topf, und jede kannte jede. Web: os.js hatte 1425 Zeilen und alles darin. Jetzt core.js (Zustand, Server, Einstellungen), glyphs.js (die Pixelraster), js/widgets/ mit einem Widget je Datei, os.js nur noch die Ansichten. Stylesheet nach css/. Die HoneyBar bleibt eine einzelne Datei ohne Importe — sie wird von fremden Seiten mit einer Zeile eingebunden, da darf nichts nachladen. Nebenbei: Das gebaute Binary lag mit 9,8 MB im Repo und ist jetzt ignoriert. Und der Test gegen eine zweite Versionsnummer geht nicht mehr auf os.js, sondern durch alle .js — sonst haette ihn dieser Umbau still ausgehebelt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d08b0a229c
commit
52deffa30e
20 files changed
+780
-624
No files matched your search
+49
-34
@@ -1,6 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"honeyos/internal/auth"
|
||||
"honeyos/internal/config"
|
||||
"honeyos/internal/store"
|
||||
"io/fs"
|
||||
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
@@ -98,18 +103,18 @@ func (f *fakeIdP) idToken(t *testing.T) string {
|
||||
|
||||
func newTestServer(t *testing.T, issuer string) *Server {
|
||||
t.Helper()
|
||||
store, err := OpenStore(dataPath(t.TempDir()))
|
||||
st, err := store.Open(store.DataPath(t.TempDir()))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg := Config{
|
||||
cfg := config.Config{
|
||||
PublicURL: "https://os.example.test",
|
||||
SessionSecret: strings.Repeat("k", 40),
|
||||
OIDCIssuer: issuer,
|
||||
OIDCClientID: "honeyos",
|
||||
OIDCClientSecret: "geheim",
|
||||
}
|
||||
return &Server{cfg: cfg, auth: NewAuth(cfg), store: store}
|
||||
return &Server{cfg: cfg, auth: auth.New(cfg), store: st}
|
||||
}
|
||||
|
||||
// TestAnmeldeablauf geht den Weg einmal ganz durch: Umleitung zum Anbieter,
|
||||
@@ -140,7 +145,7 @@ func TestAnmeldeablauf(t *testing.T) {
|
||||
|
||||
var stateCk *http.Cookie
|
||||
for _, c := range rec.Result().Cookies() {
|
||||
if c.Name == stateCookie {
|
||||
if c.Name == auth.StateCookie {
|
||||
stateCk = c
|
||||
}
|
||||
}
|
||||
@@ -163,9 +168,9 @@ func TestAnmeldeablauf(t *testing.T) {
|
||||
var sess, csrf *http.Cookie
|
||||
for _, c := range rec.Result().Cookies() {
|
||||
switch c.Name {
|
||||
case sessionCookie:
|
||||
case auth.SessionCookie:
|
||||
sess = c
|
||||
case csrfCookie:
|
||||
case auth.CSRFCookie:
|
||||
csrf = c
|
||||
}
|
||||
}
|
||||
@@ -215,7 +220,7 @@ func TestGefälschtesCookieZähltNicht(t *testing.T) {
|
||||
h := newTestServer(t, idp.URL).routes()
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/me", nil)
|
||||
req.AddCookie(&http.Cookie{Name: sessionCookie, Value: "c3ViLTk5OQ.9999999999.gefälscht"})
|
||||
req.AddCookie(&http.Cookie{Name: auth.SessionCookie, Value: "c3ViLTk5OQ.9999999999.gefälscht"})
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusUnauthorized {
|
||||
@@ -315,7 +320,7 @@ func TestOffenerBetrieb(t *testing.T) {
|
||||
if rec.Code != http.StatusNoContent {
|
||||
t.Fatalf("Schreiben im offenen Betrieb: %d", rec.Code)
|
||||
}
|
||||
if s.store.User(localSub).Settings["phosphor"] != "green" {
|
||||
if s.store.User(store.LocalSub).Settings["phosphor"] != "green" {
|
||||
t.Error("Einstellung im offenen Betrieb nicht gespeichert")
|
||||
}
|
||||
}
|
||||
@@ -326,7 +331,7 @@ func TestLeisteNurFürEingetrageneApps(t *testing.T) {
|
||||
idp := newFakeIdP(t, "honeyos")
|
||||
s := newTestServer(t, idp.URL)
|
||||
h := s.routes()
|
||||
if err := s.store.SetApps([]App{{ID: "aether", Name: "AETHER", URL: "https://cloud.example.test/"}}); err != nil {
|
||||
if err := s.store.SetApps([]store.App{{ID: "aether", Name: "AETHER", URL: "https://cloud.example.test/"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -361,7 +366,7 @@ func signIn(t *testing.T, h http.Handler) (sess, csrf *http.Cookie) {
|
||||
loc, _ := url.Parse(rec.Header().Get("Location"))
|
||||
var stateCk *http.Cookie
|
||||
for _, c := range rec.Result().Cookies() {
|
||||
if c.Name == stateCookie {
|
||||
if c.Name == auth.StateCookie {
|
||||
stateCk = c
|
||||
}
|
||||
}
|
||||
@@ -375,9 +380,9 @@ func signIn(t *testing.T, h http.Handler) (sess, csrf *http.Cookie) {
|
||||
h.ServeHTTP(rec, req)
|
||||
for _, c := range rec.Result().Cookies() {
|
||||
switch c.Name {
|
||||
case sessionCookie:
|
||||
case auth.SessionCookie:
|
||||
sess = c
|
||||
case csrfCookie:
|
||||
case auth.CSRFCookie:
|
||||
csrf = c
|
||||
}
|
||||
}
|
||||
@@ -390,18 +395,18 @@ func signIn(t *testing.T, h http.Handler) (sess, csrf *http.Cookie) {
|
||||
// ── Betrieb hinter einem Anmelde-Proxy ─────────────────────────────────────
|
||||
// Kein eigener Anmeldeschirm: Pangolin o. ä. hat die Tür schon zugemacht.
|
||||
|
||||
func proxyServer(t *testing.T, cfg Config) *Server {
|
||||
func proxyServer(t *testing.T, cfg config.Config) *Server {
|
||||
t.Helper()
|
||||
store, err := OpenStore(dataPath(t.TempDir()))
|
||||
st, err := store.Open(store.DataPath(t.TempDir()))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg.SessionSecret = strings.Repeat("k", 40)
|
||||
return &Server{cfg: cfg, auth: NewAuth(cfg), store: store}
|
||||
return &Server{cfg: cfg, auth: auth.New(cfg), store: st}
|
||||
}
|
||||
|
||||
func TestProxyBetriebLässtDurch(t *testing.T) {
|
||||
h := proxyServer(t, Config{ProxyAuth: true}).routes()
|
||||
h := proxyServer(t, config.Config{ProxyAuth: true}).routes()
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, httptest.NewRequest("GET", "/api/me", nil))
|
||||
@@ -420,7 +425,7 @@ func TestProxyBetriebLässtDurch(t *testing.T) {
|
||||
|
||||
// Nennt der Proxy einen Namen, bekommt jeder Name seine eigenen Einstellungen.
|
||||
func TestProxyKopfzeileTrenntEinstellungen(t *testing.T) {
|
||||
s := proxyServer(t, Config{ProxyAuth: true, UserHeader: "Remote-User"})
|
||||
s := proxyServer(t, config.Config{ProxyAuth: true, UserHeader: "Remote-User"})
|
||||
h := s.routes()
|
||||
|
||||
put := func(user, body string) {
|
||||
@@ -451,7 +456,7 @@ func TestAppListeBrauchtDenSchlüssel(t *testing.T) {
|
||||
idp := newFakeIdP(t, "honeyos")
|
||||
s := newTestServer(t, idp.URL)
|
||||
s.cfg.AppsKey = "geheim123"
|
||||
s.auth = NewAuth(s.cfg)
|
||||
s.auth = auth.New(s.cfg)
|
||||
h := s.routes()
|
||||
|
||||
get := func(mod func(*http.Request)) int {
|
||||
@@ -483,7 +488,7 @@ func TestSchlüsselDarfNichtSchreiben(t *testing.T) {
|
||||
idp := newFakeIdP(t, "honeyos")
|
||||
s := newTestServer(t, idp.URL)
|
||||
s.cfg.AppsKey = "geheim123"
|
||||
s.auth = NewAuth(s.cfg)
|
||||
s.auth = auth.New(s.cfg)
|
||||
h := s.routes()
|
||||
|
||||
req := httptest.NewRequest("PUT", "/api/apps", strings.NewReader(`[{"id":"x","url":"https://a.test"}]`))
|
||||
@@ -497,7 +502,7 @@ func TestSchlüsselDarfNichtSchreiben(t *testing.T) {
|
||||
|
||||
// Die Oberfläche muss den Schlüssel mitbekommen, sonst fragt sie sich selbst aus.
|
||||
func TestStartseiteTrägtDenSchlüssel(t *testing.T) {
|
||||
s := proxyServer(t, Config{ProxyAuth: true, AppsKey: "geheim123"})
|
||||
s := proxyServer(t, config.Config{ProxyAuth: true, AppsKey: "geheim123"})
|
||||
rec := httptest.NewRecorder()
|
||||
s.routes().ServeHTTP(rec, httptest.NewRequest("GET", "/", nil))
|
||||
|
||||
@@ -506,7 +511,7 @@ func TestStartseiteTrägtDenSchlüssel(t *testing.T) {
|
||||
}
|
||||
|
||||
// Ohne Schlüssel darf auch nichts drinstehen.
|
||||
s2 := proxyServer(t, Config{ProxyAuth: true})
|
||||
s2 := proxyServer(t, config.Config{ProxyAuth: true})
|
||||
rec = httptest.NewRecorder()
|
||||
s2.routes().ServeHTTP(rec, httptest.NewRequest("GET", "/", nil))
|
||||
if strings.Contains(rec.Body.String(), "honeyos-key") {
|
||||
@@ -518,7 +523,7 @@ func TestStartseiteTrägtDenSchlüssel(t *testing.T) {
|
||||
// jede Anfrage als angemeldet. Der Schlüssel darf dann NICHT auf diese Prüfung
|
||||
// zurückfallen, sonst gibt der freigegebene Pfad die Liste an jeden heraus.
|
||||
func TestSchlüsselGiltAuchHinterDemProxy(t *testing.T) {
|
||||
h := proxyServer(t, Config{ProxyAuth: true, AppsKey: "geheim123"}).routes()
|
||||
h := proxyServer(t, config.Config{ProxyAuth: true, AppsKey: "geheim123"}).routes()
|
||||
|
||||
get := func(key string) int {
|
||||
req := httptest.NewRequest("GET", "/api/apps", nil)
|
||||
@@ -543,11 +548,11 @@ func TestSchlüsselGiltAuchHinterDemProxy(t *testing.T) {
|
||||
|
||||
// Die Leiste stellt genau eine Anfrage — sie muss Liste und Aussehen bringen.
|
||||
func TestAppListeBringtDasAussehenMit(t *testing.T) {
|
||||
s := proxyServer(t, Config{ProxyAuth: true, AppsKey: "geheim123"})
|
||||
if err := s.store.SetSettings(localSub, map[string]string{"phosphor": "green", "glow": "off"}); err != nil {
|
||||
s := proxyServer(t, config.Config{ProxyAuth: true, AppsKey: "geheim123"})
|
||||
if err := s.store.SetSettings(store.LocalSub, map[string]string{"phosphor": "green", "glow": "off"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := s.store.SetApps([]App{{ID: "aether", Name: "AETHER", URL: "https://cloud.example.test"}}); err != nil {
|
||||
if err := s.store.SetApps([]store.App{{ID: "aether", Name: "AETHER", URL: "https://cloud.example.test"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -557,7 +562,7 @@ func TestAppListeBringtDasAussehenMit(t *testing.T) {
|
||||
s.routes().ServeHTTP(rec, req)
|
||||
|
||||
var got struct {
|
||||
Apps []App
|
||||
Apps []store.App
|
||||
Settings map[string]string
|
||||
}
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &got); err != nil {
|
||||
@@ -574,7 +579,7 @@ func TestAppListeBringtDasAussehenMit(t *testing.T) {
|
||||
// Die angezeigte Version muss vom Server kommen. Vorher stand sie zusätzlich in
|
||||
// os.js und blieb bei 0.2 stehen, während die Images schon 0.4 hiessen.
|
||||
func TestVersionKommtVomServer(t *testing.T) {
|
||||
h := proxyServer(t, Config{ProxyAuth: true}).routes()
|
||||
h := proxyServer(t, config.Config{ProxyAuth: true}).routes()
|
||||
|
||||
for _, pfad := range []string{"/api/public", "/api/me"} {
|
||||
rec := httptest.NewRecorder()
|
||||
@@ -586,14 +591,24 @@ func TestVersionKommtVomServer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Und in der Oberfläche darf keine zweite fest eingetragen sein.
|
||||
raw, err := webFS.ReadFile("web/os.js")
|
||||
// Und in der Oberfläche darf keine zweite fest eingetragen sein — in keiner
|
||||
// Datei, egal wie die Oberfläche später aufgeteilt wird.
|
||||
err := fs.WalkDir(webFS, ".", func(pfad string, d fs.DirEntry, err error) error {
|
||||
if err != nil || d.IsDir() || !strings.HasSuffix(pfad, ".js") {
|
||||
return err
|
||||
}
|
||||
raw, err := webFS.ReadFile(pfad)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.Contains(string(raw), `const VERSION = "`) {
|
||||
t.Errorf("%s trägt wieder eine eigene Version — die läuft mit den Image-Tags auseinander", pfad)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(string(raw), `const VERSION = "`) {
|
||||
t.Error("os.js trägt wieder eine eigene Version — die läuft mit den Image-Tags auseinander")
|
||||
}
|
||||
}
|
||||
|
||||
// Der Fall, nach dem Marc gefragt hat: Einstellungen am Rechner setzen, und die
|
||||
@@ -601,7 +616,7 @@ func TestVersionKommtVomServer(t *testing.T) {
|
||||
// also beim Standardnutzer — die Einstellungen können aber unter einem Namen
|
||||
// aus der Proxy-Kopfzeile liegen.
|
||||
func TestLeisteFindetDasAussehenAuchÜberDieSiteGrenze(t *testing.T) {
|
||||
s := proxyServer(t, Config{ProxyAuth: true, UserHeader: "Remote-User", AppsKey: "geheim123"})
|
||||
s := proxyServer(t, config.Config{ProxyAuth: true, UserHeader: "Remote-User", AppsKey: "geheim123"})
|
||||
h := s.routes()
|
||||
|
||||
// Am Rechner, durch den Proxy: grün.
|
||||
@@ -628,7 +643,7 @@ func TestLeisteFindetDasAussehenAuchÜberDieSiteGrenze(t *testing.T) {
|
||||
|
||||
// Bei mehreren Nutzern darf nicht geraten werden, wessen Farbe gilt.
|
||||
func TestBeiMehrerenNutzernWirdNichtGeraten(t *testing.T) {
|
||||
s := proxyServer(t, Config{ProxyAuth: true, UserHeader: "Remote-User", AppsKey: "geheim123"})
|
||||
s := proxyServer(t, config.Config{ProxyAuth: true, UserHeader: "Remote-User", AppsKey: "geheim123"})
|
||||
for name, farbe := range map[string]string{"marc": "green", "gast": "ice"} {
|
||||
if err := s.store.SetSettings("proxy:"+name, map[string]string{"phosphor": farbe}); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
Reference in new issue
Block a user