diff --git a/handlers/jira_test.go b/handlers/jira_test.go new file mode 100644 index 0000000..653b644 --- /dev/null +++ b/handlers/jira_test.go @@ -0,0 +1,83 @@ +package handlers + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "net/url" + "sentinent-backend/database" + "sentinent-backend/middleware" + "sentinent-backend/services" + "sentinent-backend/utils" + "testing" + "time" +) + +func TestJiraAuthHandlerSetsSignedStateCookie(t *testing.T) { + setupIntegrationsTestDB(t) + defer database.DB.Close() + + originalJwtKey := utils.JwtKey + utils.JwtKey = []byte("test-jwt-secret") + t.Cleanup(func() { + utils.JwtKey = originalJwtKey + }) + + t.Setenv("JIRA_CLIENT_ID", "jira-client") + t.Setenv("JIRA_CLIENT_SECRET", "jira-secret") + t.Setenv("API_BASE_URL", "https://api.example.com") + if err := services.InitJiraService(); err != nil { + t.Fatalf("failed to initialize Jira service: %v", err) + } + + req := httptest.NewRequest(http.MethodGet, "/api/integrations/jira/auth?workspace_id=9&redirect_url=https://app.example.com/settings", nil) + req = req.WithContext(context.WithValue(req.Context(), middleware.UserEmailKey, "reader@example.com")) + rr := httptest.NewRecorder() + + JiraAuthHandler(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("expected status 200, got %d: %s", rr.Code, rr.Body.String()) + } + + var payload map[string]string + if err := json.NewDecoder(rr.Body).Decode(&payload); err != nil { + t.Fatalf("failed to decode Jira auth response: %v", err) + } + + authURL, err := url.Parse(payload["auth_url"]) + if err != nil { + t.Fatalf("failed to parse auth URL: %v", err) + } + + email, workspaceID, redirectURL, err := validateJiraOAuthState(authURL.Query().Get("state")) + if err != nil { + t.Fatalf("expected Jira state to validate: %v", err) + } + if email != "reader@example.com" || workspaceID != 9 || redirectURL != "https://app.example.com/settings" { + t.Fatalf("unexpected state values: email=%q workspace=%d redirect=%q", email, workspaceID, redirectURL) + } + + cookie := rr.Result().Cookies()[0] + if cookie.Name != jiraOAuthStateCookieName || !cookie.HttpOnly { + t.Fatalf("expected HttpOnly Jira state cookie, got %+v", cookie) + } +} + +func TestValidateJiraOAuthStateRejectsExpiredState(t *testing.T) { + originalJwtKey := utils.JwtKey + utils.JwtKey = []byte("test-jwt-secret") + t.Cleanup(func() { + utils.JwtKey = originalJwtKey + }) + + state, err := createJiraOAuthState("reader@example.com", 9, "", time.Now().Add(-jiraOAuthStateTTL-time.Minute)) + if err != nil { + t.Fatalf("failed to create expired state: %v", err) + } + + if _, _, _, err := validateJiraOAuthState(state); err == nil { + t.Fatal("expected expired Jira state to be rejected") + } +} diff --git a/services/jira_description_test.go b/services/jira_description_test.go new file mode 100644 index 0000000..3d75780 --- /dev/null +++ b/services/jira_description_test.go @@ -0,0 +1,47 @@ +package services + +import ( + "strings" + "testing" +) + +func TestFormatDescriptionExtractsADFText(t *testing.T) { + desc := map[string]interface{}{ + "type": "doc", + "content": []interface{}{ + map[string]interface{}{ + "type": "paragraph", + "content": []interface{}{ + map[string]interface{}{"type": "text", "text": "Ship "}, + map[string]interface{}{"type": "text", "text": "backend"}, + }, + }, + }, + } + + if got := formatDescription(desc); got != "Ship backend" { + t.Fatalf("expected extracted ADF text, got %q", got) + } +} + +func TestFormatDescriptionFallsBackToJSON(t *testing.T) { + desc := map[string]interface{}{"unknown": "value"} + + if got := formatDescription(desc); got != `{"unknown":"value"}` { + t.Fatalf("expected JSON fallback, got %q", got) + } +} + +func TestFormatDescriptionTruncatesLongText(t *testing.T) { + desc := map[string]interface{}{ + "type": "paragraph", + "content": []interface{}{ + map[string]interface{}{"type": "text", "text": strings.Repeat("a", 501)}, + }, + } + + got := formatDescription(desc) + if len(got) != 503 || !strings.HasSuffix(got, "...") { + t.Fatalf("expected 500 character truncation with suffix, got length %d", len(got)) + } +}