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") } }