Files
pangolin/infra/domains/tools/internal/mirror/mirror_test.go
T
wangjia 7d89ec9d91 feat(infra/domains): 域名池 + CDN 前置 + 签名端点分发 (tsk_NU9JuUweHWMt)
- domains.md: 四组域名隔离登记 + 冷备池 ≥5 + 启用流程(不含身份信息)
- cdn/terraform: Cloudflare 配置即代码(WAF/bot/速率限制/代理DNS/回源鉴权注入)+ 30min 重放 Runbook
- server/internal/originauth: 回源鉴权中间件,非 CDN 网段或鉴权头不符一律 403,支持双值轮换
- tools/endpoint-signer: 离线 Ed25519 签名 CLI(端点 + 公告文档,单调版本防回滚,key_id 双公钥轮换)
- tools/publish-mirrors: ≥3 镜像发布 + hash 一致性校验 + 故障转移取回
- CLIENT-CONTRACT.md: schema/验签/防回滚/合并/兜底链/channel 客户端契约
- 出站独立出口要求写入部署文档;私钥/token/身份信息一律不入库

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 14:21:55 +08:00

114 lines
3.0 KiB
Go

package mirror
import (
"errors"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
)
func threeDirTargets(t *testing.T) []Target {
t.Helper()
ts := make([]Target, 3)
for i := range ts {
dir := t.TempDir()
ts[i] = Target{Name: "m" + string(rune('1'+i)), Dir: dir, FetchURL: "file://" + dir}
}
return ts
}
func TestPublishAndConsistency(t *testing.T) {
targets := threeDirTargets(t)
content := []byte(`{"version":1,"sig":"x"}`)
results, err := Publish(content, "endpoints.v1.json", targets)
if err != nil {
t.Fatalf("Publish: %v", err)
}
if len(results) != 3 {
t.Fatalf("want 3 results, got %d", len(results))
}
want := SHA256Hex(content)
if err := VerifyConsistency("endpoints.v1.json", targets, want); err != nil {
t.Fatalf("VerifyConsistency: %v", err)
}
}
func TestConsistencyDetectsCorruptMirror(t *testing.T) {
targets := threeDirTargets(t)
content := []byte(`{"version":1}`)
if _, err := Publish(content, "doc.json", targets); err != nil {
t.Fatal(err)
}
// Corrupt mirror #2.
if err := os.WriteFile(filepath.Join(targets[1].Dir, "doc.json"), []byte("tampered"), 0o644); err != nil {
t.Fatal(err)
}
if err := VerifyConsistency("doc.json", targets, SHA256Hex(content)); err == nil {
t.Fatal("want consistency error for corrupt mirror")
}
}
func TestFetchFailoverSkipsInvalid(t *testing.T) {
targets := threeDirTargets(t)
good := []byte("GOOD")
// m1 has bad content, m2 good, m3 good.
if err := os.WriteFile(filepath.Join(targets[0].Dir, "o.json"), []byte("BAD"), 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(targets[1].Dir, "o.json"), good, 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(targets[2].Dir, "o.json"), good, 0o644); err != nil {
t.Fatal(err)
}
validate := func(b []byte) error {
if string(b) != "GOOD" {
return errors.New("invalid")
}
return nil
}
got, winner, err := Fetch("o.json", targets, validate)
if err != nil {
t.Fatalf("Fetch: %v", err)
}
if string(got) != "GOOD" {
t.Fatalf("got %q", got)
}
if winner != targets[1].Name {
t.Fatalf("winner = %q, want %q", winner, targets[1].Name)
}
}
func TestFetchFailoverHTTPMirrorDown(t *testing.T) {
// First mirror: HTTP 500. Second mirror: serves the document.
down := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer down.Close()
up := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("PAYLOAD"))
}))
defer up.Close()
targets := []Target{
{Name: "down", FetchURL: down.URL},
{Name: "up", FetchURL: up.URL},
}
got, winner, err := Fetch("any.json", targets, nil)
if err != nil {
t.Fatalf("Fetch: %v", err)
}
if string(got) != "PAYLOAD" || winner != "up" {
t.Fatalf("got=%q winner=%q", got, winner)
}
}
func TestFetchAllFail(t *testing.T) {
targets := []Target{{Name: "x", Dir: t.TempDir()}}
if _, _, err := Fetch("missing.json", targets, nil); err == nil {
t.Fatal("want error when all mirrors fail")
}
}