yao/grpc/auth/guard_test.go
Max 6e68efaba3 Implement gRPC support in the Yao SDK
- Add gRPC server configuration to the application, allowing for gRPC communication.
- Introduce new Makefile targets for gRPC unit testing and proto code generation.
- Update CI workflows to include gRPC tests with SQLite as the transport layer.
- Refactor the sandbox design to support multi-node capabilities and improve isolation.
- Enhance the service layer to facilitate internal request forwarding for gRPC APIs.

This commit lays the groundwork for integrating gRPC into the Yao SDK, improving performance and scalability.
2026-03-04 13:17:48 +08:00

169 lines
4.9 KiB
Go

package auth_test
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"github.com/yaoapp/yao/grpc/pb"
"github.com/yaoapp/yao/grpc/tests/testutils"
)
func TestAuth_NoToken_Rejected(t *testing.T) {
conn := testutils.Prepare(t)
defer testutils.Clean()
client := testutils.NewClient(conn)
_, err := client.Run(context.Background(), &pb.RunRequest{Process: "utils.app.Ping"})
assert.Error(t, err)
st, ok := status.FromError(err)
assert.True(t, ok)
assert.Equal(t, codes.Unauthenticated, st.Code())
}
func TestAuth_ValidToken(t *testing.T) {
conn := testutils.Prepare(t)
defer testutils.Clean()
client := testutils.NewClient(conn)
token := testutils.ObtainAccessToken(t, "grpc:run")
ctx := testutils.WithToken(context.Background(), token)
_, err := client.Run(ctx, &pb.RunRequest{Process: "utils.app.Ping"})
// Run returns Unimplemented (handler stub), not an auth error
st, ok := status.FromError(err)
assert.True(t, ok)
assert.NotEqual(t, codes.Unauthenticated, st.Code())
assert.NotEqual(t, codes.PermissionDenied, st.Code())
}
func TestAuth_WrongScope_Denied(t *testing.T) {
conn := testutils.Prepare(t)
defer testutils.Clean()
client := testutils.NewClient(conn)
token := testutils.ObtainAccessToken(t, "grpc:mcp")
ctx := testutils.WithToken(context.Background(), token)
_, err := client.Run(ctx, &pb.RunRequest{Process: "utils.app.Ping"})
assert.Error(t, err)
st, ok := status.FromError(err)
assert.True(t, ok)
assert.Equal(t, codes.PermissionDenied, st.Code())
}
func TestAuth_TokenRefresh(t *testing.T) {
conn := testutils.Prepare(t)
defer testutils.Clean()
client := testutils.NewClient(conn)
expiredToken := testutils.ObtainExpiredAccessToken(t, "grpc:run")
refreshToken := testutils.ObtainRefreshToken(t, "grpc:run")
ctx := testutils.WithRefreshToken(context.Background(), expiredToken, refreshToken)
// The call should succeed (auth interceptor refreshes the token)
_, err := client.Run(ctx, &pb.RunRequest{Process: "utils.app.Ping"})
st, ok := status.FromError(err)
assert.True(t, ok)
// Should not be an auth error — either Unimplemented (handler stub) or OK
assert.NotEqual(t, codes.Unauthenticated, st.Code())
assert.NotEqual(t, codes.PermissionDenied, st.Code())
}
func TestHealthz_Public(t *testing.T) {
conn := testutils.Prepare(t)
defer testutils.Clean()
client := testutils.NewClient(conn)
resp, err := client.Healthz(context.Background(), &pb.Empty{})
assert.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, "ok", resp.Status)
}
func TestAuth_InvalidBearerFormat(t *testing.T) {
conn := testutils.Prepare(t)
defer testutils.Clean()
client := testutils.NewClient(conn)
ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "not-a-valid-token-at-all")
_, err := client.Run(ctx, &pb.RunRequest{Process: "utils.app.Ping"})
assert.Error(t, err)
st, _ := status.FromError(err)
assert.Equal(t, codes.Unauthenticated, st.Code())
}
func TestAuth_StreamInterceptor_NoToken(t *testing.T) {
conn := testutils.Prepare(t)
defer testutils.Clean()
client := testutils.NewClient(conn)
stream, err := client.ChatCompletionsStream(context.Background(), &pb.ChatRequest{
Connector: "openai",
})
if err != nil {
st, _ := status.FromError(err)
assert.Equal(t, codes.Unauthenticated, st.Code())
return
}
_, err = stream.Recv()
assert.Error(t, err)
st, _ := status.FromError(err)
assert.Equal(t, codes.Unauthenticated, st.Code())
}
func TestAuth_StreamInterceptor_ValidToken(t *testing.T) {
conn := testutils.Prepare(t)
defer testutils.Clean()
client := testutils.NewClient(conn)
token := testutils.ObtainAccessToken(t, "grpc:agent")
ctx := testutils.WithToken(context.Background(), token)
stream, err := client.AgentStream(ctx, &pb.AgentRequest{
AssistantId: "nonexistent",
Messages: []byte(`[{"role":"user","content":"hi"}]`),
})
if err != nil {
st, _ := status.FromError(err)
assert.NotEqual(t, codes.Unauthenticated, st.Code())
assert.NotEqual(t, codes.PermissionDenied, st.Code())
return
}
_, err = stream.Recv()
assert.Error(t, err)
st, _ := status.FromError(err)
assert.NotEqual(t, codes.Unauthenticated, st.Code())
assert.NotEqual(t, codes.PermissionDenied, st.Code())
}
func TestAuth_StreamInterceptor_WrongScope(t *testing.T) {
conn := testutils.Prepare(t)
defer testutils.Clean()
client := testutils.NewClient(conn)
token := testutils.ObtainAccessToken(t, "grpc:run")
ctx := testutils.WithToken(context.Background(), token)
stream, err := client.AgentStream(ctx, &pb.AgentRequest{
AssistantId: "test",
Messages: []byte(`[{"role":"user","content":"hi"}]`),
})
if err != nil {
st, _ := status.FromError(err)
assert.Equal(t, codes.PermissionDenied, st.Code())
return
}
_, err = stream.Recv()
assert.Error(t, err)
st, _ := status.FromError(err)
assert.Equal(t, codes.PermissionDenied, st.Code())
}