You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

257 lines
6.7 KiB

package plugin
import (
"reflect"
"testing"
"time"
"github.com/seaweedfs/seaweedfs/weed/pb/plugin_pb"
)
func TestConfigStoreDescriptorRoundTrip(t *testing.T) {
t.Parallel()
tempDir := t.TempDir()
store, err := NewConfigStore(tempDir)
if err != nil {
t.Fatalf("NewConfigStore: %v", err)
}
descriptor := &plugin_pb.JobTypeDescriptor{
JobType: "vacuum",
DisplayName: "Vacuum",
Description: "Vacuum volumes",
DescriptorVersion: 1,
}
if err := store.SaveDescriptor("vacuum", descriptor); err != nil {
t.Fatalf("SaveDescriptor: %v", err)
}
got, err := store.LoadDescriptor("vacuum")
if err != nil {
t.Fatalf("LoadDescriptor: %v", err)
}
if got == nil {
t.Fatalf("LoadDescriptor: nil descriptor")
}
if got.DisplayName != descriptor.DisplayName {
t.Fatalf("unexpected display name: got %q want %q", got.DisplayName, descriptor.DisplayName)
}
}
func TestConfigStoreRunHistoryRetention(t *testing.T) {
t.Parallel()
store, err := NewConfigStore(t.TempDir())
if err != nil {
t.Fatalf("NewConfigStore: %v", err)
}
base := time.Now().UTC().Add(-24 * time.Hour)
for i := 0; i < 15; i++ {
err := store.AppendRunRecord("balance", &JobRunRecord{
RunID: "s" + time.Duration(i).String(),
JobID: "job-success",
JobType: "balance",
WorkerID: "worker-a",
Outcome: RunOutcomeSuccess,
CompletedAt: timeToPtr(base.Add(time.Duration(i) * time.Minute)),
})
if err != nil {
t.Fatalf("AppendRunRecord success[%d]: %v", i, err)
}
}
for i := 0; i < 12; i++ {
err := store.AppendRunRecord("balance", &JobRunRecord{
RunID: "e" + time.Duration(i).String(),
JobID: "job-error",
JobType: "balance",
WorkerID: "worker-b",
Outcome: RunOutcomeError,
CompletedAt: timeToPtr(base.Add(time.Duration(i) * time.Minute)),
})
if err != nil {
t.Fatalf("AppendRunRecord error[%d]: %v", i, err)
}
}
history, err := store.LoadRunHistory("balance")
if err != nil {
t.Fatalf("LoadRunHistory: %v", err)
}
if len(history.SuccessfulRuns) != MaxSuccessfulRunHistory {
t.Fatalf("successful retention mismatch: got %d want %d", len(history.SuccessfulRuns), MaxSuccessfulRunHistory)
}
if len(history.ErrorRuns) != MaxErrorRunHistory {
t.Fatalf("error retention mismatch: got %d want %d", len(history.ErrorRuns), MaxErrorRunHistory)
}
for i := 1; i < len(history.SuccessfulRuns); i++ {
t1 := time.Time{}
if history.SuccessfulRuns[i-1].CompletedAt != nil {
t1 = *history.SuccessfulRuns[i-1].CompletedAt
}
t2 := time.Time{}
if history.SuccessfulRuns[i].CompletedAt != nil {
t2 = *history.SuccessfulRuns[i].CompletedAt
}
if t1.Before(t2) {
t.Fatalf("successful run order not descending at %d", i)
}
}
for i := 1; i < len(history.ErrorRuns); i++ {
t1 := time.Time{}
if history.ErrorRuns[i-1].CompletedAt != nil {
t1 = *history.ErrorRuns[i-1].CompletedAt
}
t2 := time.Time{}
if history.ErrorRuns[i].CompletedAt != nil {
t2 = *history.ErrorRuns[i].CompletedAt
}
if t1.Before(t2) {
t.Fatalf("error run order not descending at %d", i)
}
}
}
func TestConfigStoreListJobTypes(t *testing.T) {
t.Parallel()
store, err := NewConfigStore("")
if err != nil {
t.Fatalf("NewConfigStore: %v", err)
}
if err := store.SaveDescriptor("vacuum", &plugin_pb.JobTypeDescriptor{JobType: "vacuum"}); err != nil {
t.Fatalf("SaveDescriptor: %v", err)
}
if err := store.SaveJobTypeConfig(&plugin_pb.PersistedJobTypeConfig{
JobType: "balance",
AdminRuntime: &plugin_pb.AdminRuntimeConfig{Enabled: true},
}); err != nil {
t.Fatalf("SaveJobTypeConfig: %v", err)
}
if err := store.AppendRunRecord("ec", &JobRunRecord{Outcome: RunOutcomeSuccess, CompletedAt: timeToPtr(time.Now().UTC())}); err != nil {
t.Fatalf("AppendRunRecord: %v", err)
}
got, err := store.ListJobTypes()
if err != nil {
t.Fatalf("ListJobTypes: %v", err)
}
want := []string{"balance", "ec", "vacuum"}
if !reflect.DeepEqual(got, want) {
t.Fatalf("unexpected job types: got=%v want=%v", got, want)
}
}
func TestConfigStoreMonitorStateRoundTrip(t *testing.T) {
t.Parallel()
store, err := NewConfigStore(t.TempDir())
if err != nil {
t.Fatalf("NewConfigStore: %v", err)
}
tracked := []TrackedJob{
{
JobID: "job-1",
JobType: "vacuum",
State: "running",
Progress: 55,
WorkerID: "worker-a",
CreatedAt: timeToPtr(time.Now().UTC().Add(-2 * time.Minute)),
UpdatedAt: timeToPtr(time.Now().UTC().Add(-1 * time.Minute)),
},
}
activities := []JobActivity{
{
JobID: "job-1",
JobType: "vacuum",
Source: "worker_progress",
Message: "processing",
Stage: "running",
OccurredAt: timeToPtr(time.Now().UTC()),
Details: map[string]interface{}{
"step": "scan",
},
},
}
if err := store.SaveTrackedJobs(tracked); err != nil {
t.Fatalf("SaveTrackedJobs: %v", err)
}
if err := store.SaveActivities(activities); err != nil {
t.Fatalf("SaveActivities: %v", err)
}
gotTracked, err := store.LoadTrackedJobs()
if err != nil {
t.Fatalf("LoadTrackedJobs: %v", err)
}
if len(gotTracked) != 1 || gotTracked[0].JobID != tracked[0].JobID {
t.Fatalf("unexpected tracked jobs: %+v", gotTracked)
}
gotActivities, err := store.LoadActivities()
if err != nil {
t.Fatalf("LoadActivities: %v", err)
}
if len(gotActivities) != 1 || gotActivities[0].Message != activities[0].Message {
t.Fatalf("unexpected activities: %+v", gotActivities)
}
if gotActivities[0].Details["step"] != "scan" {
t.Fatalf("unexpected activity details: %+v", gotActivities[0].Details)
}
}
func TestConfigStoreJobDetailRoundTrip(t *testing.T) {
t.Parallel()
store, err := NewConfigStore(t.TempDir())
if err != nil {
t.Fatalf("NewConfigStore: %v", err)
}
input := TrackedJob{
JobID: "job-detail-1",
JobType: "vacuum",
Summary: "detail summary",
Detail: "detail payload",
CreatedAt: timeToPtr(time.Now().UTC().Add(-2 * time.Minute)),
UpdatedAt: timeToPtr(time.Now().UTC()),
Parameters: map[string]interface{}{
"volume_id": map[string]interface{}{"int64_value": "3"},
},
Labels: map[string]string{
"source": "detector",
},
ResultOutputValues: map[string]interface{}{
"moved": map[string]interface{}{"bool_value": true},
},
}
if err := store.SaveJobDetail(input); err != nil {
t.Fatalf("SaveJobDetail: %v", err)
}
got, err := store.LoadJobDetail(input.JobID)
if err != nil {
t.Fatalf("LoadJobDetail: %v", err)
}
if got == nil {
t.Fatalf("LoadJobDetail returned nil")
}
if got.Detail != input.Detail {
t.Fatalf("unexpected detail: got=%q want=%q", got.Detail, input.Detail)
}
if got.Labels["source"] != "detector" {
t.Fatalf("unexpected labels: %+v", got.Labels)
}
if got.ResultOutputValues == nil {
t.Fatalf("expected result output values")
}
}