Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 14 additions & 46 deletions pkg/common/slack/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"path/filepath"
"strings"

commonconfig "github.com/openshift/osde2e/pkg/common/config"
"github.com/openshift/osde2e/pkg/common/util"
)

Expand All @@ -19,8 +20,6 @@ type ArtifactLink struct {
}

const (
maxWorkflowFieldLength = 30000

fullOutputThreshold = 250
finalSummaryLines = 80

Expand Down Expand Up @@ -76,6 +75,7 @@ type WorkflowPayload struct {
Image string `json:"image,omitempty"`
Env string `json:"env,omitempty"`
Commit string `json:"commit,omitempty"`
TektonURL string `json:"tekton_url,omitempty"`
}

// ClusterInfo holds cluster information for reporting
Expand All @@ -96,14 +96,13 @@ func (s *SlackReporter) buildWorkflowPayload(result *AnalysisResult, config *Rep
payload.Channel = channel
}

payload.Summary = s.buildSummaryField(config)
payload.Analysis = s.buildAnalysisField(result)

if links, ok := config.Settings["artifact_links"].([]ArtifactLink); ok && len(links) > 0 {
payload.ExtendedLogs = s.enforceFieldLimit(s.buildArtifactLinksSection(links), maxWorkflowFieldLength)
payload.ExtendedLogs = s.enforceFieldLimit(s.buildArtifactLinksSection(links), commonconfig.SlackMessageLength)
} else if reportDir, ok := config.Settings["report_dir"].(string); ok && reportDir != "" {
if testOutput := s.readTestOutput(reportDir); testOutput != "" {
payload.ExtendedLogs = s.enforceFieldLimit(testOutput, maxWorkflowFieldLength)
payload.ExtendedLogs = s.enforceFieldLimit(testOutput, commonconfig.SlackMessageLength)
} else {
payload.ExtendedLogs = "No test failure logs found in the report directory."
}
Expand All @@ -117,28 +116,22 @@ func (s *SlackReporter) buildWorkflowPayload(result *AnalysisResult, config *Rep
payload.ClusterDetails = "Cluster information not available."
}

if image, ok := config.Settings["image"].(string); ok && image != "" {
if image, ok := config.Settings["repo"].(string); ok {
payload.Image = image
parts := strings.Split(image, ":")
if len(parts) == 2 {
payload.Commit = parts[1]
}
}

if env, ok := config.Settings["env"].(string); ok && env != "" {
if commit, ok := config.Settings["commit"].(string); ok {
payload.Commit = commit
}
if env, ok := config.Settings["env"].(string); ok {
payload.Env = env
}
if tektonURL, ok := config.Settings["tekton_url"].(string); ok {
payload.TektonURL = tektonURL
}
Comment thread
ritmun marked this conversation as resolved.

return payload
}

func (s *SlackReporter) buildSummaryField(config *ReporterConfig) string {
var builder strings.Builder
builder.WriteString(":failed: Pipeline Failed at E2E Test\n\n")
builder.WriteString(s.buildTestSuiteSection(config))
return s.enforceFieldLimit(builder.String(), maxWorkflowFieldLength)
}

func (s *SlackReporter) buildAnalysisField(result *AnalysisResult) string {
var builder strings.Builder

Expand All @@ -156,7 +149,7 @@ func (s *SlackReporter) buildAnalysisField(result *AnalysisResult) string {
builder.WriteString(result.Error)
}

return s.enforceFieldLimit(builder.String(), maxWorkflowFieldLength)
return s.enforceFieldLimit(builder.String(), commonconfig.SlackMessageLength)
}

func (s *SlackReporter) buildClusterInfoSection(config *ReporterConfig) string {
Expand All @@ -166,11 +159,8 @@ func (s *SlackReporter) buildClusterInfoSection(config *ReporterConfig) string {
}

var builder strings.Builder
builder.WriteString("====== ☸️ Cluster Information ======\n")
builder.WriteString(fmt.Sprintf("====== ☸️ Cluster (expires at %s) ======\n", clusterInfo.Expiration))
builder.WriteString(fmt.Sprintf("• Cluster ID: `%s`\n", clusterInfo.ID))
if clusterInfo.Expiration != "" {
builder.WriteString(fmt.Sprintf("• Expiration: `%s`\n", clusterInfo.Expiration))
}
if clusterInfo.Version != "" {
builder.WriteString(fmt.Sprintf("• Version: `%s`\n", clusterInfo.Version))
}
Expand All @@ -182,28 +172,6 @@ func (s *SlackReporter) buildClusterInfoSection(config *ReporterConfig) string {
return builder.String()
}

func (s *SlackReporter) buildTestSuiteSection(config *ReporterConfig) string {
image, ok := config.Settings["image"].(string)
if !ok || image == "" {
return ""
}

imageInfo := strings.Split(image, ":")
if len(imageInfo) < 2 {
return ""
}

var builder strings.Builder
builder.WriteString("====== 🧪 Test Suite Information ======\n")
builder.WriteString(fmt.Sprintf("• Image: `%s`\n", image))
if env, ok := config.Settings["env"].(string); ok && env != "" {
builder.WriteString(fmt.Sprintf("• Environment: `%s`\n", env))
}
builder.WriteString("\n")

return builder.String()
}

// buildArtifactLinksSection formats S3 artifact URLs for the Slack message.
// Presigned URLs are valid for 7 days after upload.
func (s *SlackReporter) buildArtifactLinksSection(links []ArtifactLink) string {
Expand Down
53 changes: 1 addition & 52 deletions pkg/common/slack/reporter_testoutput_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ func TestSlackReporter_buildClusterInfoSection(t *testing.T) {
result := reporter.buildClusterInfoSection(config)

expectedFields := []string{
"====== ☸️ Cluster Information ======",
"====== ☸️ Cluster",
"cluster-abc",
"4.23",
"aws",
Expand Down Expand Up @@ -355,54 +355,3 @@ func TestSlackReporter_buildClusterInfoSection(t *testing.T) {
}
})
}

func TestSlackReporter_buildTestSuiteSection(t *testing.T) {
reporter := NewSlackReporter()

t.Run("builds test suite info", func(t *testing.T) {
config := &ReporterConfig{
Settings: map[string]interface{}{
"image": "quay.io/openshift/test:v2.0",
"env": "staging",
},
}

result := reporter.buildTestSuiteSection(config)

if !strings.Contains(result, "quay.io/openshift/test") {
t.Error("should contain image name")
}
if !strings.Contains(result, "v2.0") {
t.Error("should contain commit/tag")
}
if !strings.Contains(result, "staging") {
t.Error("should contain environment")
}
})

t.Run("returns empty for missing image", func(t *testing.T) {
config := &ReporterConfig{
Settings: map[string]interface{}{},
}

result := reporter.buildTestSuiteSection(config)

if result != "" {
t.Errorf("expected empty string for missing image, got: %s", result)
}
})

t.Run("returns empty for invalid image format", func(t *testing.T) {
config := &ReporterConfig{
Settings: map[string]interface{}{
"image": "invalid-no-tag",
},
}

result := reporter.buildTestSuiteSection(config)

if result != "" {
t.Errorf("expected empty string for invalid image format, got: %s", result)
}
})
}
97 changes: 8 additions & 89 deletions pkg/common/slack/reporter_workflow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"net/http/httptest"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestSlackReporter_buildWorkflowPayload(t *testing.T) {
Expand Down Expand Up @@ -38,7 +40,8 @@ func TestSlackReporter_buildWorkflowPayload(t *testing.T) {
"webhook_url": "https://hooks.slack.com/test",
"channel": "C06HQR8HN0L",
"cluster_info": clusterInfo,
"image": "quay.io/test:abc123",
"repo": "image quay.io/test",
"commit": "abc123",
"env": "stage",
},
}
Expand All @@ -50,25 +53,10 @@ func TestSlackReporter_buildWorkflowPayload(t *testing.T) {
t.Errorf("expected channel C06HQR8HN0L, got %s", payload.Channel)
}

if payload.Summary == "" {
t.Error("summary field is required but empty")
}

if payload.Analysis == "" {
t.Error("analysis field is required but empty")
}

// Verify summary contains test suite info (what failed)
if !strings.Contains(payload.Summary, "quay.io/test") {
t.Error("summary should contain image name")
}
if !strings.Contains(payload.Summary, "abc123") {
t.Error("summary should contain commit")
}
if !strings.Contains(payload.Summary, "stage") {
t.Error("summary should contain environment")
}

// Verify cluster_details contains cluster info (for debugging)
if payload.ClusterDetails == "" {
t.Error("cluster_details should not be empty when cluster info is provided")
Expand All @@ -89,17 +77,9 @@ func TestSlackReporter_buildWorkflowPayload(t *testing.T) {
}

// Verify optional fields
if payload.Image != "quay.io/test:abc123" {
t.Errorf("expected image quay.io/test:abc123, got %s", payload.Image)
}

if payload.Commit != "abc123" {
t.Errorf("expected commit abc123, got %s", payload.Commit)
}

if payload.Env != "stage" {
t.Errorf("expected env stage, got %s", payload.Env)
}
assert.Equal(t, "image quay.io/test", payload.Image)
assert.Equal(t, "abc123", payload.Commit)
assert.Equal(t, "stage", payload.Env, "Env")
}

func TestSlackReporter_Report_WorkflowFormat(t *testing.T) {
Expand Down Expand Up @@ -151,78 +131,17 @@ func TestSlackReporter_Report_WorkflowFormat(t *testing.T) {
t.Errorf("expected channel C123456, got %s", capturedPayload.Channel)
}

if capturedPayload.Summary == "" {
t.Error("summary should not be empty")
}

if capturedPayload.Analysis == "" {
t.Error("analysis should not be empty")
}

// Verify cluster info is in cluster_details field (not summary)
// Verify cluster info is in cluster_details field
if capturedPayload.ClusterDetails == "" {
t.Error("cluster_details should not be empty when cluster info is provided")
}
if !strings.Contains(capturedPayload.ClusterDetails, "test-456") {
t.Error("cluster_details should contain cluster ID")
}

// Verify summary contains test suite info
if !strings.Contains(capturedPayload.Summary, "quay.io/openshift/test") {
t.Error("summary should contain test image")
}

if capturedPayload.Image != "quay.io/openshift/test:v1.0" {
t.Errorf("expected image quay.io/openshift/test:v1.0, got %s", capturedPayload.Image)
}

if capturedPayload.Commit != "v1.0" {
t.Errorf("expected commit v1.0, got %s", capturedPayload.Commit)
}
}

func TestSlackReporter_buildSummaryField(t *testing.T) {
reporter := NewSlackReporter()

clusterInfo := &ClusterInfo{
ID: "cluster-789",
Name: "my-test-cluster",
Version: "4.22",
Provider: "gcp",
Expiration: "2026-02-01T12:00:00Z",
}

config := &ReporterConfig{
Settings: map[string]interface{}{
"cluster_info": clusterInfo,
"image": "quay.io/app:commit-xyz",
"env": "dev",
},
}

summary := reporter.buildSummaryField(config)

// Check for header
if !strings.Contains(summary, ":failed:") {
t.Error("summary should contain failure emoji")
}
if !strings.Contains(summary, "Pipeline Failed") {
t.Error("summary should contain failure message")
}

// Summary should NOT contain cluster info (it's in cluster_details now)
// Summary should ONLY contain test suite info (what failed)

// Check for test suite info
if !strings.Contains(summary, "quay.io/app") {
t.Error("summary should contain test image")
}
if !strings.Contains(summary, "commit-xyz") {
t.Error("summary should contain commit")
}
if !strings.Contains(summary, "dev") {
t.Error("summary should contain environment")
}
}

func TestSlackReporter_buildAnalysisField(t *testing.T) {
Expand Down
Loading