Files

171 lines
4.6 KiB
Go

// 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.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package consume
import (
"bytes"
"errors"
"strings"
"testing"
"time"
)
func base() Config {
return Config{
WorkDir: "/tmp/x",
IPCEndpoint: "/tmp/x/bus.sock",
ClientID: "ding_abc",
}
}
func TestValidate_Happy(t *testing.T) {
if err := ValidateConfig(base()); err != nil {
t.Fatalf("baseline should be valid, got %v", err)
}
}
func TestValidate_RequiredFields(t *testing.T) {
cases := []struct {
name string
mut func(*Config)
}{
{"empty WorkDir", func(c *Config) { c.WorkDir = "" }},
{"whitespace WorkDir", func(c *Config) { c.WorkDir = " " }},
{"empty IPCEndpoint", func(c *Config) { c.IPCEndpoint = "" }},
{"empty ClientID", func(c *Config) { c.ClientID = "" }},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
c := base()
tc.mut(&c)
err := ValidateConfig(c)
if !IsValidationError(err) {
t.Fatalf("expected ValidationError, got %v", err)
}
})
}
}
func TestValidate_ForceRequiresForeground(t *testing.T) {
c := base()
c.Force = true
c.Foreground = false
err := ValidateConfig(c)
if !errors.Is(err, ErrForceRequiresForeground) {
t.Fatalf("err = %v, want ErrForceRequiresForeground", err)
}
for _, recoveryStep := range []string{"event stop --all --dry-run", "event stop --all --yes", "event consume"} {
if strings.Contains(err.Error(), recoveryStep) {
continue
}
t.Errorf("error message must include the recovery hint, got: %s", err.Error())
}
// With --foreground it's fine.
c.Foreground = true
if err := ValidateConfig(c); err != nil {
t.Fatalf("--force + --foreground should be valid, got %v", err)
}
}
func TestValidate_FormatJSONRequiresBounded(t *testing.T) {
c := base()
c.Format = FormatJSON
c.MaxEvents = 0
c.Duration = 0
err := ValidateConfig(c)
if !errors.Is(err, ErrJSONFormatRequiresBounded) {
t.Fatalf("err = %v, want ErrJSONFormatRequiresBounded", err)
}
// With --max-events it passes.
c.MaxEvents = 10
if err := ValidateConfig(c); err != nil {
t.Fatalf("--format json + --max-events should be valid: %v", err)
}
c.MaxEvents = 0
c.Duration = 30 * time.Second
if err := ValidateConfig(c); err != nil {
t.Fatalf("--format json + --duration should be valid: %v", err)
}
// NDJSON has no such requirement.
c.Format = FormatNDJSON
c.MaxEvents = 0
c.Duration = 0
if err := ValidateConfig(c); err != nil {
t.Fatalf("ndjson unbounded should be valid: %v", err)
}
}
func TestValidateNoOutputConflict(t *testing.T) {
c := base()
c.OutputDir = "/tmp/events"
if err := ValidateNoOutputConflict(c, ""); err != nil {
t.Fatalf("no global -o → ok, got %v", err)
}
if err := ValidateNoOutputConflict(c, "/tmp/out.json"); !IsValidationError(err) {
t.Fatalf("--output-dir + global -o should be ValidationError, got %v", err)
}
c2 := base()
c2.Routes, _ = ParseRoutes([]string{`^im=dir:./im/`})
if err := ValidateNoOutputConflict(c2, "/tmp/out.json"); !IsValidationError(err) {
t.Fatalf("--route + global -o should be ValidationError, got %v", err)
}
}
func TestPrintDryRun_NilWriterSafe(t *testing.T) {
// Must not panic.
PrintDryRun(nil, base())
}
func TestPrintDryRun_RendersAllSetFields(t *testing.T) {
var buf bytes.Buffer
c := base()
c.EventTypes = []string{"im.*", "approval.*"}
c.Filter = "^im\\."
c.Format = FormatCompact
c.Flatten = true
c.OutputDir = "/tmp/events"
c.Routes, _ = ParseRoutes([]string{`^im\.=dir:/tmp/im/`})
c.MaxEvents = 5
c.Duration = 30 * time.Second
c.Compact = true
c.Quiet = true
c.Foreground = true
c.Force = true
PrintDryRun(&buf, c)
out := buf.String()
wants := []string{
"client_id", "workdir", "ipc_endpoint", "im.*,approval.*",
"^im\\.", "compact", "/tmp/events", "route[0]", "max_events : 5",
"duration", "flatten : true", "true",
}
for _, w := range wants {
if !strings.Contains(out, w) {
t.Errorf("dry-run missing %q in output:\n%s", w, out)
}
}
}
func TestPrintDryRun_CatchAllWhenEventTypesEmpty(t *testing.T) {
var buf bytes.Buffer
PrintDryRun(&buf, base())
if !strings.Contains(buf.String(), "(catch-all)") {
t.Errorf("expected '(catch-all)' for empty event_types:\n%s", buf.String())
}
}