fix: emit structured audit verification output

This commit is contained in:
张卓澎 committed 2026-07-17 13:44:18 +08:00
1 parent 5a8376ac0f
commit 91bd7c7802
2 files changed
+89 -3

No files matched your search

+24 -3
View File
@@ -13,6 +13,7 @@ import (
"time"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/audit"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/output"
"github.com/spf13/cobra"
)
@@ -117,9 +118,29 @@ func newAuditVerifyCommand() *cobra.Command {
}
}
valid, brokenAt, err := auditVerify(target)
if err != nil {
return fmt.Errorf("校验失败: %w", err)
valid, brokenAt, verifyErr := auditVerify(target)
if output.ResolveFormat(cmd, output.FormatTable) == output.FormatJSON {
if verifyErr != nil && brokenAt == 0 {
return fmt.Errorf("校验失败: %w", verifyErr)
}
payload := map[string]any{
"valid": valid,
"file": target,
"brokenAt": brokenAt,
}
if verifyErr != nil {
payload["reason"] = verifyErr.Error()
}
if err := output.WriteCommandPayload(cmd, payload, output.FormatTable); err != nil {
return err
}
if !valid {
auditExit(1)
}
return nil
}
if verifyErr != nil {
return fmt.Errorf("校验失败: %w", verifyErr)
}
if valid {
fmt.Printf("✓ %s 哈希链完整(全部通过)\n", filepath.Base(target))
@@ -0,0 +1,65 @@
// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
package app
import (
"bytes"
"encoding/json"
"errors"
"testing"
"github.com/spf13/cobra"
)
func executeAuditVerifyJSON(t *testing.T, verify func(string) (bool, int, error)) (map[string]any, int, error) {
t.Helper()
previousVerify, previousExit := auditVerify, auditExit
t.Cleanup(func() {
auditVerify, auditExit = previousVerify, previousExit
})
auditVerify = verify
exitCode := 0
auditExit = func(code int) { exitCode = code }
root := &cobra.Command{Use: "dws"}
root.SilenceErrors = true
root.SilenceUsage = true
root.PersistentFlags().String("format", "json", "output format")
root.AddCommand(newAuditVerifyCommand())
var stdout bytes.Buffer
root.SetOut(&stdout)
root.SetArgs([]string{"verify", "--file", "/tmp/audit.jsonl"})
err := root.Execute()
var payload map[string]any
if decodeErr := json.Unmarshal(stdout.Bytes(), &payload); decodeErr != nil {
t.Fatalf("audit verify stdout must be one JSON document: %v\n%s", decodeErr, stdout.String())
}
return payload, exitCode, err
}
func TestCrossPlatformCoverageAuditVerifyJSONOutputIsSingleDocument(t *testing.T) {
payload, exitCode, err := executeAuditVerifyJSON(t, func(string) (bool, int, error) {
return true, 0, nil
})
if err != nil || exitCode != 0 {
t.Fatalf("audit verify returned err=%v exit=%d", err, exitCode)
}
if payload["valid"] != true || payload["file"] != "/tmp/audit.jsonl" || payload["brokenAt"] != float64(0) {
t.Fatalf("unexpected audit payload: %#v", payload)
}
}
func TestCrossPlatformCoverageAuditVerifyBrokenJSONIncludesReasonBeforeExit(t *testing.T) {
payload, exitCode, err := executeAuditVerifyJSON(t, func(string) (bool, int, error) {
return false, 3, errors.New("prev_hash mismatch")
})
if err != nil || exitCode != 1 {
t.Fatalf("broken audit verify returned err=%v exit=%d", err, exitCode)
}
if payload["valid"] != false || payload["brokenAt"] != float64(3) || payload["reason"] != "prev_hash mismatch" {
t.Fatalf("unexpected broken audit payload: %#v", payload)
}
}