Files
Memoh/internal/mcp/mcpclient/client_test.go
T
Ringo.Typowriter e6a6dbe3f6 feat(channel): add QQ channel support and image message pipeline (#199)
* feat(channel): add qq adapter and outbound delivery

* feat(channel): ingest inbound qq messages

* feat(web): expose qq channel in management ui

* feat(channel): support qq attachment ingestion

* fix(mcp): fail read raw immediately for missing files

* fix(agent): parse inline image data into native image parts

* test(agent): align read_media tool tests with SDK options

* fix(channel): harden qq image delivery and reconnect loop

Avoid data URLs for qq channel images, reset reconnect backoff after healthy sessions, and fall back gracefully for malformed public image URLs.

* fix(channel): restore qq media delivery and target resolution

* fix(qq,mcp,agent): fix message/qq regressions and pass go lint

* fix(qq,agent): validate inline base64 and sync heartbeat seq

* fix(qq): validate remote voice mime for upload checks

* fix(qq): fall back intents and restore adapter wiring

* fix(qq): prevent final text leakage and dedupe persisted inbound query
2026-03-07 17:12:06 +08:00

131 lines
3.1 KiB
Go

package mcpclient
import (
"context"
"errors"
"io"
"net"
"testing"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/status"
"google.golang.org/grpc/test/bufconn"
pb "github.com/memohai/memoh/internal/mcp/mcpcontainer"
)
const testBufSize = 1 << 20
type rawReadTestServer struct {
pb.UnimplementedContainerServiceServer
files map[string][]byte
}
func (s *rawReadTestServer) ReadRaw(req *pb.ReadRawRequest, stream pb.ContainerService_ReadRawServer) error {
data, ok := s.files[req.GetPath()]
if !ok {
return status.Errorf(codes.NotFound, "open: open %s: no such file or directory", req.GetPath())
}
if len(data) == 0 {
return nil
}
if err := stream.Send(&pb.DataChunk{Data: data[:1]}); err != nil {
return err
}
if len(data) > 1 {
if err := stream.Send(&pb.DataChunk{Data: data[1:]}); err != nil {
return err
}
}
return nil
}
func newTestReadRawClient(t *testing.T, files map[string][]byte) *Client {
t.Helper()
lis := bufconn.Listen(testBufSize)
srv := grpc.NewServer()
pb.RegisterContainerServiceServer(srv, &rawReadTestServer{files: files})
done := make(chan struct{})
go func() {
defer close(done)
_ = srv.Serve(lis)
}()
t.Cleanup(func() {
srv.Stop()
<-done
})
dialer := func(ctx context.Context, _ string) (net.Conn, error) {
return lis.DialContext(ctx)
}
conn, err := grpc.NewClient("passthrough://bufnet",
grpc.WithContextDialer(dialer),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
t.Fatalf("grpc.NewClient: %v", err)
}
t.Cleanup(func() { _ = conn.Close() })
return NewClientFromConn(conn)
}
func TestClientReadRawMissingFileReturnsNotFoundImmediately(t *testing.T) {
t.Parallel()
client := newTestReadRawClient(t, map[string][]byte{})
_, err := client.ReadRaw(context.Background(), "/data/media/missing.jpg")
if err == nil {
t.Fatal("expected read raw to fail for missing file")
}
if !errors.Is(err, ErrNotFound) {
t.Fatalf("expected ErrNotFound, got %v", err)
}
}
func TestClientReadRawPreservesFirstChunk(t *testing.T) {
t.Parallel()
client := newTestReadRawClient(t, map[string][]byte{
"/data/media/existing.jpg": []byte("hello"),
})
reader, err := client.ReadRaw(context.Background(), "/data/media/existing.jpg")
if err != nil {
t.Fatalf("ReadRaw returned error: %v", err)
}
defer func() { _ = reader.Close() }()
data, err := io.ReadAll(reader)
if err != nil {
t.Fatalf("read raw reader failed: %v", err)
}
if got := string(data); got != "hello" {
t.Fatalf("expected full payload, got %q", got)
}
}
func TestClientReadRawSupportsEmptyFile(t *testing.T) {
t.Parallel()
client := newTestReadRawClient(t, map[string][]byte{
"/data/media/empty.txt": {},
})
reader, err := client.ReadRaw(context.Background(), "/data/media/empty.txt")
if err != nil {
t.Fatalf("ReadRaw returned error: %v", err)
}
defer func() { _ = reader.Close() }()
data, err := io.ReadAll(reader)
if err != nil {
t.Fatalf("read raw empty reader failed: %v", err)
}
if len(data) != 0 {
t.Fatalf("expected empty payload, got %q", string(data))
}
}