ci: add code admission gate (#53)
* ci: add code admission gate * ci: fix fork release baseline (cherry picked from commit 7ff2f3a5f0435209908822e141a6355fc6fa4aa6)
This commit is contained in:
1 parent
d288820b64
commit
8f4ab176a8
11 files changed
+2108
-1
No files matched your search
@@ -0,0 +1,219 @@
|
||||
package mock_mcp_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/app"
|
||||
)
|
||||
|
||||
const mockMCPSmokeHelperEnv = "DWS_MOCK_MCP_SMOKE_HELPER"
|
||||
|
||||
type recordedToolCall struct {
|
||||
path string
|
||||
method string
|
||||
authorization string
|
||||
jsonrpc string
|
||||
tool string
|
||||
arguments map[string]any
|
||||
err error
|
||||
}
|
||||
|
||||
// TestCLIHelperProcess runs the production CLI entrypoint with real os.Args.
|
||||
// The parent test supplies only isolated temp directories, a loopback endpoint,
|
||||
// and a synthetic token accepted by the local fake server.
|
||||
func TestCLIHelperProcess(t *testing.T) {
|
||||
if os.Getenv(mockMCPSmokeHelperEnv) != "1" {
|
||||
return
|
||||
}
|
||||
|
||||
marker := -1
|
||||
for i, arg := range os.Args {
|
||||
if arg == "--" {
|
||||
marker = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if marker < 0 {
|
||||
fmt.Fprintln(os.Stderr, "Mock MCP smoke helper: missing -- argument marker")
|
||||
os.Exit(2)
|
||||
}
|
||||
os.Args = append([]string{"dws"}, os.Args[marker+1:]...)
|
||||
os.Exit(app.Execute())
|
||||
}
|
||||
|
||||
func TestMockMCPSmoke_CLIRoutesSerializedArgumentsAndPrintsJSON(t *testing.T) {
|
||||
var requestsMu sync.Mutex
|
||||
var requests []recordedToolCall
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
call := recordedToolCall{
|
||||
path: r.URL.Path,
|
||||
method: r.Method,
|
||||
authorization: r.Header.Get("Authorization"),
|
||||
}
|
||||
|
||||
var envelope struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
ID int `json:"id"`
|
||||
Method string `json:"method"`
|
||||
Params struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments"`
|
||||
} `json:"params"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&envelope); err != nil {
|
||||
call.err = err
|
||||
} else {
|
||||
call.jsonrpc = envelope.JSONRPC
|
||||
call.tool = envelope.Params.Name
|
||||
call.arguments = envelope.Params.Arguments
|
||||
if envelope.Method != "tools/call" {
|
||||
call.err = fmt.Errorf("JSON-RPC method = %q, want tools/call", envelope.Method)
|
||||
}
|
||||
}
|
||||
requestsMu.Lock()
|
||||
requests = append(requests, call)
|
||||
requestsMu.Unlock()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"jsonrpc": "2.0",
|
||||
"id": envelope.ID,
|
||||
"result": map[string]any{
|
||||
"content": []map[string]any{{
|
||||
"type": "text",
|
||||
"text": `{"success":true,"result":[{"userId":"mock-user-1","name":"Local Mock"}]}`,
|
||||
}},
|
||||
},
|
||||
})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
env := isolatedCLIEnv(t, map[string]string{
|
||||
"DINGTALK_CONTACT_MCP_URL": server.URL + "/mcp/contact",
|
||||
})
|
||||
args := []string{
|
||||
"--token", "ci-smoke-token",
|
||||
"--format", "json",
|
||||
"contact", "user", "get",
|
||||
"--ids", "user-001,user-002",
|
||||
}
|
||||
stdout, stderr, err := runCLI(t, env, args...)
|
||||
if err != nil {
|
||||
t.Fatalf("dws %s failed: %v\nstdout:\n%s\nstderr:\n%s", strings.Join(args, " "), err, stdout, stderr)
|
||||
}
|
||||
|
||||
requestsMu.Lock()
|
||||
recorded := append([]recordedToolCall(nil), requests...)
|
||||
requestsMu.Unlock()
|
||||
if len(recorded) != 1 {
|
||||
t.Fatalf("local fake MCP server received %d requests, want exactly one tools/call: %#v", len(recorded), recorded)
|
||||
}
|
||||
call := recorded[0]
|
||||
if call.err != nil {
|
||||
t.Fatal(call.err)
|
||||
}
|
||||
if call.path != "/mcp/contact" {
|
||||
t.Fatalf("request path = %q, want /mcp/contact", call.path)
|
||||
}
|
||||
if call.method != http.MethodPost {
|
||||
t.Fatalf("HTTP method = %q, want POST", call.method)
|
||||
}
|
||||
if call.authorization != "Bearer ci-smoke-token" {
|
||||
t.Fatalf("Authorization = %q, want synthetic smoke token", call.authorization)
|
||||
}
|
||||
if call.jsonrpc != "2.0" {
|
||||
t.Fatalf("jsonrpc = %q, want 2.0", call.jsonrpc)
|
||||
}
|
||||
if call.tool != "get_user_info_by_user_ids" {
|
||||
t.Fatalf("tool = %q, want get_user_info_by_user_ids", call.tool)
|
||||
}
|
||||
wantArgs := map[string]any{"user_id_list": []any{"user-001", "user-002"}}
|
||||
if !reflect.DeepEqual(call.arguments, wantArgs) {
|
||||
t.Fatalf("arguments = %#v, want %#v", call.arguments, wantArgs)
|
||||
}
|
||||
|
||||
var payload map[string]any
|
||||
if err := json.Unmarshal([]byte(stdout), &payload); err != nil {
|
||||
t.Fatalf("CLI returned non-JSON stdout: %v\nstdout:\n%s\nstderr:\n%s", err, stdout, stderr)
|
||||
}
|
||||
if payload["success"] != true {
|
||||
t.Fatalf("CLI success = %#v, want true; payload=%#v", payload["success"], payload)
|
||||
}
|
||||
result, ok := payload["result"].([]any)
|
||||
if !ok || len(result) != 1 {
|
||||
t.Fatalf("CLI result = %#v, want one mock user", payload["result"])
|
||||
}
|
||||
user, _ := result[0].(map[string]any)
|
||||
if user["userId"] != "mock-user-1" {
|
||||
t.Fatalf("CLI userId = %#v, want mock-user-1; payload=%#v", user["userId"], payload)
|
||||
}
|
||||
}
|
||||
|
||||
func isolatedCLIEnv(t *testing.T, extra map[string]string) []string {
|
||||
t.Helper()
|
||||
|
||||
root := t.TempDir()
|
||||
controlled := map[string]string{
|
||||
"HOME": root,
|
||||
"USERPROFILE": root,
|
||||
"DWS_CONFIG_DIR": filepath.Join(root, "config"),
|
||||
"DWS_KEYCHAIN_DIR": filepath.Join(root, "keychain"),
|
||||
"DWS_DISABLE_KEYCHAIN": "1",
|
||||
"HTTP_PROXY": "http://127.0.0.1:1",
|
||||
"HTTPS_PROXY": "http://127.0.0.1:1",
|
||||
"http_proxy": "http://127.0.0.1:1",
|
||||
"https_proxy": "http://127.0.0.1:1",
|
||||
"NO_PROXY": "127.0.0.1,localhost,::1",
|
||||
"no_proxy": "127.0.0.1,localhost,::1",
|
||||
mockMCPSmokeHelperEnv: "1",
|
||||
"DWS_ALLOW_HTTP_ENDPOINTS": "1",
|
||||
"DWS_TRUSTED_DOMAINS": "127.0.0.1,localhost,::1",
|
||||
}
|
||||
for key, value := range extra {
|
||||
controlled[key] = value
|
||||
}
|
||||
|
||||
env := make([]string, 0, len(controlled)+8)
|
||||
for _, key := range []string{"PATH", "TMPDIR", "TEMP", "TMP", "LANG", "LC_ALL", "TZ", "SYSTEMROOT"} {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
env = append(env, key+"="+value)
|
||||
}
|
||||
}
|
||||
for key, value := range controlled {
|
||||
env = append(env, key+"="+value)
|
||||
}
|
||||
sort.Strings(env)
|
||||
return env
|
||||
}
|
||||
|
||||
func runCLI(t *testing.T, env []string, args ...string) (string, string, error) {
|
||||
t.Helper()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
processArgs := append([]string{"-test.run=^TestCLIHelperProcess$", "--"}, args...)
|
||||
cmd := exec.CommandContext(ctx, os.Args[0], processArgs...)
|
||||
cmd.Env = env
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Run()
|
||||
if ctx.Err() != nil {
|
||||
t.Fatalf("dws %s timed out: %v\nstdout:\n%s\nstderr:\n%s", strings.Join(args, " "), ctx.Err(), stdout.String(), stderr.String())
|
||||
}
|
||||
return stdout.String(), stderr.String(), err
|
||||
}
|
||||
Reference in new issue
Block a user