mysqlbinlog/parse_event_convert_payload_test.go
starainrt 0c9d9c6eae
feat(binlog): 增加 logical clock 元数据解析与统计支撑
- 暴露事务级 last_committed、sequence_number、transaction_length 和 commit timestamp
- 在 GTID event 转换时透传 logical clock 元数据
- 新增 ParseOptions、ParseProgress,支持上下文取消和解析进度回调
- 保留 TransactionPayloadEvent 展开后的 tablemap 与压缩类型信息
- 增加 TransactionSummary 辅助结构,便于上层统计事务结果、耗时、表分布和逻辑时钟
- 清理测试对外部大 binlog 样本的依赖,保证独立库测试可运行
- 修正跨平台测试与 GTID 输出格式断言,提升发版稳定性
2026-05-10 14:02:53 +08:00

122 lines
3.8 KiB
Go

package binlog
import (
"testing"
"github.com/starainrt/go-mysql/mysql"
"github.com/starainrt/go-mysql/replication"
)
func TestParseBinlogEvent_TableMapEvent(t *testing.T) {
ev := &replication.BinlogEvent{
Header: &replication.EventHeader{EventType: replication.TABLE_MAP_EVENT},
Event: &replication.TableMapEvent{
Schema: []byte("db1"),
Table: []byte("tb1"),
},
}
events := ParseBinlogEvent(ev)
if len(events) != 1 {
t.Fatalf("expected 1 event, got %d", len(events))
}
if events[0].Type != "tablemap" {
t.Fatalf("expected tablemap event type, got %q", events[0].Type)
}
if events[0].DB != "db1" || events[0].TB != "tb1" {
t.Fatalf("unexpected db/table: %s.%s", events[0].DB, events[0].TB)
}
}
func TestParseBinlogEvent_TransactionPayloadContainsTableMap(t *testing.T) {
table := &replication.TableMapEvent{
Schema: []byte("db2"),
Table: []byte("tb2"),
ColumnType: []byte{mysql.MYSQL_TYPE_LONG},
}
payload := &replication.TransactionPayloadEvent{
CompressionType: CompressionZSTD,
Events: []*replication.BinlogEvent{
{
Header: &replication.EventHeader{EventType: replication.TABLE_MAP_EVENT},
Event: table,
},
{
Header: &replication.EventHeader{EventType: replication.WRITE_ROWS_EVENTv2},
Event: &replication.RowsEvent{
Table: table,
Rows: [][]interface{}{{int32(1)}},
},
},
},
}
ev := &replication.BinlogEvent{
Header: &replication.EventHeader{EventType: replication.TRANSACTION_PAYLOAD_EVENT},
Event: payload,
}
events := ParseBinlogEvent(ev)
if len(events) != 2 {
t.Fatalf("expected 2 events from payload, got %d", len(events))
}
if events[0].Type != "tablemap" {
t.Fatalf("expected first payload event to be tablemap, got %q", events[0].Type)
}
if events[1].Type != "insert" {
t.Fatalf("expected second payload event to be insert, got %q", events[1].Type)
}
if events[0].CompressionType != "ZSTD" || events[1].CompressionType != "ZSTD" {
t.Fatalf("expected payload events to carry compression type, got %q/%q", events[0].CompressionType, events[1].CompressionType)
}
}
func TestParseBinlogEvent_NilInput(t *testing.T) {
if got := ParseBinlogEvent(nil); got != nil {
t.Fatalf("expected nil for nil event, got %#v", got)
}
if got := ParseBinlogEvent(&replication.BinlogEvent{}); got != nil {
t.Fatalf("expected nil for missing header, got %#v", got)
}
}
func TestParseBinlogEvent_GTIDMetadata(t *testing.T) {
ev := &replication.BinlogEvent{
Header: &replication.EventHeader{
EventType: replication.GTID_EVENT,
ServerID: 12,
Timestamp: 123456,
LogPos: 456,
EventSize: 64,
},
Event: &replication.GTIDEvent{
SID: []byte{0x74, 0xde, 0xc5, 0xa0, 0x3a, 0xc7, 0x11, 0xf0, 0xba, 0x0c, 0xfa, 0x16, 0x3e, 0xea, 0x29, 0x9f},
GNO: 42,
LastCommitted: 40,
SequenceNumber: 42,
TransactionLength: 2048,
ImmediateCommitTimestamp: 1700000000000001,
OriginalCommitTimestamp: 1700000000000000,
},
}
events := ParseBinlogEvent(ev)
if len(events) != 1 {
t.Fatalf("expected 1 event, got %d", len(events))
}
got := events[0]
if got.Type != "gtid" {
t.Fatalf("expected gtid event, got %q", got.Type)
}
if got.LastCommitted != 40 || got.SequenceNumber != 42 || got.TransactionLength != 2048 {
t.Fatalf("logical metadata mismatch: last=%d seq=%d len=%d", got.LastCommitted, got.SequenceNumber, got.TransactionLength)
}
if got.ImmediateCommitTimestamp != 1700000000000001 || got.OriginalCommitTimestamp != 1700000000000000 {
t.Fatalf("commit timestamps mismatch: immediate=%d original=%d", got.ImmediateCommitTimestamp, got.OriginalCommitTimestamp)
}
if got.ServerID != 12 || got.Timestamp != 123456 || got.LogPos != 456 || got.EventSize != 64 {
t.Fatalf("header metadata mismatch: %#v", got)
}
}