Files
m1saka 7d8a5b6f74 Update proxy to Zhanlu v1.4.2 provider flow
The 1.4.2 extension replaced the old signed/encrypted chat gateway with an
OpenAI-compatible aigateway. Align the proxy with the new flow:

- Use ecloud.10086.cn login/model base URLs, zhanlu_ide plugin headers and
  v1.4.2 plugin version
- Provision the model API key via SM2-signed get-or-create after v1/login
  profile fetch; store api_key/model_base_url/email in credentials
- Chat via Bearer apiKey against {modelBaseUrl}/chat/completions with plain
  OpenAI SSE passthrough; fetch /v1/models from the gateway model-info endpoint
- Force HTTP/1.1 upstream (gateway drops HTTP/2 ALPN negotiation with EOF)
- Drop obsolete AES body encryption, model name mapping and vscode headers
2026-08-05 14:55:28 +08:00

75 lines
2.4 KiB
Go

package sign
import (
"encoding/hex"
"net/url"
"strings"
"testing"
"time"
"git.misaka.ren/M1saka/zhanlu_proxy/internal/auth"
)
const testPublicKeyPEM = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhxudxTewPgljUHEZHkusP7m3I+zA4/RGvuUMt6TtII/m4zwUOm/Y31zHBTmkCCt8k5vj9y+AmO0TsGmHooNQuMebakdmEWdcA5h7YAHHFbF2w5LcxIXjib08vgVpA+m3R5xPbLK+vfHe2aAX36b5nHReDNncY5vAl3U4CgIEBGPqyG67vJytRWqP+sfEdw5+m192Rf4SCGyiBzRmjiVlH3zeEBjdbOrkAnzKOVz6AHBl2q7LPLJKIzxjoAyhEp5qnDjHUFo5VZUgFwUOt83A/jbGMyzmjRoxBuvKcs9tBuorZyUwIsZN6E+rtQk2YqMPj4RkDsZ7LRmj6on8sN2rHQIDAQAB
-----END PUBLIC KEY-----`
func TestBuildOpURLStructure(t *testing.T) {
pub, err := auth.ParsePublicKey(testPublicKeyPEM)
if err != nil {
t.Fatal(err)
}
now := time.Date(2026, 8, 5, 2, 30, 0, 0, time.UTC)
s := Signer{
PublicKey: pub,
Now: func() time.Time { return now },
Nonce: func() string { return "fixed-nonce-1234567890" },
}
creds := auth.Credentials{AccessKey: "AK123", SecretKey: "SK456", Token: "TOK789"}
u, err := s.BuildOpURL("/api/acepilot/zhanlu/v1/login", creds, "https://ecloud.10086.cn", "POST")
if err != nil {
t.Fatal(err)
}
if !strings.HasPrefix(u, "https://ecloud.10086.cn/api/acepilot/zhanlu/v1/login?") {
t.Fatalf("url = %s", u)
}
parsed, err := url.Parse(u)
if err != nil {
t.Fatal(err)
}
q := parsed.Query()
if q.Get("AccessKey") != "AK123" {
t.Errorf("AccessKey = %q", q.Get("AccessKey"))
}
if q.Get("SignatureMethod") != "HmacSHA1" {
t.Errorf("SignatureMethod = %q", q.Get("SignatureMethod"))
}
if q.Get("SignatureVersion") != "V2.0" {
t.Errorf("SignatureVersion = %q", q.Get("SignatureVersion"))
}
if q.Get("Version") != "2016-12-05" {
t.Errorf("Version = %q", q.Get("Version"))
}
// Beijing time (UTC+8) formatted with Z suffix, matching the plugin's yE9.
if q.Get("Timestamp") != "2026-08-05T10:30:00Z" {
t.Errorf("Timestamp = %q, want 2026-08-05T10:30:00Z (Beijing time)", q.Get("Timestamp"))
}
if q.Get("Signature") == "" {
t.Error("Signature is empty")
}
if _, err := hex.DecodeString(q.Get("Signature")); err != nil {
t.Errorf("Signature is not hex: %v", err)
}
authz := q.Get("authorization")
if authz == "" {
t.Error("authorization is empty")
}
}
func TestBuildOpURLMissingCreds(t *testing.T) {
s := Signer{}
if _, err := s.BuildOpURL("/x", auth.Credentials{}, "http://x", "POST"); err == nil {
t.Fatal("expected error for missing credentials")
}
}