package web_test import ( "context" "fmt" "net/http" "net/url" "strings" "testing" "time" "github.com/netcell-it/deklarix/internal/store" ) func werkzeugForm() url.Values { return url.Values{ "name": {"ChatGPT Enterprise"}, "anbieter": {"OpenAI"}, "verarbeitungslaender": {"Irland"}, "avv_verfuegbar": {"on"}, "avv_url": {"https://example.com/avv"}, "training_opt_out": {"on"}, "training_standard": {"on"}, "aufbewahrung_tage": {"30"}, "zertifizierungen": {"ISO 27001, SOC 2"}, "geeignete_zwecke": {"Textgenerierung"}, "einschraenkungen": {""}, "letzte_pruefung": {"2026-08-01"}, "quelle": {"https://example.com/beleg"}, } } func TestBetreiberCanCreateZentralesWerkzeug(t *testing.T) { fs := newFakeStore() s := newServer(t, fs) betreiberCookie := seedAccountWithRole(t, fs, "Deklarix Betreiber", "betreiber@example.com", "betreiber") resp := postForm(t, s, betreiberCookie, "/betreiber/werkzeuge", werkzeugForm()) if resp.Code != http.StatusSeeOther { t.Fatalf("status = %d, want 303, body: %s", resp.Code, resp.Body.String()) } var w store.Werkzeug for _, wz := range fs.werkzeuge { w = wz } if w.Name != "ChatGPT Enterprise" || w.AccountID != nil { t.Fatalf("Werkzeug = %+v, unerwartete Werte", w) } if len(w.Zertifizierungen) != 2 { t.Errorf("Zertifizierungen = %v, want 2 Einträge", w.Zertifizierungen) } list := getWithCookie(t, s, betreiberCookie, "/betreiber/werkzeuge") if !strings.Contains(list.Body.String(), "ChatGPT Enterprise") { t.Errorf("expected the new werkzeug in the Katalogliste, got: %s", list.Body.String()) } } func TestBetreiberWerkzeugCreateRequiresLetztePruefungUndQuelle(t *testing.T) { fs := newFakeStore() s := newServer(t, fs) betreiberCookie := seedAccountWithRole(t, fs, "Deklarix Betreiber", "betreiber@example.com", "betreiber") form := werkzeugForm() form.Set("letzte_pruefung", "") resp := postForm(t, s, betreiberCookie, "/betreiber/werkzeuge", form) if resp.Code != http.StatusOK { t.Fatalf("status = %d, want 200 (Formular mit Fehler) ohne letzte_pruefung", resp.Code) } if len(fs.werkzeuge) != 0 { t.Errorf("expected no werkzeug to be created, got %d", len(fs.werkzeuge)) } } func TestBetreiberCanUpdateAndDeleteZentralesWerkzeug(t *testing.T) { fs := newFakeStore() s := newServer(t, fs) betreiberCookie := seedAccountWithRole(t, fs, "Deklarix Betreiber", "betreiber@example.com", "betreiber") postForm(t, s, betreiberCookie, "/betreiber/werkzeuge", werkzeugForm()) var id string for wid := range fs.werkzeuge { id = wid } editResp := getWithCookie(t, s, betreiberCookie, "/betreiber/werkzeuge/"+id) if editResp.Code != http.StatusOK || !strings.Contains(editResp.Body.String(), "ChatGPT Enterprise") { t.Fatalf("edit form status = %d, body: %s", editResp.Code, editResp.Body.String()) } updateForm := werkzeugForm() updateForm.Set("name", "ChatGPT Enterprise (aktualisiert)") updateResp := postForm(t, s, betreiberCookie, "/betreiber/werkzeuge/"+id, updateForm) if updateResp.Code != http.StatusSeeOther { t.Fatalf("update status = %d, body: %s", updateResp.Code, updateResp.Body.String()) } if fs.werkzeuge[id].Name != "ChatGPT Enterprise (aktualisiert)" { t.Errorf("Name = %q nach Update, unerwartet", fs.werkzeuge[id].Name) } deleteResp := postForm(t, s, betreiberCookie, "/betreiber/werkzeuge/"+id+"/loeschen", url.Values{}) if deleteResp.Code != http.StatusSeeOther { t.Fatalf("delete status = %d, body: %s", deleteResp.Code, deleteResp.Body.String()) } if _, ok := fs.werkzeuge[id]; ok { t.Error("expected the werkzeug to be deleted") } } // TestBetreiberCannotEditMandantenEigenesWerkzeug: ein mandantenspezifischer // Katalogeintrag (account_id gesetzt) ist über die Betreiber-Seiten // nicht erreichbar — das bleibt Sache des jeweiligen Mandanten. func TestBetreiberCannotEditMandantenEigenesWerkzeug(t *testing.T) { fs := newFakeStore() s := newServer(t, fs) betreiberCookie := seedAccountWithRole(t, fs, "Deklarix Betreiber", "betreiber@example.com", "betreiber") acc, err := fs.CreateAccount(context.Background(), "Mandant mit eigenem Werkzeug") if err != nil { t.Fatalf("CreateAccount: %v", err) } eigenesID := "eigenes-werkzeug" fs.werkzeuge[eigenesID] = store.Werkzeug{ID: eigenesID, AccountID: &acc.ID, Name: "Mandanten-eigenes Tool"} resp := getWithCookie(t, s, betreiberCookie, "/betreiber/werkzeuge/"+eigenesID) if resp.Code != http.StatusNotFound { t.Fatalf("status = %d, want 404 for a mandanten-eigenes werkzeug", resp.Code) } } func TestBetreiberWerkzeugListeFiltertNachSucheUndZweck(t *testing.T) { fs := newFakeStore() s := newServer(t, fs) betreiberCookie := seedAccountWithRole(t, fs, "Deklarix Betreiber", "betreiber@example.com", "betreiber") fs.werkzeuge["a"] = store.Werkzeug{ ID: "a", Name: "ChatGPT Enterprise", Anbieter: "OpenAI", Verarbeitungslaender: []string{"USA"}, GeeigneteZwecke: []string{"Textgenerierung"}, LetztePruefung: time.Now(), } fs.werkzeuge["b"] = store.Werkzeug{ ID: "b", Name: "DeepL Pro", Anbieter: "DeepL SE", Verarbeitungslaender: []string{"Deutschland"}, GeeigneteZwecke: []string{"Übersetzung"}, LetztePruefung: time.Now(), } resp := getWithCookie(t, s, betreiberCookie, "/betreiber/werkzeuge?q=deepl") if resp.Code != http.StatusOK { t.Fatalf("status = %d", resp.Code) } if strings.Contains(resp.Body.String(), "ChatGPT Enterprise") || !strings.Contains(resp.Body.String(), "DeepL Pro") { t.Fatalf("q=deepl sollte nur DeepL Pro zeigen, body: %s", resp.Body.String()) } resp = getWithCookie(t, s, betreiberCookie, "/betreiber/werkzeuge?zweck=Textgenerierung") if !strings.Contains(resp.Body.String(), "ChatGPT Enterprise") || strings.Contains(resp.Body.String(), "DeepL Pro") { t.Fatalf("zweck=Textgenerierung sollte nur ChatGPT Enterprise zeigen, body: %s", resp.Body.String()) } resp = getWithCookie(t, s, betreiberCookie, "/betreiber/werkzeuge?q=nichts-passt") if !strings.Contains(resp.Body.String(), "Keine Treffer") { t.Fatalf("erwartet 'Keine Treffer' bei erfolgloser Suche, body: %s", resp.Body.String()) } } func TestBetreiberWerkzeugListePaginiert(t *testing.T) { fs := newFakeStore() s := newServer(t, fs) betreiberCookie := seedAccountWithRole(t, fs, "Deklarix Betreiber", "betreiber@example.com", "betreiber") for i := 0; i < 25; i++ { id := fmt.Sprintf("wz-%02d", i) fs.werkzeuge[id] = store.Werkzeug{ ID: id, Name: fmt.Sprintf("Werkzeug %02d", i), Anbieter: "Testanbieter", LetztePruefung: time.Now(), } } resp := getWithCookie(t, s, betreiberCookie, "/betreiber/werkzeuge") if resp.Code != http.StatusOK { t.Fatalf("status = %d", resp.Code) } if !strings.Contains(resp.Body.String(), "Seite 1 von 2") { t.Fatalf("erwartet 'Seite 1 von 2' bei 25 Einträgen (20 pro Seite), body: %s", resp.Body.String()) } if !strings.Contains(resp.Body.String(), "Weiter") || strings.Contains(resp.Body.String(), "Zurück") { t.Fatalf("Seite 1 sollte einen Weiter-, aber keinen Zurück-Button zeigen") } resp = getWithCookie(t, s, betreiberCookie, "/betreiber/werkzeuge?page=2") if !strings.Contains(resp.Body.String(), "Seite 2 von 2") { t.Fatalf("erwartet 'Seite 2 von 2', body: %s", resp.Body.String()) } if strings.Contains(resp.Body.String(), "Weiter") || !strings.Contains(resp.Body.String(), "Zurück") { t.Fatalf("Seite 2 sollte einen Zurück-, aber keinen Weiter-Button zeigen") } }