Files
es-demo/operations/index/ism_unit_test.go
mouseleee fc14798af5 feat: 增加ism管理接口
test: 索引模板和ism的单元测试和集成测试
2025-11-16 23:00:31 +08:00

504 lines
11 KiB
Go

package index
import (
"context"
"encoding/json"
"strings"
"testing"
"es-demo/client"
"es-demo/config"
)
// TestISMPolicyManager_PutPolicy_Validation tests policy validation
func TestISMPolicyManager_PutPolicy_Validation(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test in short mode")
}
if err := config.Load(".env"); err != nil {
t.Logf("warning: failed to load .env file: %v", err)
}
config.Init()
cfg := &client.Config{
Endpoint: config.Endpoint,
Region: config.Region,
AccessKey: config.AccessKey,
SecretKey: config.SecretKey,
}
c, err := client.NewClient(cfg)
if err != nil {
t.Fatalf("failed to create client: %v", err)
}
pm := NewISMPolicyManager(c)
ctx := context.Background()
tests := []struct {
name string
policyName string
policy *Policy
wantErr bool
}{
{
name: "empty policy name",
policyName: "",
policy: &Policy{
DefaultState: "hot",
States: []State{{Name: "hot"}},
},
wantErr: true,
},
{
name: "invalid policy - nil",
policyName: "test",
policy: nil,
wantErr: true,
},
{
name: "invalid policy - no states",
policyName: "test",
policy: &Policy{
Description: "Test",
},
wantErr: true,
},
{
name: "invalid policy - missing default_state",
policyName: "test",
policy: &Policy{
States: []State{{Name: "hot"}},
},
wantErr: true,
},
{
name: "invalid policy - default_state not in states",
policyName: "test",
policy: &Policy{
DefaultState: "cold",
States: []State{{Name: "hot"}},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := pm.PutPolicy(ctx, tt.policyName, tt.policy)
if (err != nil) != tt.wantErr {
t.Errorf("PutPolicy() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
// TestISMPolicyManager_GetPolicy_NotFound tests GetPolicy when policy doesn't exist
func TestISMPolicyManager_GetPolicy_NotFound(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test in short mode")
}
if err := config.Load(".env"); err != nil {
t.Logf("warning: failed to load .env file: %v", err)
}
config.Init()
cfg := &client.Config{
Endpoint: config.Endpoint,
Region: config.Region,
AccessKey: config.AccessKey,
SecretKey: config.SecretKey,
}
c, err := client.NewClient(cfg)
if err != nil {
t.Fatalf("failed to create client: %v", err)
}
pm := NewISMPolicyManager(c)
ctx := context.Background()
_, err = pm.GetPolicy(ctx, "non-existent-policy-12345")
if err == nil {
t.Error("GetPolicy() should return error for non-existent policy")
}
}
// TestISMPolicyManager_DeletePolicy_NotFound tests DeletePolicy when policy doesn't exist
func TestISMPolicyManager_DeletePolicy_NotFound(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test in short mode")
}
if err := config.Load(".env"); err != nil {
t.Logf("warning: failed to load .env file: %v", err)
}
config.Init()
cfg := &client.Config{
Endpoint: config.Endpoint,
Region: config.Region,
AccessKey: config.AccessKey,
SecretKey: config.SecretKey,
}
c, err := client.NewClient(cfg)
if err != nil {
t.Fatalf("failed to create client: %v", err)
}
pm := NewISMPolicyManager(c)
ctx := context.Background()
err = pm.DeletePolicy(ctx, "non-existent-policy-12345")
if err == nil {
t.Error("DeletePolicy() should return error for non-existent policy")
}
}
// TestISMPolicyManager_ListPolicies_EmptyCheck tests ListPolicies returns valid map
func TestISMPolicyManager_ListPolicies_EmptyCheck(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test in short mode")
}
if err := config.Load(".env"); err != nil {
t.Logf("warning: failed to load .env file: %v", err)
}
config.Init()
cfg := &client.Config{
Endpoint: config.Endpoint,
Region: config.Region,
AccessKey: config.AccessKey,
SecretKey: config.SecretKey,
}
c, err := client.NewClient(cfg)
if err != nil {
t.Fatalf("failed to create client: %v", err)
}
pm := NewISMPolicyManager(c)
ctx := context.Background()
policies, err := pm.ListPolicies(ctx)
if err != nil {
t.Fatalf("ListPolicies() error = %v", err)
}
if policies == nil {
t.Error("ListPolicies() should not return nil map")
}
}
// TestPolicy_ValidateEdgeCases tests additional edge cases for policy validation
func TestPolicy_ValidateEdgeCases(t *testing.T) {
tests := []struct {
name string
policy *Policy
wantErr bool
}{
{
name: "nil policy",
policy: nil,
wantErr: true,
},
{
name: "empty states and empty phases",
policy: &Policy{
Description: "Empty policy",
},
wantErr: true,
},
{
name: "both ISM states and ILM phases",
policy: &Policy{
DefaultState: "hot",
States: []State{{Name: "hot"}},
Phases: map[string]Phase{
"hot": {MinAge: "0ms"},
},
},
wantErr: true,
},
{
name: "valid ISM policy with transitions",
policy: &Policy{
Description: "Test",
DefaultState: "hot",
States: []State{
{
Name: "hot",
Transitions: []Transition{
{
StateName: "warm",
Conditions: &Conditions{
MinIndexAge: "1d",
},
},
},
},
{
Name: "warm",
},
},
},
wantErr: false,
},
{
name: "valid ISM policy with actions",
policy: &Policy{
DefaultState: "hot",
States: []State{
{
Name: "hot",
Actions: []Action{
{
Config: map[string]interface{}{
"rollover": map[string]interface{}{
"min_index_age": "1d",
},
},
},
},
},
},
},
wantErr: false,
},
{
name: "valid ILM policy with multiple phases",
policy: &Policy{
Description: "ILM policy",
Phases: map[string]Phase{
"hot": {
MinAge: "0ms",
Actions: map[string]interface{}{
"rollover": map[string]interface{}{
"max_age": "30d",
},
},
},
"delete": {
MinAge: "90d",
Actions: map[string]interface{}{
"delete": map[string]interface{}{},
},
},
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.policy.Validate()
if (err != nil) != tt.wantErr {
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
// TestPolicy_JSONMarshaling tests policy JSON marshaling
func TestPolicy_JSONMarshaling(t *testing.T) {
policy := &Policy{
Description: "Test policy",
DefaultState: "hot",
States: []State{
{
Name: "hot",
Actions: []Action{
{
Config: map[string]interface{}{
"rollover": map[string]interface{}{
"min_index_age": "1d",
},
},
},
},
},
},
ISMTemplate: []ISMTemplate{
{
IndexPatterns: []string{"test-*"},
Priority: 100,
},
},
}
// Marshal
data, err := json.Marshal(policy)
if err != nil {
t.Fatalf("json.Marshal() error = %v", err)
}
// Unmarshal
var decoded Policy
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("json.Unmarshal() error = %v", err)
}
// Verify
if decoded.Description != policy.Description {
t.Errorf("Description = %v, want %v", decoded.Description, policy.Description)
}
if decoded.DefaultState != policy.DefaultState {
t.Errorf("DefaultState = %v, want %v", decoded.DefaultState, policy.DefaultState)
}
if len(decoded.States) != len(policy.States) {
t.Errorf("States length = %d, want %d", len(decoded.States), len(policy.States))
}
if len(decoded.ISMTemplate) != len(policy.ISMTemplate) {
t.Errorf("ISMTemplate length = %d, want %d", len(decoded.ISMTemplate), len(policy.ISMTemplate))
}
}
// TestPolicy_MarshalError tests error handling in policy marshaling
func TestPolicy_MarshalError(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test in short mode")
}
if err := config.Load(".env"); err != nil {
t.Logf("warning: failed to load .env file: %v", err)
}
config.Init()
cfg := &client.Config{
Endpoint: config.Endpoint,
Region: config.Region,
AccessKey: config.AccessKey,
SecretKey: config.SecretKey,
}
c, err := client.NewClient(cfg)
if err != nil {
t.Fatalf("failed to create client: %v", err)
}
pm := NewISMPolicyManager(c)
ctx := context.Background()
// 创建一个包含不可序列化数据的策略
policy := &Policy{
DefaultState: "hot",
States: []State{
{
Name: "hot",
Actions: []Action{
{
Config: map[string]interface{}{
"invalid": make(chan int), // channels 不能被 JSON 序列化
},
},
},
},
},
}
err = pm.PutPolicy(ctx, "test", policy)
if err == nil {
t.Error("PutPolicy() should return error for invalid JSON")
}
if !strings.Contains(err.Error(), "marshal") {
t.Errorf("Error should mention marshal, got: %v", err)
}
}
// TestState_Transitions tests state transition logic
func TestState_Transitions(t *testing.T) {
state := State{
Name: "hot",
Transitions: []Transition{
{
StateName: "warm",
Conditions: &Conditions{
MinIndexAge: "1d",
},
},
{
StateName: "delete",
Conditions: &Conditions{
MinIndexAge: "7d",
},
},
},
}
if len(state.Transitions) != 2 {
t.Errorf("Expected 2 transitions, got %d", len(state.Transitions))
}
if state.Transitions[0].StateName != "warm" {
t.Errorf("First transition state = %v, want warm", state.Transitions[0].StateName)
}
if state.Transitions[1].Conditions.MinIndexAge != "7d" {
t.Errorf("Second transition min_index_age = %v, want 7d", state.Transitions[1].Conditions.MinIndexAge)
}
}
// TestAction_Config tests action configuration
func TestAction_Config(t *testing.T) {
action := Action{
Config: map[string]interface{}{
"rollover": map[string]interface{}{
"min_index_age": "1d",
"min_doc_count": 1000000,
},
},
}
rollover, ok := action.Config["rollover"].(map[string]interface{})
if !ok {
t.Fatal("Failed to get rollover config")
}
if rollover["min_index_age"] != "1d" {
t.Errorf("min_index_age = %v, want 1d", rollover["min_index_age"])
}
if rollover["min_doc_count"] != 1000000 {
t.Errorf("min_doc_count = %v, want 1000000", rollover["min_doc_count"])
}
}
// TestISMTemplate_Patterns tests ISM template index patterns
func TestISMTemplate_Patterns(t *testing.T) {
template := ISMTemplate{
IndexPatterns: []string{"logs-*", "metrics-*", "traces-*"},
Priority: 100,
}
if len(template.IndexPatterns) != 3 {
t.Errorf("Expected 3 index patterns, got %d", len(template.IndexPatterns))
}
if template.Priority != 100 {
t.Errorf("Priority = %d, want 100", template.Priority)
}
// Test JSON marshaling
data, err := json.Marshal(template)
if err != nil {
t.Fatalf("json.Marshal() error = %v", err)
}
var decoded ISMTemplate
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("json.Unmarshal() error = %v", err)
}
if len(decoded.IndexPatterns) != len(template.IndexPatterns) {
t.Errorf("Decoded index patterns length = %d, want %d", len(decoded.IndexPatterns), len(template.IndexPatterns))
}
}