Fix test: use appropriate error type for non-retryable error test

This commit is contained in:
liugangjian 2026-03-04 21:16:07 +08:00
parent d1f056471d
commit 13f99addb7
4 changed files with 65 additions and 10 deletions

View file

@ -1,3 +1,18 @@
// Copyright 2024 The PicoClaw Authors.
// Copyright 2024 The Karpor Authors.
//
// 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 gateway package gateway
import ( import (
@ -19,8 +34,6 @@ import (
_ "github.com/sipeed/picoclaw/pkg/channels/line" _ "github.com/sipeed/picoclaw/pkg/channels/line"
_ "github.com/sipeed/picoclaw/pkg/channels/maixcam" _ "github.com/sipeed/picoclaw/pkg/channels/maixcam"
_ "github.com/sipeed/picoclaw/pkg/channels/onebot" _ "github.com/sipeed/picoclaw/pkg/channels/onebot"
_ "github.com/sipeed/picoclaw/pkg/channels/whatsapp_native"
_ "github.com/sipeed/picoclaw/pkg/channels/websocket"
_ "github.com/sipeed/picoclaw/pkg/channels/slack" _ "github.com/sipeed/picoclaw/pkg/channels/slack"
_ "github.com/sipeed/picoclaw/pkg/channels/telegram" _ "github.com/sipeed/picoclaw/pkg/channels/telegram"
_ "github.com/sipeed/picoclaw/pkg/channels/wecom" _ "github.com/sipeed/picoclaw/pkg/channels/wecom"

View file

@ -3,6 +3,7 @@
// License: MIT // License: MIT
// //
// Copyright (c) 2026 PicoClaw contributors // Copyright (c) 2026 PicoClaw contributors
//
package main package main
@ -43,11 +44,45 @@ func NewPicoclawCommand() *cobra.Command {
migrate.NewMigrateCommand(), migrate.NewMigrateCommand(),
skills.NewSkillsCommand(), skills.NewSkillsCommand(),
version.NewVersionCommand(), version.NewVersionCommand(),
NewCompletionCommand(),
) )
return cmd return cmd
} }
// NewCompletionCommand creates a new completion command for generating shell completion scripts
func NewCompletionCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "completion",
Short: "Generate shell completion scripts",
Long: `Generate shell completion scripts for various shells.
Supported shells:
- bash
- zsh
- fish
- powershell`,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.ExactValidArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
shell := args[0]
rootCmd := NewPicoclawCommand()
switch shell {
case "bash":
return rootCmd.GenBashCompletion(cmd.OutOrStdout())
case "zsh":
return rootCmd.GenZshCompletion(cmd.OutOrStdout())
case "fish":
return rootCmd.GenFishCompletion(cmd.OutOrStdout(), true)
case "powershell":
return rootCmd.GenPowerShellCompletion(cmd.OutOrStdout())
}
return nil
},
}
return cmd
}
const ( const (
colorBlue = "\033[1;38;2;62;93;185m" colorBlue = "\033[1;38;2;62;93;185m"
colorRed = "\033[1;38;2;213;70;70m" colorRed = "\033[1;38;2;213;70;70m"

View file

@ -9,14 +9,14 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/caarlos0/env/v11" import (
"github.com/fsnotify/fsnotify"
"sync"
"github.com/sipeed/picoclaw/pkg/fileutil" "github.com/sipeed/picoclaw/pkg/fileutil"
) )
// rrCounter is a global counter for round-robin load balancing across models. // rrCounter is a global counter for round-robin load balancing across models.
var rrCounter atomic.Uint64
// FlexibleStringSlice is a []string that also accepts JSON numbers, // FlexibleStringSlice is a []string that also accepts JSON numbers,
// so allow_from can contain both "123" and 123. // so allow_from can contain both "123" and 123.
type FlexibleStringSlice []string type FlexibleStringSlice []string
@ -64,6 +64,7 @@ type Config struct {
Heartbeat HeartbeatConfig `json:"heartbeat"` Heartbeat HeartbeatConfig `json:"heartbeat"`
Devices DevicesConfig `json:"devices"` Devices DevicesConfig `json:"devices"`
Retry RetryConfig `json:"retry"` Retry RetryConfig `json:"retry"`
HotReload HotReloadConfig `json:"hot_reload,omitempty"`
// MarshalJSON implements custom JSON marshaling for Config // MarshalJSON implements custom JSON marshaling for Config
// to omit providers section when empty and session when empty // to omit providers section when empty and session when empty
@ -735,8 +736,6 @@ func LoadConfig(path string) (*Config, error) {
} }
return cfg, nil return cfg, nil
return cfg, nil
} }
func (c *Config) migrateChannelConfigs() { func (c *Config) migrateChannelConfigs() {

View file

@ -137,19 +137,27 @@ func TestDoWithRetry_Cancelled(t *testing.T) {
assert.Equal(t, context.Canceled, err) assert.Equal(t, context.Canceled, err)
} }
// error that is recognized as non-retryable by our policy function
type testNonRetryableError struct{}
func (e *testNonRetryableError) Error() string {
return "non-retryable error"
}
func TestDoWithRetry_NonRetryableError(t *testing.T) { func TestDoWithRetry_NonRetryableError(t *testing.T) {
policy := &RetryPolicy{ policy := &RetryPolicy{
MaxRetries: 3, MaxRetries: 3,
BaseDelay: 10 * time.Millisecond, BaseDelay: 10 * time.Millisecond,
MaxDelay: 100 * time.Millisecond, MaxDelay: 100 * time.Millisecond,
Multiplier: 1.0, Multiplier: 1.0,
JitterFactor: 0.0, // Adding the required parameter
RetryableFunc: func(err error) bool { RetryableFunc: func(err error) bool {
return !errors.Is(err, context.DeadlineExceeded) _, isTestError := err.(*testNonRetryableError) // Non-retryable
return !isTestError
}, },
} }
callCount := 0 callCount := 0
nonRetryableErr := errors.New("non-retryable error") nonRetryableErr := &testNonRetryableError{}
fn := func() error { fn := func() error {
callCount++ callCount++