Files
seqlog/index_test.go
bourdon 810664eb12 重构:优化记录格式并修复核心功能
- 修改记录存储格式为 [4B len][8B offset][4B CRC][16B UUID][data]
- 修复 TopicProcessor 中 WaitGroup 使用错误导致 handler 不执行的问题
- 修复写入保护逻辑,避免 dirtyOffset=-1 时误判为写入中
- 添加统计信息定期持久化功能
- 改进 UTF-8 字符截断处理,防止 CJK 字符乱码
- 优化 Web UI:显示人类可读的文件大小,支持点击外部关闭弹窗
- 重构示例代码,添加 webui 和 webui_integration 示例

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-04 17:54:49 +08:00

294 lines
7.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package seqlog
import (
"os"
"path/filepath"
"testing"
)
// TestIndexBasicOperations 测试索引的基本操作
func TestIndexBasicOperations(t *testing.T) {
// 创建临时目录
tmpDir := t.TempDir()
logPath := filepath.Join(tmpDir, "test.log")
// 1. 创建索引
index, err := NewRecordIndex(logPath)
if err != nil {
t.Fatalf("创建索引失败: %v", err)
}
defer index.Close()
// 2. 创建写入器(使用共享索引)
writer, err := NewLogWriter(logPath, index)
if err != nil {
t.Fatalf("创建写入器失败: %v", err)
}
// 2. 写入测试数据
testData := []string{
"第一条日志",
"第二条日志",
"第三条日志",
"第四条日志",
"第五条日志",
}
offsets := make([]int64, 0, len(testData))
for _, data := range testData {
offset, err := writer.Append([]byte(data))
if err != nil {
t.Fatalf("写入失败: %v", err)
}
offsets = append(offsets, offset)
t.Logf("写入记录: offset=%d, data=%s", offset, data)
}
// 关闭写入器
if err := writer.Close(); err != nil {
t.Fatalf("关闭写入器失败: %v", err)
}
// 3. 验证索引文件存在
indexPath := logPath + ".idx"
if _, err := os.Stat(indexPath); os.IsNotExist(err) {
t.Fatalf("索引文件不存在: %s", indexPath)
}
t.Logf("索引文件已创建: %s", indexPath)
// 验证记录数量
if index.Count() != len(testData) {
t.Errorf("记录数量不匹配: got %d, want %d", index.Count(), len(testData))
}
// 验证每条记录的偏移量
for i, expectedOffset := range offsets {
actualOffset, err := index.GetOffset(i)
if err != nil {
t.Errorf("获取第 %d 条记录的偏移失败: %v", i, err)
continue
}
if actualOffset != expectedOffset {
t.Errorf("第 %d 条记录偏移量不匹配: got %d, want %d", i, actualOffset, expectedOffset)
}
}
// 验证二分查找
for i, offset := range offsets {
idx := index.FindIndex(offset)
if idx != i {
t.Errorf("FindIndex(%d) = %d, want %d", offset, idx, i)
}
}
t.Logf("索引基本操作测试通过")
}
// TestIndexRebuild 测试索引重建功能
func TestIndexRebuild(t *testing.T) {
tmpDir := t.TempDir()
logPath := filepath.Join(tmpDir, "test.log")
// 1. 创建索引和写入器,写入数据
index1, err := NewRecordIndex(logPath)
if err != nil {
t.Fatalf("创建索引失败: %v", err)
}
writer, err := NewLogWriter(logPath, index1)
if err != nil {
t.Fatalf("创建写入器失败: %v", err)
}
offsets := make([]int64, 0, 3)
for i := 0; i < 3; i++ {
offset, err := writer.Append([]byte("测试数据"))
if err != nil {
t.Fatalf("写入失败: %v", err)
}
offsets = append(offsets, offset)
}
writer.Close()
index1.Close()
// 2. 重新加载索引(测试索引加载功能)
index, err := NewRecordIndex(logPath)
if err != nil {
t.Fatalf("创建索引失败: %v", err)
}
defer index.Close()
// 验证重建的索引
if index.Count() != 3 {
t.Errorf("重建的索引记录数不正确: got %d, want 3", index.Count())
}
for i, expectedOffset := range offsets {
actualOffset, err := index.GetOffset(i)
if err != nil {
t.Errorf("获取偏移失败: %v", err)
continue
}
if actualOffset != expectedOffset {
t.Errorf("偏移量不匹配: got %d, want %d", actualOffset, expectedOffset)
}
}
t.Logf("索引重建测试通过")
}
// TestQueryWithIndex 测试带索引的查询
func TestQueryWithIndex(t *testing.T) {
tmpDir := t.TempDir()
logPath := filepath.Join(tmpDir, "test.log")
// 创建索引
index, err := NewRecordIndex(logPath)
if err != nil {
t.Fatalf("创建索引失败: %v", err)
}
defer index.Close()
// 创建写入器(使用共享索引)
writer, err := NewLogWriter(logPath, index)
if err != nil {
t.Fatalf("创建写入器失败: %v", err)
}
// 写入 10 条记录
for range 10 {
_, err := writer.Append([]byte("测试数据"))
if err != nil {
t.Fatalf("写入失败: %v", err)
}
}
defer writer.Close()
// 2. 创建查询器(使用共享索引)
query, err := NewRecordQuery(logPath, index, writer)
if err != nil {
t.Fatalf("创建查询器失败: %v", err)
}
defer query.Close()
// 4. 测试获取记录总数
count, err := query.GetRecordCount()
if err != nil {
t.Fatalf("获取记录总数失败: %v", err)
}
if count != 10 {
t.Errorf("记录总数不正确: got %d, want 10", count)
}
// 5. 测试向后查询(查询更早的记录)
// 从第 5 条记录向后查询 3 条(查询索引 4, 3, 2
results, err := query.QueryNewest(4, 3)
if err != nil {
t.Fatalf("向后查询失败: %v", err)
}
if len(results) != 3 {
t.Errorf("查询结果数量不正确: got %d, want 3", len(results))
}
t.Logf("带索引的查询测试通过")
}
// TestIndexAppend 测试索引追加功能
func TestIndexAppend(t *testing.T) {
tmpDir := t.TempDir()
logPath := filepath.Join(tmpDir, "test.log")
// 1. 创建索引和写入器,写入初始数据
index1, err := NewRecordIndex(logPath)
if err != nil {
t.Fatalf("创建索引失败: %v", err)
}
writer, err := NewLogWriter(logPath, index1)
if err != nil {
t.Fatalf("创建写入器失败: %v", err)
}
for range 5 {
writer.Append([]byte("初始数据"))
}
writer.Close()
index1.Close()
// 2. 重新打开索引和写入器,追加新数据
index2, err := NewRecordIndex(logPath)
if err != nil {
t.Fatalf("重新打开索引失败: %v", err)
}
writer, err = NewLogWriter(logPath, index2)
if err != nil {
t.Fatalf("重新打开写入器失败: %v", err)
}
for range 3 {
writer.Append([]byte("追加数据"))
}
writer.Close()
index2.Close()
// 3. 验证索引
index, err := NewRecordIndex(logPath)
if err != nil {
t.Fatalf("加载索引失败: %v", err)
}
defer index.Close()
if index.Count() != 8 {
t.Errorf("索引记录数不正确: got %d, want 8", index.Count())
}
t.Logf("索引追加测试通过")
}
// TestIndexHeader 测试索引头部信息
func TestIndexHeader(t *testing.T) {
tmpDir := t.TempDir()
logPath := filepath.Join(tmpDir, "test.log")
// 创建索引
index, err := NewRecordIndex(logPath)
if err != nil {
t.Fatalf("创建索引失败: %v", err)
}
defer index.Close()
// 创建写入器(使用共享索引)
writer, err := NewLogWriter(logPath, index)
if err != nil {
t.Fatalf("创建写入器失败: %v", err)
}
lastOffset, _ := writer.Append([]byte("第一条"))
writer.Append([]byte("第二条"))
lastOffset, _ = writer.Append([]byte("第三条"))
writer.Close()
// 验证魔数和版本
if index.magic != IndexMagic {
t.Errorf("Magic 不正确: got 0x%X, want 0x%X", index.magic, IndexMagic)
}
if index.version != IndexVersion {
t.Errorf("Version 不正确: got %d, want %d", index.version, IndexVersion)
}
// 验证记录总数(从内存索引计算)
if index.Count() != 3 {
t.Errorf("Count 不正确: got %d, want 3", index.Count())
}
// 验证最后一条记录偏移(从内存索引获取)
if index.LastOffset() != lastOffset {
t.Errorf("LastOffset 不正确: got %d, want %d", index.LastOffset(), lastOffset)
}
t.Logf("索引头部信息测试通过")
}