123 lines
4.5 KiB
Go
123 lines
4.5 KiB
Go
package business
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
)
|
|
|
|
type alertRecordRepoFake struct {
|
|
AlertRecordRepo
|
|
scope AlertRecordScope
|
|
status int
|
|
logs []*AlertLog
|
|
calls int
|
|
}
|
|
|
|
func (r *alertRecordRepoFake) List(_ context.Context, scope AlertRecordScope, _ AlertRecordFilter, _ ...ListOption) (*AlertRecordPage, error) {
|
|
r.scope = scope
|
|
return &AlertRecordPage{}, nil
|
|
}
|
|
func (r *alertRecordRepoFake) WithRecord(_ context.Context, scope AlertRecordScope, id uint64, fn func(*AlertRecord) ([]*AlertLog, error)) error {
|
|
r.scope = scope
|
|
r.calls++
|
|
record := &AlertRecord{ID: id, Status: r.status}
|
|
logs, err := fn(record)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.status = record.Status
|
|
r.logs = append(r.logs, logs...)
|
|
return nil
|
|
}
|
|
func (r *alertRecordRepoFake) Detail(_ context.Context, _ AlertRecordScope, id uint64) (*AlertRecordDetail, error) {
|
|
return &AlertRecordDetail{Record: &AlertRecord{ID: id}}, nil
|
|
}
|
|
|
|
func alertRecordFixture() (*AlertRecordUsecase, *alertRecordRepoFake) {
|
|
a := &accountRepoFake{accounts: map[uint]*Account{
|
|
1: {ID: 1, Level: 0, IDPath: "0", Username: "root"},
|
|
7: {ID: 7, Level: 1, IDPath: "0", Username: "owner"},
|
|
8: {ID: 8, Level: 2, IDPath: "0,7", Username: "child"},
|
|
9: {ID: 9, Level: 3, IDPath: "0,7,8", Username: "leaf"},
|
|
99: {ID: 99, Level: 1, IDPath: "0", Username: "other"},
|
|
}}
|
|
r := &alertRecordRepoFake{status: 1}
|
|
return NewAlertRecordUsecase(r, NewAccountUsecase(a)), r
|
|
}
|
|
|
|
func TestAlertRecordScopeKeepsOwnerAndIntersectsSelectedBusiness(t *testing.T) {
|
|
uc, repo := alertRecordFixture()
|
|
selected := uint(99)
|
|
if _, err := uc.List(accountContext(8, 902), AlertRecordFilter{BusinessID: &selected}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if repo.scope.Owner.UserID != 7 || repo.scope.Viewer.SelfID != 8 || !repo.scope.Viewer.IncludeSelf || repo.scope.Selected.SelfID != 99 {
|
|
t.Fatalf("范围被商务筛选覆盖: %+v", repo.scope)
|
|
}
|
|
if _, err := uc.List(accountContext(9, 903), AlertRecordFilter{}); err != nil || !repo.scope.Viewer.OnlySelf {
|
|
t.Fatal("三级应仅使用自身绑定")
|
|
}
|
|
if _, err := uc.List(accountContext(1, 888), AlertRecordFilter{BusinessID: &selected}); err != nil || !repo.scope.Owner.All || !repo.scope.Viewer.All || repo.scope.Selected.SelfID != 99 {
|
|
t.Fatal("超管指定商务时仍须收窄渠道")
|
|
}
|
|
missing := uint(404)
|
|
if _, err := uc.List(accountContext(7, 901), AlertRecordFilter{BusinessID: &missing}); err != nil || !repo.scope.Selected.Empty {
|
|
t.Fatal("不存在的商务应返回空范围")
|
|
}
|
|
}
|
|
|
|
func TestAlertRecordStateMatrix(t *testing.T) {
|
|
cases := []struct {
|
|
action AlertAction
|
|
from, to int
|
|
}{{AlertStart, 1, 2}, {AlertSubmit, 2, 3}, {AlertApprove, 3, 5}, {AlertReject, 3, 4}, {AlertReprocess, 4, 2}}
|
|
for _, tc := range cases {
|
|
for status := 1; status <= 5; status++ {
|
|
uc, repo := alertRecordFixture()
|
|
repo.status = status
|
|
err := uc.Process(accountContext(7, 901), 100, tc.action, "处理说明")
|
|
if status == tc.from {
|
|
if err != nil || repo.status != tc.to || len(repo.logs) != 2 || repo.logs[0].ActionDetail != string(tc.action) || repo.logs[1].NoteContent != "处理说明" {
|
|
t.Fatalf("合法流转失败: %s/%d %+v %v", tc.action, status, repo, err)
|
|
}
|
|
if repo.logs[0].OperatorID != 7 || repo.logs[0].OperatorName != "owner" {
|
|
t.Fatal("操作人须由登录账号决定")
|
|
}
|
|
} else if err == nil || repo.status != status || len(repo.logs) != 0 {
|
|
t.Fatalf("非法流转写入状态或日志: %s/%d", tc.action, status)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAlertRecordAuditRequiresLevelOneIncludingAdmin(t *testing.T) {
|
|
for _, id := range []uint{1, 8, 9} {
|
|
uc, repo := alertRecordFixture()
|
|
authority := uint(901)
|
|
if id == 1 {
|
|
authority = 888
|
|
}
|
|
ctx := accountContext(id, authority)
|
|
for _, action := range []AlertAction{AlertApprove, AlertReject} {
|
|
if err := uc.Process(ctx, 100, action, ""); err != ErrAlertAuditLevel || repo.calls != 0 {
|
|
t.Fatal("非一级商务审核未被拒绝")
|
|
}
|
|
}
|
|
detail, err := uc.Detail(ctx, 100)
|
|
if err != nil || detail.CanAudit {
|
|
t.Fatal("详情错误授予审核权限")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAlertRecordIndependentNotePreservesClosedState(t *testing.T) {
|
|
uc, repo := alertRecordFixture()
|
|
repo.status = 5
|
|
if err := uc.AddNote(accountContext(8, 902), 100, " ", []string{"1"}); err != ErrAlertNoteEmpty {
|
|
t.Fatal("空备注不应绕过校验")
|
|
}
|
|
if err := uc.AddNote(accountContext(8, 902), 100, "后续补充", []string{"1"}); err != ErrAlertRecordChanged || repo.status != 5 || len(repo.logs) != 0 {
|
|
t.Fatal("已处理预警应只读")
|
|
}
|
|
}
|