gotidb/pkg/engine/engine_test.go

284 lines
6.5 KiB
Go
Raw 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 engine
import (
"context"
"testing"
"time"
)
// mockEngine 是一个用于测试的模拟引擎实现
type mockEngine struct {
points []DataPoint
stats EngineStats
opened bool
closed bool
}
func (m *mockEngine) Open() error {
m.opened = true
return nil
}
func (m *mockEngine) Close() error {
m.closed = true
return nil
}
func (m *mockEngine) Write(ctx context.Context, points []DataPoint) error {
m.points = append(m.points, points...)
m.stats.PointsCount += int64(len(points))
m.stats.LastWriteTime = time.Now()
return nil
}
func (m *mockEngine) Query(ctx context.Context, query Query) (QueryResult, error) {
var result QueryResult
if len(m.points) == 0 {
return result, nil
}
// 根据查询类型返回不同的结果
switch query.Type {
case QueryTypeLatest:
// 返回最新的数据点
latest := m.points[len(m.points)-1]
result = append(result, SeriesResult{
SeriesID: latest.GetSeriesID(),
Points: []DataPoint{latest},
})
case QueryTypeRaw:
// 返回所有匹配的数据点
var matchedPoints []DataPoint
for _, point := range m.points {
if point.Timestamp >= query.StartTime && point.Timestamp <= query.EndTime {
// 检查标签是否匹配
if lmatchTags(point.Labels, query.Tags) {
matchedPoints = append(matchedPoints, point)
}
}
}
if len(matchedPoints) > 0 {
result = append(result, SeriesResult{
SeriesID: matchedPoints[0].GetSeriesID(),
Points: matchedPoints,
})
}
}
return result, nil
}
func (m *mockEngine) Compact() error {
m.stats.CompactionCount++
m.stats.LastCompaction = time.Now()
return nil
}
func (m *mockEngine) Cleanup() error {
return nil
}
func (m *mockEngine) Stats() EngineStats {
return m.stats
}
// matchTags 检查数据点的标签是否匹配查询标签
func lmatchTags(pointTags, queryTags map[string]string) bool {
for k, v := range queryTags {
if pointTags[k] != v {
return false
}
}
return true
}
func TestDataPoint_GetSeriesID(t *testing.T) {
tests := []struct {
name string
point DataPoint
labels map[string]string
}{
{
name: "empty labels",
point: DataPoint{
Labels: map[string]string{},
},
},
{
name: "single label",
point: DataPoint{
Labels: map[string]string{"host": "server1"},
},
},
{
name: "multiple labels",
point: DataPoint{
Labels: map[string]string{
"host": "server1",
"region": "us-west",
"service": "api",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
id := tt.point.GetSeriesID()
if id == "" {
t.Error("GetSeriesID() returned empty string")
}
// 相同标签应该生成相同的ID
id2 := tt.point.GetSeriesID()
if id != id2 {
t.Errorf("GetSeriesID() not consistent: got %v and %v", id, id2)
}
// 创建一个新的数据点,使用相同的标签
point2 := DataPoint{Labels: tt.point.Labels}
id3 := point2.GetSeriesID()
if id != id3 {
t.Errorf("GetSeriesID() not consistent across instances: got %v and %v", id, id3)
}
})
}
}
func TestWriteBuffer(t *testing.T) {
// 创建一个模拟的WriteBufferHandler
handler := &mockBufferHandler{
toflush: []DataPoint{},
points: make([]DataPoint, 0),
}
// 创建WriteBuffer设置缓冲区大小为2
buffer := NewWriteBuffer(handler, 2)
// 测试写入单个数据点
point1 := DataPoint{
Timestamp: time.Now().UnixNano(),
Value: 1.0,
Labels: map[string]string{"test": "1"},
}
if err := buffer.Write(point1); err != nil {
t.Errorf("Write() error = %v", err)
}
if len(handler.points) != 0 {
t.Errorf("Buffer flushed too early, got %d points, want 0", len(handler.points))
}
// 测试写入第二个数据点(应该触发刷新)
point2 := DataPoint{
Timestamp: time.Now().UnixNano(),
Value: 2.0,
Labels: map[string]string{"test": "2"},
}
if err := buffer.Write(point2); err != nil {
t.Errorf("Write() error = %v", err)
}
if len(handler.toflush) != 2 {
t.Errorf("Buffer not flushed when full, got %d points, want 2", len(handler.points))
}
// 测试手动刷新
point3 := DataPoint{
Timestamp: time.Now().UnixNano(),
Value: 3.0,
Labels: map[string]string{"test": "3"},
}
if err := buffer.Write(point3); err != nil {
t.Errorf("Write() error = %v", err)
}
if err := buffer.Flush(); err != nil {
t.Errorf("Flush() error = %v", err)
}
if len(handler.toflush) != 3 {
t.Errorf("Manual flush failed, got %d points, want 3", len(handler.points))
}
}
// mockBufferHandler 是一个用于测试的WriteBufferHandler实现
type mockBufferHandler struct {
toflush []DataPoint
points []DataPoint
}
func (h *mockBufferHandler) WriteToBuffer(point DataPoint) error {
h.points = append(h.points, point)
return nil
}
func (h *mockBufferHandler) FlushBuffer() error {
h.toflush = append(h.toflush, h.points...)
h.points = []DataPoint{}
return nil
}
func (h *mockBufferHandler) ValidatePoint(point DataPoint) error {
return nil
}
func TestEngineFactory(t *testing.T) {
// 注册模拟引擎
RegisterMemoryEngine(func(config *MemoryEngineConfig) (Engine, error) {
return &mockEngine{}, nil
})
RegisterBoltEngine(func(config *BoltEngineConfig) (Engine, error) {
return &mockEngine{}, nil
})
tests := []struct {
name string
config EngineConfig
wantErr bool
}{
{
name: "memory engine",
config: NewMemoryEngineConfig(),
wantErr: false,
},
{
name: "bolt engine",
config: NewBoltEngineConfig("test.db"),
wantErr: false,
},
{
name: "invalid config type",
config: &struct{ EngineConfig }{},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
engine, err := NewEngine(tt.config)
if (err != nil) != tt.wantErr {
t.Errorf("NewEngine() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && engine == nil {
t.Error("NewEngine() returned nil engine")
}
})
}
}
func TestEngineConfig(t *testing.T) {
// 测试内存引擎配置
memConfig := NewMemoryEngineConfig()
if memConfig.Type() != "memory" {
t.Errorf("MemoryEngineConfig.Type() = %v, want memory", memConfig.Type())
}
if memConfig.MaxHistoryValues != 30 {
t.Errorf("MemoryEngineConfig.MaxHistoryValues = %v, want 30", memConfig.MaxHistoryValues)
}
// 测试Bolt引擎配置
boltConfig := NewBoltEngineConfig("test.db")
if boltConfig.Type() != "bolt" {
t.Errorf("BoltEngineConfig.Type() = %v, want bolt", boltConfig.Type())
}
if boltConfig.Path != "test.db" {
t.Errorf("BoltEngineConfig.Path = %v, want test.db", boltConfig.Path)
}
}