package render import ( "encoding/json" "strings" "testing" ) func TestAlertCardIsInteractiveWithRedHeader(t *testing.T) { body, err := AlertCard(Alert{ Environment: "preview", Title: "归档目录不可用", Summary: "正在重启以重新挂载", Reason: "宿主机上的归档目录已被删除,容器仍指向旧目录(挂载失效)。", Path: "/data/full-sync", }) if err != nil { t.Fatal(err) } var decoded map[string]any if err := json.Unmarshal(body, &decoded); err != nil { t.Fatalf("alert card must be valid JSON: %v", err) } if decoded["msg_type"] != "interactive" { t.Fatalf("alert must be an interactive card, got %v", decoded["msg_type"]) } card := decoded["card"].(map[string]any) header := card["header"].(map[string]any) if header["template"] != "red" { t.Fatalf("alert header should be red so it stands out, got %v", header["template"]) } title := header["title"].(map[string]any)["content"].(string) if !strings.Contains(title, "预演") || !strings.Contains(title, "归档目录不可用") { t.Fatalf("unexpected title: %q", title) } } func TestAlertCardShowsPathAndRepeatCount(t *testing.T) { body, err := AlertCard(Alert{ Environment: "dev", Title: "归档目录不可用", Summary: "正在重启以重新挂载", Reason: "目录不存在。", Path: "/data/full-sync", ExitCount: 3, }) if err != nil { t.Fatal(err) } text := string(body) if !strings.Contains(text, "/data/full-sync") { t.Fatal("card should show the path") } if !strings.Contains(text, "已重复 3 次") { t.Fatal("card should warn about repeated exits") } } // 单次故障时不该出现「已重复」这种话,避免误导。 func TestAlertCardOmitsRepeatNoticeOnFirstOccurrence(t *testing.T) { body, err := AlertCard(Alert{Environment: "dev", Title: "归档目录不可用", ExitCount: 1}) if err != nil { t.Fatal(err) } if strings.Contains(string(body), "已重复") { t.Fatal("first occurrence must not claim repeats") } } func TestFriendlyReasonTranslatesKnownErrors(t *testing.T) { cases := map[string]string{ "archive directory was deleted on the host (nlink=0); recreate this container": "宿主机上的归档目录已被删除", "stat /data/x: no such file or directory": "目录不存在。", "open /data/x: permission denied": "没有读取权限。", } for input, want := range cases { if got := FriendlyReason(input); !strings.Contains(got, want) { t.Fatalf("FriendlyReason(%q) = %q, want it to contain %q", input, got, want) } } // 未知错误原样保留,便于排查 unknown := "some unexpected failure" if got := FriendlyReason(unknown); got != unknown { t.Fatalf("unknown reason must be preserved, got %q", got) } }