Files
story/internal/diagnostics/integration_issues_integration_test.go
T

110 lines
6.0 KiB
Go

package diagnostics
import (
"context"
"errors"
"os"
"testing"
"time"
"data-terminal-cloud/internal/audit"
"data-terminal-cloud/internal/concurrency"
"data-terminal-cloud/internal/database"
"data-terminal-cloud/internal/idempotency"
"data-terminal-cloud/internal/identity"
"github.com/google/uuid"
)
func TestIntegrationIssueLifecycleIsTenantScopedIdempotentAndVersioned(t *testing.T) {
databaseURL := os.Getenv("TEST_DATABASE_URL")
if databaseURL == "" {
t.Skip("TEST_DATABASE_URL is not set")
}
ctx := context.Background()
pool, err := database.Open(ctx, databaseURL, "integration-issue-test")
if err != nil {
t.Fatal(err)
}
defer pool.Close()
service, err := NewService(pool, idempotency.NewExecutor(pool), audit.NewRepository())
if err != nil {
t.Fatal(err)
}
admin := identity.Principal{Issuer: "http://localhost:58180/realms/voice-local", Subject: "00000000-0000-0000-0000-000000000101"}
tenantPublicID := "10000000-0000-0000-0000-000000000010"
devicePublicID := "10000000-0000-0000-0000-000000000511"
key := "integration-issue-" + uuid.NewString()
requestPrefix := "integration-issue-test-" + uuid.NewString()
baselineSummary, err := service.SummarizeIntegrationIssues(ctx, admin, tenantPublicID)
if err != nil {
t.Fatal(err)
}
var issueID string
defer func() {
if issueID != "" {
_, _ = pool.Exec(context.Background(), `DELETE FROM integration_issue WHERE public_id=$1::uuid`, issueID)
}
_, _ = pool.Exec(context.Background(), `DELETE FROM audit_event WHERE request_id LIKE $1`, requestPrefix+"%")
_, _ = pool.Exec(context.Background(), `DELETE FROM idempotency_record WHERE idempotency_key=$1`, key)
}()
input := CreateIntegrationIssueInput{
Phenomenon: "设备入房后供应商任务未在预期时间进入活动状态",
Severity: "HIGH",
DeviceID: devicePublicID, OccurredAt: time.Now().UTC().Add(-time.Minute),
RequestID: requestPrefix + "-source", ResponsibleParty: "Agent研发组",
}
created, firstCreated, err := service.CreateIntegrationIssue(ctx, admin, tenantPublicID, key, requestPrefix+"-create", input)
if err != nil || !firstCreated || created.Status != "OPEN" || created.Severity != "HIGH" || created.Version != 1 || created.DeviceID != devicePublicID {
t.Fatalf("created=%+v first=%t err=%v", created, firstCreated, err)
}
issueID = created.ID
replayed, replayCreated, err := service.CreateIntegrationIssue(ctx, admin, tenantPublicID, key, requestPrefix+"-replay", input)
if err != nil || replayCreated || replayed.ID != created.ID {
t.Fatalf("replayed=%+v created=%t err=%v", replayed, replayCreated, err)
}
items, err := service.ListIntegrationIssues(ctx, admin, tenantPublicID, "OPEN")
if err != nil || len(items) == 0 || items[0].ID != created.ID {
t.Fatalf("items=%+v err=%v", items, err)
}
blockingSummary, err := service.SummarizeIntegrationIssues(ctx, admin, tenantPublicID)
if err != nil || blockingSummary.ReleaseBlocking != baselineSummary.ReleaseBlocking+1 || blockingSummary.ReleaseGateSatisfied {
t.Fatalf("blocking summary=%+v baseline=%+v err=%v", blockingSummary, baselineSummary, err)
}
resolvedStatus, resolution := "RESOLVED", "修正供应商任务启动回调的路由配置"
rootCause, fixedVersion := "回调路由错误地使用了旧供应商事件类型", "0.49.0-rc.1+build.36"
if _, err = service.UpdateIntegrationIssue(ctx, admin, tenantPublicID, created.ID, requestPrefix+"-invalid-resolve", 1, UpdateIntegrationIssueInput{Status: &resolvedStatus}); !errors.Is(err, ErrInvalid) {
t.Fatalf("missing resolution error=%v", err)
}
resolved, err := service.UpdateIntegrationIssue(ctx, admin, tenantPublicID, created.ID, requestPrefix+"-resolve", 1, UpdateIntegrationIssueInput{Status: &resolvedStatus, ResolutionResult: &resolution, RootCause: &rootCause, FixedVersion: &fixedVersion})
if err != nil || resolved.Status != "RESOLVED" || resolved.Version != 2 || resolved.ResolutionResult != resolution || resolved.RootCause != rootCause || resolved.FixedVersion != fixedVersion {
t.Fatalf("resolved=%+v err=%v", resolved, err)
}
if _, err = service.UpdateIntegrationIssue(ctx, admin, tenantPublicID, created.ID, requestPrefix+"-stale", 1, UpdateIntegrationIssueInput{ResponsibleParty: &resolution}); !errors.Is(err, concurrency.ErrVersionConflict) {
t.Fatalf("stale version error=%v", err)
}
verifiedStatus, conclusion := "VERIFIED", "连续三次模拟联调均进入ACTIVE且没有遗留任务"
evidence := "请求关联号 integration-regression-36;诊断包 artifact-36;重复3次"
if _, err = service.UpdateIntegrationIssue(ctx, admin, tenantPublicID, created.ID, requestPrefix+"-invalid-verify", 2, UpdateIntegrationIssueInput{Status: &verifiedStatus, RegressionConclusion: &conclusion}); !errors.Is(err, ErrInvalid) {
t.Fatalf("missing regression evidence error=%v", err)
}
verified, err := service.UpdateIntegrationIssue(ctx, admin, tenantPublicID, created.ID, requestPrefix+"-verify", 2, UpdateIntegrationIssueInput{Status: &verifiedStatus, RegressionConclusion: &conclusion, RegressionEvidence: &evidence})
if err != nil || verified.Status != "VERIFIED" || verified.Version != 3 || verified.RegressionConclusion != conclusion || verified.RegressionEvidence != evidence {
t.Fatalf("verified=%+v err=%v", verified, err)
}
closedStatus := "CLOSED"
closed, err := service.UpdateIntegrationIssue(ctx, admin, tenantPublicID, created.ID, requestPrefix+"-close", 3, UpdateIntegrationIssueInput{Status: &closedStatus})
if err != nil || closed.Version != 4 || closed.Status != "CLOSED" {
t.Fatalf("closed=%+v err=%v", closed, err)
}
closedSummary, err := service.SummarizeIntegrationIssues(ctx, admin, tenantPublicID)
if err != nil || closedSummary.ReleaseBlocking != baselineSummary.ReleaseBlocking || closedSummary.ReleaseGateSatisfied != baselineSummary.ReleaseGateSatisfied {
t.Fatalf("closed summary=%+v baseline=%+v err=%v", closedSummary, baselineSummary, err)
}
if _, err = service.ListIntegrationIssues(ctx, admin, "20000000-0000-0000-0000-000000000010", ""); !errors.Is(err, ErrAccessDenied) {
t.Fatalf("cross tenant error=%v", err)
}
}