171 lines
4.6 KiB
Go
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())
|
|
}
|
|
}
|