Files
sub2api-cn-relay-manager/internal/pack/publisher_test.go
2026-05-28 07:30:02 +08:00

172 lines
5.4 KiB
Go

package pack
import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
func TestPublishProviderManifestWritesProviderBumpsPackAndCommits(t *testing.T) {
t.Parallel()
if _, err := exec.LookPath("git"); err != nil {
t.Skip("git is required for publish test")
}
repoRoot := t.TempDir()
packDir := filepath.Join(repoRoot, "packs", "openai-cn-pack")
createPackFixtureAt(t, packDir, map[string]string{
"pack.json": `{
"pack_id": "openai-cn-pack",
"version": "1.1.4",
"vendor": "YourTeam",
"target_host": "sub2api",
"min_host_version": "0.1.126",
"max_host_version": "0.2.x",
"providers_dir": "providers",
"checksum_file": "checksums.txt"
}`,
"providers/minimax-53hk.json": `{
"provider_id": "minimax-53hk",
"display_name": "MiniMax 53hk 中转兼容",
"base_url": "https://api.53hk.cn/v1",
"platform": "openai",
"account_type": "apikey",
"default_models": ["MiniMax-M2.7-highspeed"],
"smoke_test_model": "MiniMax-M2.7-highspeed",
"group_template": {"name": "g", "rate_multiplier": 1.0},
"channel_template": {"name": "c", "model_mapping": {"MiniMax-M2.7-highspeed": "MiniMax-M2.7-highspeed"}},
"plan_template": {"name": "p", "price": 1, "validity_days": 30, "validity_unit": "day"},
"import": {"supports_multi_key": true, "supports_strict": true, "supports_partial": true}
}`,
})
initGitRepo(t, repoRoot)
result, err := PublishProviderManifest(context.Background(), PublishProviderManifestRequest{
RepoRoot: repoRoot,
PackID: "openai-cn-pack",
Manifest: ProviderManifest{
ProviderID: "openai-zhongzhuan",
DisplayName: "OpenAI 中转",
BaseURL: "https://api.example.com/v1",
Platform: "openai",
SmokeTestModel: "gpt-5.4",
DefaultModels: []string{"gpt-5.4", "gpt-5.4-mini"},
},
CommitMessage: "feat(pack): publish openai-zhongzhuan",
})
if err != nil {
t.Fatalf("PublishProviderManifest() error = %v", err)
}
if result.PublishMode != "created" {
t.Fatalf("PublishMode = %q, want created", result.PublishMode)
}
if result.PackVersionBefore != "1.1.4" || result.PackVersionAfter != "1.1.5" {
t.Fatalf("pack versions = %q -> %q, want 1.1.4 -> 1.1.5", result.PackVersionBefore, result.PackVersionAfter)
}
if strings.TrimSpace(result.CommitSHA) == "" {
t.Fatal("CommitSHA is empty")
}
publishedPack, err := LoadDir(packDir)
if err != nil {
t.Fatalf("LoadDir() after publish error = %v", err)
}
if publishedPack.Manifest.Version != "1.1.5" {
t.Fatalf("published pack version = %q, want 1.1.5", publishedPack.Manifest.Version)
}
body, err := readFileString(filepath.Join(packDir, "providers", "openai-zhongzhuan.json"))
if err != nil {
t.Fatalf("readFileString() provider error = %v", err)
}
var provider ProviderManifest
if err := json.Unmarshal([]byte(body), &provider); err != nil {
t.Fatalf("json.Unmarshal() provider error = %v", err)
}
if provider.AccountType != "apikey" {
t.Fatalf("AccountType = %q, want apikey", provider.AccountType)
}
if provider.ChannelTemplate.ModelMapping["gpt-5.4-mini"] != "gpt-5.4-mini" {
t.Fatalf("ChannelTemplate.ModelMapping = %+v, want identity mapping for gpt-5.4-mini", provider.ChannelTemplate.ModelMapping)
}
checksums, err := readFileString(filepath.Join(packDir, "checksums.txt"))
if err != nil {
t.Fatalf("readFileString() checksums error = %v", err)
}
if !strings.Contains(checksums, "providers/openai-zhongzhuan.json") {
t.Fatalf("checksums.txt = %q, want providers/openai-zhongzhuan.json entry", checksums)
}
}
func TestPublishProviderManifestRejectsMissingRepoRoot(t *testing.T) {
t.Parallel()
_, err := PublishProviderManifest(context.Background(), PublishProviderManifestRequest{
PackID: "openai-cn-pack",
Manifest: ProviderManifest{
ProviderID: "deepseek",
DisplayName: "DeepSeek",
BaseURL: "https://api.deepseek.com",
Platform: "openai",
SmokeTestModel: "deepseek-chat",
DefaultModels: []string{"deepseek-chat"},
},
})
if err == nil || !strings.Contains(err.Error(), "repo root") {
t.Fatalf("PublishProviderManifest() error = %v, want repo root detail", err)
}
}
func createPackFixtureAt(t *testing.T, packDir string, files map[string]string) {
t.Helper()
var lines []string
for relativePath, content := range files {
absolutePath := filepath.Join(packDir, relativePath)
mustWrite(t, absolutePath, content)
sum := sha256Sum(content)
lines = append(lines, sum+" "+relativePath)
}
mustWrite(t, filepath.Join(packDir, "checksums.txt"), strings.Join(lines, "\n")+"\n")
}
func sha256Sum(content string) string {
sum := sha256.Sum256([]byte(content))
return hex.EncodeToString(sum[:])
}
func initGitRepo(t *testing.T, repoRoot string) {
t.Helper()
runGitTest(t, repoRoot, "init")
runGitTest(t, repoRoot, "config", "user.name", "Test User")
runGitTest(t, repoRoot, "config", "user.email", "test@example.com")
runGitTest(t, repoRoot, "add", ".")
runGitTest(t, repoRoot, "commit", "-m", "chore: seed pack fixture")
}
func runGitTest(t *testing.T, repoRoot string, args ...string) {
t.Helper()
cmd := exec.Command("git", append([]string{"-C", repoRoot}, args...)...)
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("git %v error = %v: %s", args, err, strings.TrimSpace(string(output)))
}
}
func readFileString(path string) (string, error) {
body, err := os.ReadFile(path)
if err != nil {
return "", err
}
return string(body), nil
}