fixtures

package
v0.5.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 16, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package fixtures 提供 go-reqx 测试体系下的"标准输入数据"。

设计原则:

  1. 一切"测试反复构造的 payload"都集中在这里,避免每个 _test.go 自己手写;

  2. 函数命名采用 BuildXxx / NewXxx 前缀: - BuildXxx 表示"在 TempTree 上构造副作用(写文件等)",需要 *testing.T; - NewXxx 表示"返回纯字节 / 结构体",不需要 *testing.T;

  3. 所有"超大"或"恶意"输入都带上明确的硬上限(通常 64MiB),避免本机自爆;

  4. 不引入第三方依赖;纯标准库。

与 testkit/fuzz 的关系:

  • testkit/fuzz/seed.go 提供 fuzz 用的 corpus(变异输入);
  • fixtures 提供"非 fuzz 的固定输入"(happy path / known-bad);
  • 部分小型 corpus 在两者之间共享时,由 fixtures 暴露 const 切片,fuzz 引用。

Package fixtures —— gzip_bomb.go

构造"gzip bomb"字节:压缩前后比例巨大,常用于 client 解压上限的对抗测试。

安全约束:

  • 解压后的最大尺寸由调用方传入;
  • 内部硬限 64 MiB,避免单元测试机器自爆;
  • 输入字节总是全 0(最高压缩比),保证一字节级别的 ratio 控制。

Package fixtures —— headers.go

预制 HTTP Header 集合,覆盖:

  • 通用 happy path;
  • 含敏感字段(用于跨域 redirect 时是否被 client 正确丢弃的断言);
  • 超大 header(验证 client 是否在编码侧合理截断或拒绝);
  • 含 ASCII / 非 ASCII 混合(验证 sanitize 路径)。

Package fixtures —— json.go

预制 JSON payload,覆盖三类场景:

  • 合法 happy path:标准结构体序列化结果;
  • 边界 / 极端:嵌套深、字段数多、大字符串;
  • 不合法:常见的 JSON 解码错误样本,client 应当返回 err 而不是 panic。

这些 payload 被 fuzz / unit / integration 共享。

Package fixtures —— multipart.go

在 helper.TempTree 上构造 multipart 测试用文件树。

用法:

tree := helper.NewTempTree(t)
mp := fixtures.BuildMultipartTree(t, tree, fixtures.MultipartSpec{
    SmallTextFiles: 3,
    BinaryFile:     1024 * 1024,    // 1 MiB
    EmptyFile:      true,
})
// mp.SmallTexts / mp.Binary / mp.Empty 给出绝对路径,可直接传给
// rest.Request 的 multipart 上传 API。

Package fixtures —— redirect_loop.go

预制 redirect chain 输入,供 integration / security 测试反复使用。

不依赖 server:只暴露"我希望 server 返回的 Location 头序列", 由测试侧把它喂给 testkit/server.RedirectChainHandler 或自定义 handler。

Package fixtures —— streams.go

io.Reader / io.ReadCloser 装饰器,模拟各种"非 happy path"读取行为:

  • SlowReader:每次 Read 前 sleep 一段时间(模拟慢拨号 / 慢响应);
  • EOFAfterReader:读够 N 字节后立即 EOF(模拟"声明的长度比实际短");
  • ErrAfterReader:读够 N 字节后返回指定 err(模拟"读到一半网络断开");
  • FlakyReader:按概率间歇性返回错误(模拟不稳定网络);
  • LimitedReadCloser:io.LimitReader 的 ReadCloser 版本,附带 Closed 标志。

设计:

  • 严格保持 io.Reader 协议——EOF 之后绝不再返回字节;
  • 不让 Read 永远阻塞(SlowReader 仍然有 deadline 退化路径);
  • 所有装饰器都暴露统一的 Closed() / Reset() 等观察接口。

Index

Constants

View Source
const MaxGzipBombUncompressed = 64 * 1024 * 1024

MaxGzipBombUncompressed 是单次构造允许的最大解压尺寸(字节)。

比这更大的请求会被钳到本值。

Variables

This section is empty.

Functions

func CommonHeaders

func CommonHeaders() http.Header

CommonHeaders 返回一组合理 happy-path 请求头。

func DeeplyNestedJSON

func DeeplyNestedJSON(depth int) []byte

DeeplyNestedJSON 返回一个深度为 depth 的嵌套 object JSON。

用于触发解析器栈深度路径;depth 上限 4096(再大的栈深度通常会让 Go 解析器自身报错)。

func GzipBomb

func GzipBomb(uncompressedSize int) []byte

GzipBomb 返回一个 gzip 流,解压后大约 uncompressedSize 字节。

实现:循环写 chunk(4 KiB 全 0)直到达到目标大小,再 Close。

输出尺寸非常小(几 KB 即可解压出几十 MB 的全 0),适合"测试 client 是否 在 maxInMemoryBody 处截断、是否在流式路径不无脑读完"的场景。

func GzipBombLarge

func GzipBombLarge() []byte

GzipBombLarge 返回 32 MiB 解压尺寸的 bomb(默认上限以下)。

注意:此函数返回的字节流被 client 完整解压时会占用 ~32 MiB 内存; 仅在 RequireHeavy/RequireExtreme 路径上使用。

func GzipBombMedium

func GzipBombMedium() []byte

GzipBombMedium 返回 8 MiB 解压尺寸的 bomb。

func GzipBombSmall

func GzipBombSmall() []byte

GzipBombSmall 返回一份"经典示意"的 gzip bomb:约 1 MiB 解压尺寸。

适合作为 unit/security 测试的默认 bomb,不需要每次调用 GzipBomb(...)。

func HugeJSONPayload

func HugeJSONPayload(size int) []byte

HugeJSONPayload 返回一份 size 字节左右的 JSON payload;用于流式解码或截断测试。

size <= 0 时返回空切片;size > 64 MiB 时硬限到 64 MiB(防止本机内存爆炸)。

结构形如:

{"items":[{"i":0,"msg":"line-0"},{"i":1,"msg":"line-1"},...]}

func LargeUserPayload

func LargeUserPayload() []byte

LargeUserPayload 返回一份字段较多的 JSON 字节,bio 是 2 KiB 文本。

func MalformedJSONSamples

func MalformedJSONSamples() [][]byte

MalformedJSONSamples 返回一组"明显不合法"的 JSON 字节切片。

client 在收到这些响应时必须返回 decoder error,而不是 panic / 阻塞。

func MaliciousHeaders

func MaliciousHeaders() http.Header

MaliciousHeaders 返回一组"明显违规"的请求头:含 CRLF 注入、控制字符等。

client 应当在写入 wire 前拒绝这些值(go 1.7+ 的 net/http 会做 sanitize), 测试用于断言"客户端代码不被这些 header 触发崩溃"。

func MaliciousLocations

func MaliciousLocations() []string

MaliciousLocations 返回一组"client 必须拒绝"的 Location 值。

client 应当在 Follow-Redirect 路径上对它们返回 err,而不是真的拨号过去。

func MergeHeaders

func MergeHeaders(hs ...http.Header) http.Header

MergeHeaders 把多个 http.Header 合并到一个新的 Header;后者覆盖前者。

仅供测试侧便捷使用——生产路径不应这样使用。

func OversizedHeaders

func OversizedHeaders(valueSize int) http.Header

OversizedHeaders 返回一组单值极长的 headers,用于压力测试。

单 header value 长度由参数 valueSize 控制,硬上限 1 MiB。

func SensitiveHeaders

func SensitiveHeaders() http.Header

SensitiveHeaders 返回一组含敏感字段的请求头。

用于:

  • 跨域 redirect 时验证 client 是否正确丢弃 Authorization / Cookie;
  • SSRF 防护是否正确不携带凭据。

func SmallUserPayload

func SmallUserPayload() []byte

SmallUserPayload 返回一份标准 SmallUser 的 JSON 字节。

func SmallUserPayloadString

func SmallUserPayloadString() string

SmallUserPayloadString 是 SmallUserPayload 的字符串版本。

Types

type EOFAfterReader

type EOFAfterReader struct {
	// contains filtered or unexported fields
}

EOFAfterReader 是一个"前 N 字节透明转发,之后立刻 EOF"的 reader。

func NewEOFAfter

func NewEOFAfter(src io.Reader, n int64) *EOFAfterReader

NewEOFAfter 在前 n 字节透明转发 src 之后立刻返回 io.EOF。

当 src 自身在 n 字节前就已 EOF,EOFAfterReader 也会传递该 EOF。

func (*EOFAfterReader) Read

func (r *EOFAfterReader) Read(p []byte) (int, error)

Read 实现 io.Reader。

type ErrAfterReader

type ErrAfterReader struct {
	// contains filtered or unexported fields
}

ErrAfterReader 与 EOFAfterReader 类似,但 N 字节后返回的是调用方指定的 err。

func NewErrAfter

func NewErrAfter(src io.Reader, n int64, finalErr error) *ErrAfterReader

NewErrAfter 在前 n 字节透明转发后返回 finalErr。

finalErr 为 nil 时使用 io.ErrUnexpectedEOF。

func (*ErrAfterReader) Read

func (r *ErrAfterReader) Read(p []byte) (int, error)

Read 实现 io.Reader。

type FlakyReader

type FlakyReader struct {
	// contains filtered or unexported fields
}

FlakyReader 按概率间歇性返回 err,模拟不稳定网络。

func NewFlakyReader

func NewFlakyReader(src io.Reader, failProb float64, failErr error, seed uint64) *FlakyReader

NewFlakyReader 装饰 src,每次 Read 有 failProb 概率返回 failErr。

failProb 钳到 [0, 1];failErr 为 nil 时使用 io.ErrUnexpectedEOF。

seed 控制随机源;0 表示使用一个固定常量(确定可复现)。

func (*FlakyReader) Calls

func (r *FlakyReader) Calls() int64

Calls 返回累计 Read 次数。

func (*FlakyReader) Hits

func (r *FlakyReader) Hits() int64

Hits 返回累计返回 err 的次数。

func (*FlakyReader) Read

func (r *FlakyReader) Read(p []byte) (int, error)

Read 实现 io.Reader。

type LargeUser

type LargeUser struct {
	ID       int               `json:"id"`
	Name     string            `json:"name"`
	Bio      string            `json:"bio"`
	Tags     []string          `json:"tags"`
	Metadata map[string]string `json:"metadata"`
}

LargeUser 是带很多字段、大字符串的 JSON 结构体,用于"中等 body"路径。

type LimitedReadCloser

type LimitedReadCloser struct {
	io.Reader
	// contains filtered or unexported fields
}

LimitedReadCloser 是 io.LimitReader 的 ReadCloser 版本,并带 Closed() 观察。

func NewLimitedReadCloser

func NewLimitedReadCloser(r io.Reader, n int64) *LimitedReadCloser

NewLimitedReadCloser 限制最多读取 n 字节后返回 EOF;Close 时调用 closer.Close(若有)。

func (*LimitedReadCloser) Close

func (l *LimitedReadCloser) Close() error

Close 实现 io.Closer,幂等。

func (*LimitedReadCloser) Closed

func (l *LimitedReadCloser) Closed() bool

Closed 返回是否已被 Close。

type MultipartFiles

type MultipartFiles struct {
	// SmallTexts 小文本文件绝对路径列表,长度 = MultipartSpec.SmallTextFiles。
	SmallTexts []string
	// Binary 二进制文件路径,未生成时为空字符串。
	Binary string
	// Empty 0 字节文件路径,未生成时为空字符串。
	Empty string
	// UnicodeName 非 ASCII 文件名的文本文件路径,未生成时为空字符串。
	UnicodeName string
}

MultipartFiles 是 BuildMultipartTree 的返回结果。

func BuildMultipartTree

func BuildMultipartTree(t testing.TB, tree *helper.TempTree, spec MultipartSpec) MultipartFiles

BuildMultipartTree 在 tree 下构造给定 spec 的文件集合。

失败一律 t.Fatalf;调用方拿到的 MultipartFiles 总是"已成功落盘"的。

type MultipartSpec

type MultipartSpec struct {
	// SmallTextFiles 表示要生成多少个小文本文件(每个 ~256 字节,递增内容)。
	SmallTextFiles int
	// BinaryFile 表示是否生成一个伪随机二进制文件,及其大小(字节)。0 表示不生成。
	BinaryFile int
	// EmptyFile=true 时生成一个 0 字节文件。
	EmptyFile bool
	// UnicodeFilename=true 时额外生成一个 UTF-8 文件名("文件_中文.txt"),
	// 用于验证 multipart 编码中 filename 字段的处理。
	UnicodeFilename bool
}

MultipartSpec 描述要构造的文件集合。

type RedirectChain

type RedirectChain struct {
	// Locations 是按顺序返回的 Location 头列表。
	// 最后一项为空字符串时,表示链路在该步骤结束(200 响应)。
	Locations []string

	// Description 用于失败诊断。
	Description string
}

RedirectChain 描述一组预制 redirect 链路。

func HostJumpRedirectChain

func HostJumpRedirectChain(otherHostURL string) RedirectChain

HostJumpRedirectChain 返回一个"换 host"的链路,用于测试 cross-origin 重定向时 的 cookie / referrer 行为。

func LinearRedirectChain

func LinearRedirectChain(n int) RedirectChain

LinearRedirectChain 返回一个 N 步 linear redirect chain。

/redirect/N → /redirect/N-1 → ... → /redirect/0 (200 done)

func ProtocolJumpRedirectChain

func ProtocolJumpRedirectChain(httpsBase string) RedirectChain

ProtocolJumpRedirectChain 返回一个"http → https"的协议跳跃链路。

用于测试 client 在跨协议 redirect 时,是否正确处理 Authorization / Cookie 等敏感头。

func SelfLoopRedirectChain

func SelfLoopRedirectChain() RedirectChain

SelfLoopRedirectChain 返回一个会"指向自己"的 redirect 链路。

用于测试 client 是否在死循环到达 max redirects 后正确报错。

type SlowReader

type SlowReader struct {
	// contains filtered or unexported fields
}

SlowReader 在每次 Read 前 sleep 一段时间。

用于:

  • 验证 ReadTimeout / 整体 timeout 的客户端行为;
  • 触发 ResponseHeaderTimeout / streaming backpressure。

func NewSlowReader

func NewSlowReader(src io.Reader, delay time.Duration) *SlowReader

NewSlowReader 装饰 src,每次 Read 前 sleep delay。

delay <= 0 时退化为透明转发。

func (*SlowReader) Close

func (r *SlowReader) Close() error

Close 实现 io.Closer。后续 Read 立即返回 ErrClosedPipe。

func (*SlowReader) Read

func (r *SlowReader) Read(p []byte) (int, error)

Read 实现 io.Reader。

type SmallUser

type SmallUser struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email,omitempty"`
}

SmallUser 是测试用的小型结构体;用于快速 happy-path JSON 编解码。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL