Like Prometheus, but for logs.
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.
 
 
 
 
 
 
loki/pkg/logcli/delete/delete_test.go

536 lines
12 KiB

package delete
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"strconv"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/grafana/loki/v3/pkg/logcli/client"
)
func TestDeleteCreateQuery(t *testing.T) {
tests := []struct {
name string
query Query
expectError bool
expectParams client.DeleteRequestParams
expectOutput string
}{
{
name: "basic create request",
query: Query{
QueryString: "{job=\"test\"}",
Start: time.Unix(1000, 0),
End: time.Unix(2000, 0),
Quiet: false,
},
expectError: false,
expectParams: client.DeleteRequestParams{
Query: "{job=\"test\"}",
Start: "1000",
End: "2000",
},
expectOutput: "Delete request created successfully\n",
},
{
name: "create request with max interval",
query: Query{
QueryString: "{job=\"test\"}",
Start: time.Unix(1000, 0),
End: time.Unix(2000, 0),
MaxInterval: "1h",
Quiet: false,
},
expectError: false,
expectParams: client.DeleteRequestParams{
Query: "{job=\"test\"}",
Start: "1000",
End: "2000",
MaxInterval: "1h",
},
expectOutput: "Delete request created successfully\n",
},
{
name: "create request with zero times",
query: Query{
QueryString: "{job=\"test\"}",
Start: time.Time{},
End: time.Time{},
Quiet: false,
},
expectError: false,
expectParams: client.DeleteRequestParams{
Query: "{job=\"test\"}",
},
expectOutput: "Delete request created successfully\n",
},
{
name: "create request quiet mode",
query: Query{
QueryString: "{job=\"test\"}",
Start: time.Unix(1000, 0),
End: time.Unix(2000, 0),
Quiet: true,
},
expectError: false,
expectParams: client.DeleteRequestParams{
Query: "{job=\"test\"}",
Start: "1000",
End: "2000",
},
expectOutput: "",
},
{
name: "create request with API error",
query: Query{
QueryString: "{job=\"test\"}",
Start: time.Unix(1000, 0),
End: time.Unix(2000, 0),
Quiet: false,
},
expectError: true,
expectParams: client.DeleteRequestParams{
Query: "{job=\"test\"}",
Start: "1000",
End: "2000",
},
expectOutput: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Capture stdout
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
// Create mock client
mockClient := newMockDeleteClient()
if tt.expectError {
mockClient.withCreateError(errors.New("API error"))
}
// Execute test
err := tt.query.CreateQuery(mockClient)
// Restore stdout and capture output
w.Close()
os.Stdout = oldStdout
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
output := buf.String()
// Verify results
if tt.expectError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.expectOutput, output)
assert.Equal(t, 1, mockClient.createDeleteRequestCalls)
assert.Equal(t, tt.expectParams, mockClient.lastCreateParams)
assert.Equal(t, tt.query.Quiet, mockClient.lastCreateQuiet)
})
}
}
func TestDeleteListQuery(t *testing.T) {
tests := []struct {
name string
query Query
deleteRequests []client.DeleteRequest
expectError bool
expectOutput string
expectCallCount int
}{
{
name: "empty list",
query: Query{
Quiet: false,
},
deleteRequests: []client.DeleteRequest{},
expectError: false,
expectOutput: "Found 0 delete requests:\n",
},
{
name: "single delete request",
query: Query{
Quiet: false,
},
deleteRequests: []client.DeleteRequest{
{
StartTime: 1000,
EndTime: 2000,
Query: "{job=\"test\"}",
Status: "received",
},
},
expectError: false,
expectOutput: "Found 1 delete requests:\n" +
"Query: {job=\"test\"}\n" +
"Start Time: " + time.Unix(1000, 0).Format(time.RFC3339) + "\n" +
"End Time: " + time.Unix(2000, 0).Format(time.RFC3339) + "\n" +
"Status: received\n" +
"---\n",
},
{
name: "multiple delete requests",
query: Query{
Quiet: false,
},
deleteRequests: []client.DeleteRequest{
{
StartTime: 1000,
EndTime: 2000,
Query: "{job=\"test1\"}",
Status: "received",
},
{
StartTime: 3000,
EndTime: 4000,
Query: "{job=\"test2\"}",
Status: "processed",
},
},
expectError: false,
expectOutput: "Found 2 delete requests:\n" +
"Query: {job=\"test1\"}\n" +
"Start Time: " + time.Unix(1000, 0).Format(time.RFC3339) + "\n" +
"End Time: " + time.Unix(2000, 0).Format(time.RFC3339) + "\n" +
"Status: received\n" +
"---\n" +
"Query: {job=\"test2\"}\n" +
"Start Time: " + time.Unix(3000, 0).Format(time.RFC3339) + "\n" +
"End Time: " + time.Unix(4000, 0).Format(time.RFC3339) + "\n" +
"Status: processed\n" +
"---\n",
},
{
name: "quiet mode",
query: Query{
Quiet: true,
},
deleteRequests: []client.DeleteRequest{
{
StartTime: 1000,
EndTime: 2000,
Query: "{job=\"test\"}",
Status: "received",
},
},
expectError: false,
expectOutput: "Query: {job=\"test\"}\n" +
"Start Time: " + time.Unix(1000, 0).Format(time.RFC3339) + "\n" +
"End Time: " + time.Unix(2000, 0).Format(time.RFC3339) + "\n" +
"Status: received\n" +
"---\n",
},
{
name: "API error",
query: Query{
Quiet: false,
},
deleteRequests: nil,
expectError: true,
expectOutput: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Capture stdout
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
// Create mock client
mockClient := newMockDeleteClient().withDeleteRequests(tt.deleteRequests)
if tt.expectError {
mockClient.withListError(errors.New("API error"))
}
// Execute test
err := tt.query.ListQuery(mockClient)
// Restore stdout and capture output
w.Close()
os.Stdout = oldStdout
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
output := buf.String()
// Verify results
if tt.expectError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.expectOutput, output)
assert.Equal(t, 1, mockClient.listDeleteRequestsCalls)
assert.Equal(t, tt.query.Quiet, mockClient.lastListQuiet)
})
}
}
func TestDeleteListQueryJSON(t *testing.T) {
tests := []struct {
name string
query Query
deleteRequests []client.DeleteRequest
expectError bool
expectedJSON string
}{
{
name: "empty list JSON",
query: Query{
Quiet: false,
},
deleteRequests: []client.DeleteRequest{},
expectError: false,
expectedJSON: "[]\n",
},
{
name: "single request JSON",
query: Query{
Quiet: false,
},
deleteRequests: []client.DeleteRequest{
{
StartTime: 1000,
EndTime: 2000,
Query: "{job=\"test\"}",
Status: "received",
},
},
expectError: false,
expectedJSON: "[\n" +
" {\n" +
" \"start_time\": 1000,\n" +
" \"end_time\": 2000,\n" +
" \"query\": \"{job=\\\"test\\\"}\",\n" +
" \"status\": \"received\"\n" +
" }\n" +
"]\n",
},
{
name: "API error JSON",
query: Query{
Quiet: false,
},
deleteRequests: nil,
expectError: true,
expectedJSON: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Capture stdout
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
// Create mock client
mockClient := newMockDeleteClient().withDeleteRequests(tt.deleteRequests)
if tt.expectError {
mockClient.withListError(errors.New("API error"))
}
// Execute test
err := tt.query.ListQueryJSON(mockClient)
// Restore stdout and capture output
w.Close()
os.Stdout = oldStdout
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
output := buf.String()
// Verify results
if tt.expectError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.expectedJSON, output)
assert.Equal(t, 1, mockClient.listDeleteRequestsCalls)
assert.Equal(t, tt.query.Quiet, mockClient.lastListQuiet)
})
}
}
func TestDeleteCancelQuery(t *testing.T) {
tests := []struct {
name string
query Query
requestID string
force bool
expectError bool
expectOutput string
}{
{
name: "basic cancel request",
query: Query{
Quiet: false,
},
requestID: "test-request-123",
force: false,
expectError: false,
expectOutput: "Delete request test-request-123 cancelled successfully\n",
},
{
name: "cancel with force",
query: Query{
Quiet: false,
},
requestID: "test-request-456",
force: true,
expectError: false,
expectOutput: "Delete request test-request-456 cancelled successfully\n",
},
{
name: "cancel quiet mode",
query: Query{
Quiet: true,
},
requestID: "test-request-789",
force: false,
expectError: false,
expectOutput: "",
},
{
name: "cancel with API error",
query: Query{
Quiet: false,
},
requestID: "test-request-error",
force: false,
expectError: true,
expectOutput: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Capture stdout
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
// Create mock client
mockClient := newMockDeleteClient()
if tt.expectError {
mockClient.withCancelError(errors.New("API error"))
}
// Execute test
err := tt.query.CancelQuery(mockClient, tt.requestID, tt.force)
// Restore stdout and capture output
w.Close()
os.Stdout = oldStdout
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
output := buf.String()
// Verify results
if tt.expectError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.expectOutput, output)
assert.Equal(t, 1, mockClient.cancelDeleteRequestCalls)
assert.Equal(t, tt.requestID, mockClient.lastCancelRequestID)
assert.Equal(t, tt.force, mockClient.lastCancelForce)
assert.Equal(t, tt.query.Quiet, mockClient.lastCancelQuiet)
})
}
}
func TestTimeConversion(t *testing.T) {
tests := []struct {
name string
time time.Time
expected string
}{
{
name: "zero time",
time: time.Time{},
expected: "",
},
{
name: "unix epoch",
time: time.Unix(0, 0),
expected: "0",
},
{
name: "specific time",
time: time.Unix(1672531200, 0), // 2023-01-01 00:00:00 UTC
expected: "1672531200",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var result string
if !tt.time.IsZero() {
result = strconv.FormatInt(tt.time.Unix(), 10)
}
assert.Equal(t, tt.expected, result)
})
}
}
func BenchmarkDeleteCreateQuery(b *testing.B) {
mockClient := newMockDeleteClient()
query := Query{
QueryString: "{job=\"test\"}",
Start: time.Unix(1000, 0),
End: time.Unix(2000, 0),
Quiet: true,
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
err := query.CreateQuery(mockClient)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkDeleteListQuery(b *testing.B) {
deleteRequests := make([]client.DeleteRequest, 100)
for i := 0; i < 100; i++ {
deleteRequests[i] = client.DeleteRequest{
StartTime: int64(1000 + i),
EndTime: int64(2000 + i),
Query: fmt.Sprintf("{job=\"test%d\"}", i),
Status: "received",
}
}
mockClient := newMockDeleteClient().withDeleteRequests(deleteRequests)
query := Query{Quiet: true}
b.ResetTimer()
for i := 0; i < b.N; i++ {
err := query.ListQuery(mockClient)
if err != nil {
b.Fatal(err)
}
}
}