You've already forked agentic-coding-workflow
71a3a85b26
- IssueAdmin now rejects past/now ExpiresAt with CodeMcpTokenExpiresRequired - TestIssueAdmin_ExpiresMustBeFuture covers the new path - IssueSystemToken doc comment notes pgRepo currently ignores ctx tx; Part 3 must add Repository.WithTx for true same-transaction guarantee with acp_sessions (spec §6.1) - New scope_test.go (whitebox) verifies JSON round-trip preserves nil-vs-empty semantics on Tools / ProjectIDs and exercises AllowsTool / AllowsProject edges that Authenticate / future tool gating depend on
84 lines
2.3 KiB
Go
84 lines
2.3 KiB
Go
package mcp
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Scope 的 JSON round-trip 必须保留 nil-vs-empty 语义:
|
|
// - nil Tools/ProjectIDs = "允许全部"
|
|
// - empty slice = "全部禁止"
|
|
// repository.go 的 scopeToJSON / scopeFromJSON 是 encoding/json 的薄封装,
|
|
// 这里直接测公开类型的 marshal/unmarshal 路径,不耦合具体 helper 实现。
|
|
|
|
func TestScope_JSON_RoundTrip_Empty(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
raw, err := json.Marshal(Scope{})
|
|
require.NoError(t, err)
|
|
assert.JSONEq(t, "{}", string(raw), "empty Scope should marshal to {} via omitempty")
|
|
|
|
var got Scope
|
|
require.NoError(t, json.Unmarshal(raw, &got))
|
|
assert.Nil(t, got.Tools, "Tools must remain nil — meaning 'allow all'")
|
|
assert.Nil(t, got.ProjectIDs, "ProjectIDs must remain nil — meaning 'allow all'")
|
|
}
|
|
|
|
func TestScope_JSON_RoundTrip_Populated(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
pid := uuid.New()
|
|
in := Scope{
|
|
Tools: []string{"list_issues", "create_issue"},
|
|
ProjectIDs: []uuid.UUID{pid},
|
|
}
|
|
raw, err := json.Marshal(in)
|
|
require.NoError(t, err)
|
|
|
|
var out Scope
|
|
require.NoError(t, json.Unmarshal(raw, &out))
|
|
assert.Equal(t, in.Tools, out.Tools)
|
|
assert.Equal(t, in.ProjectIDs, out.ProjectIDs)
|
|
}
|
|
|
|
func TestScope_NilVsEmpty_AllowsTool(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var nilScope *Scope
|
|
assert.True(t, nilScope.AllowsTool("anything"), "nil receiver allows all")
|
|
|
|
zero := &Scope{} // Tools/ProjectIDs both nil
|
|
assert.True(t, zero.AllowsTool("anything"), "nil Tools allows all")
|
|
|
|
empty := &Scope{Tools: []string{}}
|
|
assert.False(t, empty.AllowsTool("anything"), "empty Tools forbids all")
|
|
|
|
allow := &Scope{Tools: []string{"list_issues"}}
|
|
assert.True(t, allow.AllowsTool("list_issues"))
|
|
assert.False(t, allow.AllowsTool("create_issue"))
|
|
}
|
|
|
|
func TestScope_NilVsEmpty_AllowsProject(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
pidA := uuid.New()
|
|
pidB := uuid.New()
|
|
|
|
var nilScope *Scope
|
|
assert.True(t, nilScope.AllowsProject(pidA))
|
|
|
|
zero := &Scope{}
|
|
assert.True(t, zero.AllowsProject(pidA), "nil ProjectIDs allows all")
|
|
|
|
empty := &Scope{ProjectIDs: []uuid.UUID{}}
|
|
assert.False(t, empty.AllowsProject(pidA), "empty ProjectIDs forbids all")
|
|
|
|
allow := &Scope{ProjectIDs: []uuid.UUID{pidA}}
|
|
assert.True(t, allow.AllowsProject(pidA))
|
|
assert.False(t, allow.AllowsProject(pidB))
|
|
}
|