Files
qiweimanager-master/helper/after_sales_knowledge_test.go

210 lines
6.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"os"
"strings"
"testing"
"time"
)
func TestAfterSalesResolveIssueArchivesKnowledgeCase(t *testing.T) {
cleanupAfterSalesKnowledgeTestFiles(t)
rebuildCh := stubAfterSalesKnowledgeRebuild(t)
engine := &AfterSalesIssueEngine{issues: []AfterSalesIssue{{
ID: "i1",
CreatedAt: "2026-06-04T09:00:00+08:00",
Status: afterSalesIssueStatusPending,
RoomName: "售后群",
CustomerName: "华南客户",
IssueContent: "镜头无法调焦",
AISuggestion: "检查调焦机构",
AssignedEngineerID: "engineer-a",
ImagePaths: []string{"a.jpg"},
}}}
knowledgeCase, err := engine.resolveIssue("i1", "已确认调焦环松动,重新固定后恢复。")
if err != nil {
t.Fatalf("resolve issue: %v", err)
}
if engine.issues[0].Status != afterSalesIssueStatusResolved {
t.Fatalf("expected resolved issue, got %#v", engine.issues[0])
}
if engine.issues[0].KnowledgeSourcePath == "" || engine.issues[0].KnowledgeArchivedAt == "" {
t.Fatalf("expected issue knowledge metadata, got %#v", engine.issues[0])
}
if knowledgeCase.IssueID != "i1" || knowledgeCase.ImageCount != 1 {
t.Fatalf("unexpected case: %#v", knowledgeCase)
}
data, err := os.ReadFile(knowledgeCase.MarkdownPath)
if err != nil {
t.Fatalf("read markdown: %v", err)
}
text := string(data)
for _, want := range []string{"问题IDi1", "镜头无法调焦", "已确认调焦环松动", "检查调焦机构", "售后群", "华南客户"} {
if !strings.Contains(text, want) {
t.Fatalf("expected markdown to contain %q, got %s", want, text)
}
}
cases, err := listAfterSalesKnowledgeCases()
if err != nil {
t.Fatalf("list cases: %v", err)
}
if len(cases) != 1 || cases[0].IssueID != "i1" {
t.Fatalf("expected one listed case, got %#v", cases)
}
select {
case <-rebuildCh:
case <-time.After(time.Second):
t.Fatal("expected knowledge rebuild hook")
}
}
func TestAfterSalesResolveIssueRequiresResolution(t *testing.T) {
cleanupAfterSalesKnowledgeTestFiles(t)
rebuildCh := stubAfterSalesKnowledgeRebuild(t)
engine := &AfterSalesIssueEngine{issues: []AfterSalesIssue{{
ID: "i1",
Status: afterSalesIssueStatusPending,
RoomName: "售后群",
}}}
if _, err := engine.resolveIssue("i1", " "); err == nil {
t.Fatal("expected empty resolution error")
}
if engine.issues[0].Status != afterSalesIssueStatusPending {
t.Fatalf("expected status unchanged, got %#v", engine.issues[0])
}
select {
case <-rebuildCh:
t.Fatal("did not expect rebuild")
default:
}
}
func TestAfterSalesKnowledgeCaseOverwrite(t *testing.T) {
cleanupAfterSalesKnowledgeTestFiles(t)
stubAfterSalesKnowledgeRebuild(t)
engine := &AfterSalesIssueEngine{issues: []AfterSalesIssue{{
ID: "i1",
CreatedAt: "2026-06-04T09:00:00+08:00",
Status: afterSalesIssueStatusPending,
IssueContent: "图像模糊",
AISuggestion: "检查焦距",
}}}
first, err := engine.resolveIssue("i1", "第一次处理方案")
if err != nil {
t.Fatalf("first resolve: %v", err)
}
second, err := engine.resolveIssue("i1", "第二次处理方案")
if err != nil {
t.Fatalf("second resolve: %v", err)
}
if first.MarkdownPath != second.MarkdownPath {
t.Fatalf("expected same markdown path, got %s and %s", first.MarkdownPath, second.MarkdownPath)
}
cases, err := listAfterSalesKnowledgeCases()
if err != nil {
t.Fatalf("list cases: %v", err)
}
if len(cases) != 1 || cases[0].ResolutionContent != "第二次处理方案" {
t.Fatalf("expected overwritten single case, got %#v", cases)
}
data, err := os.ReadFile(second.MarkdownPath)
if err != nil {
t.Fatalf("read markdown: %v", err)
}
if strings.Contains(string(data), "第一次处理方案") || !strings.Contains(string(data), "第二次处理方案") {
t.Fatalf("expected markdown overwrite, got %s", string(data))
}
}
func TestAfterSalesKnowledgeSyncLegacyResolvedIssue(t *testing.T) {
cleanupAfterSalesKnowledgeTestFiles(t)
rebuildCh := stubAfterSalesKnowledgeRebuild(t)
engine := &AfterSalesIssueEngine{issues: []AfterSalesIssue{{
ID: "legacy",
CreatedAt: "2026-06-02T11:10:24+08:00",
UpdatedAt: "2026-06-04T11:03:34+08:00",
Status: afterSalesIssueStatusResolved,
RoomName: "刘羽、JM.、C",
CustomerName: "JM.",
IssueContent: "客户询问客服身份",
AISuggestion: "客户询问客服身份,需确认是否需要进一步解释或提供帮助",
AssignedEngineerID: "1688855899845302",
NotifyStatus: afterSalesNotifySent,
}}}
if err := engine.syncResolvedKnowledgeCases(); err != nil {
t.Fatalf("sync legacy resolved: %v", err)
}
cases, err := listAfterSalesKnowledgeCases()
if err != nil {
t.Fatalf("list cases: %v", err)
}
if len(cases) != 1 || cases[0].IssueID != "legacy" {
t.Fatalf("expected one synced legacy case, got %#v", cases)
}
if cases[0].ResolutionContent != "客户询问客服身份,需确认是否需要进一步解释或提供帮助" {
t.Fatalf("expected AI suggestion fallback, got %#v", cases[0])
}
if engine.issues[0].KnowledgeSourcePath == "" || engine.issues[0].ResolutionContent == "" {
t.Fatalf("expected issue backfill metadata, got %#v", engine.issues[0])
}
if !fileExists(cases[0].MarkdownPath) {
t.Fatalf("expected markdown file at %s", cases[0].MarkdownPath)
}
select {
case <-rebuildCh:
case <-time.After(time.Second):
t.Fatal("expected knowledge rebuild hook")
}
}
func TestAfterSalesKnowledgeCaseMissingMarkdownFlag(t *testing.T) {
cleanupAfterSalesKnowledgeTestFiles(t)
stubAfterSalesKnowledgeRebuild(t)
engine := &AfterSalesIssueEngine{issues: []AfterSalesIssue{{ID: "i1", Status: afterSalesIssueStatusPending, IssueContent: "报错"}}}
knowledgeCase, err := engine.resolveIssue("i1", "重启设备恢复")
if err != nil {
t.Fatalf("resolve issue: %v", err)
}
if err := os.Remove(knowledgeCase.MarkdownPath); err != nil {
t.Fatalf("remove markdown: %v", err)
}
cases, err := listAfterSalesKnowledgeCases()
if err != nil {
t.Fatalf("list cases: %v", err)
}
if len(cases) != 1 || !cases[0].MissingMarkdown {
t.Fatalf("expected missing markdown flag, got %#v", cases)
}
}
func stubAfterSalesKnowledgeRebuild(t *testing.T) chan struct{} {
t.Helper()
oldHook := afterSalesKnowledgeRebuildHook
ch := make(chan struct{}, 8)
afterSalesKnowledgeRebuildHook = func() {
ch <- struct{}{}
}
t.Cleanup(func() {
afterSalesKnowledgeRebuildHook = oldHook
})
return ch
}
func cleanupAfterSalesKnowledgeTestFiles(t *testing.T) {
t.Helper()
t.Cleanup(func() {
_ = os.Remove(afterSalesKnowledgeCasesPath())
_ = os.Remove(afterSalesIssuesPath())
_ = os.RemoveAll(afterSalesKnowledgeMarkdownDir())
})
_ = os.Remove(afterSalesKnowledgeCasesPath())
_ = os.Remove(afterSalesIssuesPath())
_ = os.RemoveAll(afterSalesKnowledgeMarkdownDir())
}