Files
Enterprise-Knowledge-Base/cli/cmd/kb/pin_test.go
T
nullkey 0e081aec5c feat(cli): --log-level + kb/agent status & check + cross-cutting refactor
Operability surface and the bulk of the jopts→fopts migration:

* --log-level error|warn|info|debug + WEKNORA_LOG_LEVEL env, wired to
  the SDK via client.SetDebugLevel. Invalid --log-level returns FlagError
  (exit 2).
* kb status <kb-id> / kb check <kb-id> verb split (1 HTTP vs 1+N for
  failed_count aggregation).
* agent status <agent-id> / agent check <agent-id> verb split
  (probes kb_scope_all_reachable via 1+N HTTP).
* kb create <name> positional (matches agent create).
* Positional id help strings namespaced (<kb-id> / <agent-id>).
* All auth / context / link / doctor / kb / agent CRUD commands migrated
  to the FormatOptions API.
* root.go Execute(ctx) takes a context so signal-cancellation propagates
  via cmd.Context() into long-running commands.
* Pagination termination uses len(accum) >= total (not page*pageSize)
  so server-capped page sizes do not truncate aggregations.
2026-05-18 11:10:19 +08:00

107 lines
3.7 KiB
Go

package kb
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Tencent/WeKnora/cli/internal/cmdutil"
"github.com/Tencent/WeKnora/cli/internal/iostreams"
sdk "github.com/Tencent/WeKnora/client"
)
// fakePinSvc satisfies PinService: GetKnowledgeBase + TogglePinKnowledgeBase.
type fakePinSvc struct {
current sdk.KnowledgeBase
getErr error
toggleErr error
toggleCalled bool
}
func (f *fakePinSvc) GetKnowledgeBase(_ context.Context, id string) (*sdk.KnowledgeBase, error) {
if f.getErr != nil {
return nil, f.getErr
}
c := f.current
c.ID = id
return &c, nil
}
func (f *fakePinSvc) TogglePinKnowledgeBase(_ context.Context, id string) (*sdk.KnowledgeBase, error) {
f.toggleCalled = true
if f.toggleErr != nil {
return nil, f.toggleErr
}
c := f.current
c.ID = id
c.IsPinned = !c.IsPinned
return &c, nil
}
func TestPin_UnpinnedToPinned_CallsToggle(t *testing.T) {
out, _ := iostreams.SetForTest(t)
svc := &fakePinSvc{current: sdk.KnowledgeBase{IsPinned: false}}
require.NoError(t, runPin(context.Background(), &PinOptions{}, &cmdutil.FormatOptions{Mode: cmdutil.FormatText}, svc, "kb_abc", true))
assert.True(t, svc.toggleCalled, "must call toggle when current state differs")
assert.Contains(t, out.String(), "kb_abc")
}
func TestPin_AlreadyPinned_NoOp(t *testing.T) {
out, _ := iostreams.SetForTest(t)
svc := &fakePinSvc{current: sdk.KnowledgeBase{IsPinned: true}}
require.NoError(t, runPin(context.Background(), &PinOptions{}, &cmdutil.FormatOptions{Mode: cmdutil.FormatText}, svc, "kb_abc", true))
assert.False(t, svc.toggleCalled, "already pinned ⇒ must not call toggle")
assert.Contains(t, out.String(), "already pinned")
}
func TestUnpin_PinnedToUnpinned_CallsToggle(t *testing.T) {
_, _ = iostreams.SetForTest(t)
svc := &fakePinSvc{current: sdk.KnowledgeBase{IsPinned: true}}
require.NoError(t, runPin(context.Background(), &PinOptions{}, &cmdutil.FormatOptions{Mode: cmdutil.FormatText}, svc, "kb_abc", false))
assert.True(t, svc.toggleCalled)
}
func TestUnpin_AlreadyUnpinned_NoOp(t *testing.T) {
out, _ := iostreams.SetForTest(t)
svc := &fakePinSvc{current: sdk.KnowledgeBase{IsPinned: false}}
require.NoError(t, runPin(context.Background(), &PinOptions{}, &cmdutil.FormatOptions{Mode: cmdutil.FormatText}, svc, "kb_abc", false))
assert.False(t, svc.toggleCalled, "already unpinned ⇒ must not call toggle")
assert.Contains(t, out.String(), "already unpinned")
}
func TestPin_NotFound(t *testing.T) {
_, _ = iostreams.SetForTest(t)
svc := &fakePinSvc{getErr: errors.New("HTTP error 404: not found")}
err := runPin(context.Background(), &PinOptions{}, &cmdutil.FormatOptions{Mode: cmdutil.FormatText}, svc, "kb_missing", true)
require.Error(t, err)
var typed *cmdutil.Error
require.ErrorAs(t, err, &typed)
assert.Equal(t, cmdutil.CodeResourceNotFound, typed.Code)
assert.False(t, svc.toggleCalled)
}
func TestPin_ToggleError(t *testing.T) {
_, _ = iostreams.SetForTest(t)
svc := &fakePinSvc{
current: sdk.KnowledgeBase{IsPinned: false},
toggleErr: errors.New("HTTP error 500: internal"),
}
err := runPin(context.Background(), &PinOptions{}, &cmdutil.FormatOptions{Mode: cmdutil.FormatText}, svc, "kb_abc", true)
require.Error(t, err)
var typed *cmdutil.Error
require.ErrorAs(t, err, &typed)
assert.Equal(t, cmdutil.CodeServerError, typed.Code)
}
func TestPin_JSON(t *testing.T) {
out, _ := iostreams.SetForTest(t)
svc := &fakePinSvc{current: sdk.KnowledgeBase{IsPinned: false}}
require.NoError(t, runPin(context.Background(), &PinOptions{}, &cmdutil.FormatOptions{Mode: cmdutil.FormatJSON}, svc, "kb_abc", true))
body := out.String()
assert.Contains(t, body, `"is_pinned":true`)
assert.Contains(t, body, `"id":"kb_abc"`)
}