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("索引头部信息测试通过") }