CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (pull_request) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (push) Failing after 57s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Successful in 5s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (push) Successful in 5s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Failing after 22s
CI / Web (dms-browser) (push) Failing after 1m23s
CI / Web (pdf-viewer) (push) Failing after 55s
CI / Web (shl) (push) Failing after 13s
Connect-Schnittstellen-Vertragstests / contract-tests (push) Successful in 10m42s
Supply-Chain-Scan (Go) / govulncheck (push) Failing after 1m10s
CI / Core-Modul (Build/Vet/Test) (pull_request) Failing after 58s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (pull_request) Successful in 4s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (pull_request) Successful in 4s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (pull_request) Failing after 24s
CI / Web (dms-browser) (pull_request) Failing after 1m25s
CI / Web (pdf-viewer) (pull_request) Failing after 54s
CI / Web (shl) (pull_request) Failing after 11s
Connect-Schnittstellen-Vertragstests / contract-tests (pull_request) Successful in 12m3s
Core-Schnittstellen-Vertragstests / contract-tests (pull_request) Successful in 11m40s
Supply-Chain-Scan (Go) / govulncheck (pull_request) Failing after 1m11s
Supply-Chain-Scan (npm) / npm-audit (pull_request) Failing after 1m49s
Core-Pflichttest-Gate / pflichttest-gate (pull_request) Successful in 3m23s
RBAC-Rollenhierarchie-Pflichtgate / rbac-policy-invariant (pull_request) Successful in 11m49s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Failing after 3s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (pull_request) Failing after 3s
DB-Integrationstests / db-tests (pull_request) Canceled after 0s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
119 lines
3.3 KiB
Go
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/common/testdb"
|
|
"gitea.perlbach24.de/scripte/nexarch/connect/internal/migrate"
|
|
"gitea.perlbach24.de/scripte/nexarch/connect/internal/syncjob"
|
|
)
|
|
|
|
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)
|
|
}
|
|
}
|