package mcpserver import ( "context" "crypto/cipher" "crypto/des" "encoding/base64" "encoding/json" "os" "reflect" "sort" "testing" "time" "github.com/modelcontextprotocol/go-sdk/mcp" ) func TestNewServerConstructsMCPServer(t *testing.T) { var server *mcp.Server = NewServer() if server == nil { t.Fatal("NewServer() returned nil") } } func TestNewServerRegistersN8WinHelperToolsWithoutCallingHelper(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) serverTransport, clientTransport := mcp.NewInMemoryTransports() server := NewServer() errCh := make(chan error, 1) go func() { errCh <- server.Run(ctx, serverTransport) }() client := mcp.NewClient(&mcp.Implementation{Name: "mcpserver-test-client", Version: "0.0.0"}, nil) session, err := client.Connect(ctx, clientTransport, nil) if err != nil { cancel() t.Fatalf("connect client: %v", err) } defer func() { _ = session.Close() cancel() select { case <-errCh: case <-time.After(2 * time.Second): t.Fatalf("server did not stop") } }() result, err := session.ListTools(context.Background(), &mcp.ListToolsParams{}) if err != nil { t.Fatalf("list tools: %v", err) } gotNames := make([]string, 0, len(result.Tools)) for _, tool := range result.Tools { gotNames = append(gotNames, tool.Name) } sort.Strings(gotNames) wantNames := []string{"isphere_receive_files", "isphere_receive_messages", "isphere_search_contacts", "isphere_search_groups", "win_helper_dump_uia", "win_helper_scan_windows", "win_helper_self_check", "win_helper_version"} if !reflect.DeepEqual(gotNames, wantNames) { t.Fatalf("server tool names = %#v, want %#v", gotNames, wantNames) } } func TestNewServerFromEnvUsesPacketLogFile(t *testing.T) { plaintext := `-------------------------------------------------------------------------------------------------------------------------------------------- 2026/7/7 15:30:07 redacted from env ENV_SOURCE ` filePath := writeEncryptedPacketLogFileForServerTest(t, plaintext) t.Setenv("ISPHERE_PACKET_LOG_FILE", filePath) server, err := NewServerFromEnv() if err != nil { t.Fatalf("NewServerFromEnv returned error: %v", err) } session, cleanup := connectServerTestSession(t, server) defer cleanup() callResult, err := session.CallTool(context.Background(), &mcp.CallToolParams{ Name: "isphere_receive_messages", Arguments: map[string]any{"limit": 10}, }) if err != nil { t.Fatalf("call isphere_receive_messages: %v", err) } if callResult.IsError { t.Fatalf("call result is error: %+v", callResult) } var decoded struct { Messages []struct { ID string `json:"id"` Text string `json:"text"` Subject string `json:"subject"` ConversationID string `json:"conversation_id"` SenderID string `json:"sender_id"` ReceiverID string `json:"receiver_id"` Read bool `json:"read"` } `json:"messages"` } payload, err := json.Marshal(callResult.StructuredContent) if err != nil { t.Fatalf("marshal structured content: %v", err) } if err := json.Unmarshal(payload, &decoded); err != nil { t.Fatalf("decode structured content %s: %v", payload, err) } if len(decoded.Messages) != 1 { t.Fatalf("message count = %d, want 1", len(decoded.Messages)) } msg := decoded.Messages[0] if msg.ID != "msg-env-1" || msg.Text != "redacted from env" || msg.Subject != "ENV_SOURCE" { t.Fatalf("unexpected message: %+v", msg) } if msg.ConversationID != "sender@imopenfire1-lanzhou|receiver@imopenfire1-lanzhou" { t.Fatalf("conversation id = %q", msg.ConversationID) } if msg.SenderID != "sender@imopenfire1-lanzhou" || msg.ReceiverID != "receiver@imopenfire1-lanzhou" || !msg.Read { t.Fatalf("metadata = %+v", msg) } } func TestNewServerFromEnvUsesPacketLogDirectory(t *testing.T) { plaintext := `-------------------------------------------------------------------------------------------------------------------------------------------- 2026/7/7 15:30:07 redacted from dir DIR_SOURCE ` dirPath := writeEncryptedPacketLogDirectoryForServerTest(t, plaintext) t.Setenv("ISPHERE_PACKET_LOG_DIR", dirPath) server, err := NewServerFromEnv() if err != nil { t.Fatalf("NewServerFromEnv returned error: %v", err) } session, cleanup := connectServerTestSession(t, server) defer cleanup() callResult, err := session.CallTool(context.Background(), &mcp.CallToolParams{ Name: "isphere_receive_messages", Arguments: map[string]any{"limit": 10}, }) if err != nil { t.Fatalf("call isphere_receive_messages: %v", err) } if callResult.IsError { t.Fatalf("call result is error: %+v", callResult) } var decoded struct { Messages []struct { ID string `json:"id"` Text string `json:"text"` Subject string `json:"subject"` } `json:"messages"` } payload, err := json.Marshal(callResult.StructuredContent) if err != nil { t.Fatalf("marshal structured content: %v", err) } if err := json.Unmarshal(payload, &decoded); err != nil { t.Fatalf("decode structured content %s: %v", payload, err) } if len(decoded.Messages) != 1 { t.Fatalf("message count = %d, want 1", len(decoded.Messages)) } msg := decoded.Messages[0] if msg.ID != "msg-dir-1" || msg.Text != "redacted from dir" || msg.Subject != "DIR_SOURCE" { t.Fatalf("unexpected message: %+v", msg) } } func writeEncryptedPacketLogDirectoryForServerTest(t *testing.T, plaintext string) string { t.Helper() dir := t.TempDir() line := encryptPacketLogLineForServerTest(t, plaintext) if err := os.WriteFile(dir+"\\packet-reader-2.txt", []byte("\n"+line+"\n"), 0o600); err != nil { t.Fatalf("write encrypted packet log directory fixture: %v", err) } return dir } func connectServerTestSession(t *testing.T, server *mcp.Server) (*mcp.ClientSession, func()) { t.Helper() ctx, cancel := context.WithCancel(context.Background()) serverTransport, clientTransport := mcp.NewInMemoryTransports() errCh := make(chan error, 1) go func() { errCh <- server.Run(ctx, serverTransport) }() client := mcp.NewClient(&mcp.Implementation{Name: "mcpserver-env-test-client", Version: "0.0.0"}, nil) session, err := client.Connect(ctx, clientTransport, nil) if err != nil { cancel() t.Fatalf("connect client: %v", err) } cleanup := func() { _ = session.Close() cancel() select { case <-errCh: case <-time.After(2 * time.Second): t.Fatalf("server did not stop") } } return session, cleanup } func writeEncryptedPacketLogFileForServerTest(t *testing.T, plaintext string) string { t.Helper() path := t.TempDir() + "\\packet-reader.log" line := encryptPacketLogLineForServerTest(t, plaintext) if err := os.WriteFile(path, []byte("\n"+line+"\n"), 0o600); err != nil { t.Fatalf("write encrypted packet log file: %v", err) } return path } func encryptPacketLogLineForServerTest(t *testing.T, plaintext string) string { t.Helper() block, err := des.NewCipher([]byte("hyhccdtm")) if err != nil { t.Fatalf("NewCipher: %v", err) } plain := padPacketLogLineForServerTest([]byte(plaintext), des.BlockSize) ciphertext := make([]byte, len(plain)) iv := []byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF} cipher.NewCBCEncrypter(block, iv).CryptBlocks(ciphertext, plain) return base64.StdEncoding.EncodeToString(ciphertext) } func padPacketLogLineForServerTest(data []byte, blockSize int) []byte { pad := blockSize - len(data)%blockSize out := append([]byte(nil), data...) for i := 0; i < pad; i++ { out = append(out, byte(pad)) } return out }