Files
nexarch/connect/internal/statusapi/statusapi_test.go
patrickandClaude Sonnet 5 e18b415ebb
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (push) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Canceled after 0s
CI / Web (dms-browser) (push) Canceled after 0s
CI / Web (pdf-viewer) (push) Canceled after 0s
CI / Web (shl) (push) Canceled after 0s
Supply-Chain-Scan (Go) / govulncheck (push) Canceled after 0s
Supply-Chain-Scan (npm) / npm-audit (push) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Canceled after 0s
API-02: statusapi/connect-app HTTP-Dienst und web/connect-status Oberfläche
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 09:33:34 +02:00

119 lines
3.3 KiB
Go

package statusapi
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"gitea.perlbach24.de/scripte/nexarch/connect/internal/migrate"
"gitea.perlbach24.de/scripte/nexarch/connect/internal/syncjob"
"gitea.perlbach24.de/scripte/nexarch/connect/internal/testdb"
)
func newTestServer(t *testing.T) *Server {
t.Helper()
dsn := os.Getenv("TEST_TENANT_DSN")
if dsn == "" {
t.Skip("TEST_TENANT_DSN nicht gesetzt, Integrationstest uebersprungen")
}
pool := testdb.Pool(t, dsn)
root, err := filepath.Abs("../../migrations")
if err != nil {
t.Fatalf("migrationsverzeichnis: %v", err)
}
migrations, err := migrate.Load(root)
if err != nil {
t.Fatalf("migrationen laden: %v", err)
}
if _, err := migrate.Up(context.Background(), pool, migrations); err != nil {
t.Fatalf("migrationen anwenden: %v", err)
}
return NewServer(syncjob.NewStore(pool))
}
// TestHandleList_RequiresIdentity is Akzeptanzkriterium 1's negative side:
// without identity headers there is no status leak.
func TestHandleList_RequiresIdentity(t *testing.T) {
s := newTestServer(t)
mux := http.NewServeMux()
s.RegisterRoutes(mux)
for _, tc := range []struct {
name string
headers map[string]string
}{
{"kein header", nil},
{"nur user", map[string]string{"X-User-Id": "u1"}},
{"nur tenant", map[string]string{"X-Tenant-Slug": "acme"}},
} {
t.Run(tc.name, func(t *testing.T) {
req := httptest.NewRequest("GET", "/connectors/status", nil)
for k, v := range tc.headers {
req.Header.Set(k, v)
}
rec := httptest.NewRecorder()
mux.ServeHTTP(rec, req)
if rec.Code != http.StatusUnauthorized {
t.Fatalf("expected 401, got %d", rec.Code)
}
})
}
}
// TestHandleList_ReturnsScheduleAndDiagnosis covers AK1-3: status per
// connector at a glance, last run visible, error diagnosis from the log.
func TestHandleList_ReturnsScheduleAndDiagnosis(t *testing.T) {
s := newTestServer(t)
mux := http.NewServeMux()
s.RegisterRoutes(mux)
ctx := context.Background()
ref := syncjob.Ref{TenantSlug: "acme", ConnectorName: "testconnector", ConnectorInstanceID: t.Name()}
jobID, err := s.jobs.Create(ctx, ref, 60)
if err != nil {
t.Fatalf("create: %v", err)
}
run, err := s.jobs.TriggerNow(ctx, jobID)
if err != nil {
t.Fatalf("trigger: %v", err)
}
failErr := "verbindung zu drittsystem abgelehnt"
if err := s.jobs.CompleteRun(ctx, run.ID, errors.New(failErr)); err != nil {
t.Fatalf("complete: %v", err)
}
req := httptest.NewRequest("GET", "/connectors/status", nil)
req.Header.Set("X-User-Id", "u1")
req.Header.Set("X-Tenant-Slug", "acme")
rec := httptest.NewRecorder()
mux.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", rec.Code, rec.Body.String())
}
var out []connectorStatus
if err := json.Unmarshal(rec.Body.Bytes(), &out); err != nil {
t.Fatalf("decode: %v", err)
}
var found *connectorStatus
for i := range out {
if out[i].JobID == jobID {
found = &out[i]
}
}
if found == nil {
t.Fatal("expected the job in the status list")
}
if found.LastRunStatus == nil || *found.LastRunStatus != "failed" {
t.Fatalf("expected failed status, got %v", found.LastRunStatus)
}
if found.LastError == nil || *found.LastError != failErr {
t.Fatalf("expected diagnosable error %q, got %v", failErr, found.LastError)
}
}