Files
nomad/api/evaluations_test.go
Luiz Aoqui b246227869 api: paginated results with different ordering (#12128)
The paginator logic was built when go-memdb iterators would return items
ordered lexicographically by their ID prefixes, but #12054 added the
option for some tables to return results ordered by their `CreateIndex`
instead, which invalidated the previous paginator assumption.

The iterator used for pagination must still return results in some order
so that the paginator can properly handle requests where the next_token
value is not present in the results anymore (e.g., the eval was GC'ed).

In these situations, the paginator will start the returned page in the
first element right after where the requested token should've been.

This commit moves the logic to generate pagination tokens from the
elements being paginated to the iterator itself so that callers can have
more control over the token format to make sure they are properly
ordered and stable.

It also allows configuring the paginator as being ordered in ascending
or descending order, which is relevant when looking for a token that may
not be present anymore.
2022-03-01 15:36:49 -05:00

160 lines
4.2 KiB
Go

package api
import (
"sort"
"testing"
"github.com/hashicorp/nomad/api/internal/testutil"
"github.com/stretchr/testify/require"
)
func TestEvaluations_List(t *testing.T) {
t.Parallel()
c, s := makeClient(t, nil, nil)
defer s.Stop()
e := c.Evaluations()
// Listing when nothing exists returns empty
result, qm, err := e.List(nil)
require.NoError(t, err)
require.Equal(t, uint64(0), qm.LastIndex, "bad index")
require.Equal(t, 0, len(result), "expected 0 evaluations")
// Register a job. This will create an evaluation.
jobs := c.Jobs()
job := testJob()
resp, wm, err := jobs.Register(job, nil)
require.NoError(t, err)
assertWriteMeta(t, wm)
// Check the evaluations again
result, qm, err = e.List(nil)
require.NoError(t, err)
assertQueryMeta(t, qm)
// if the eval fails fast there can be more than 1
// but they are in order of most recent first, so look at the last one
require.Greater(t, len(result), 0, "expected eval (%s), got none", resp.EvalID)
idx := len(result) - 1
require.Equal(t, resp.EvalID, result[idx].ID, "expected eval (%s), got: %#v", resp.EvalID, result[idx])
// wait until the 2nd eval shows up before we try paging
results := []*Evaluation{}
testutil.WaitForResult(func() (bool, error) {
results, _, err = e.List(nil)
if len(results) < 2 || err != nil {
return false, err
}
return true, nil
}, func(err error) {
t.Fatalf("err: %s", err)
})
// query first page
result, qm, err = e.List(&QueryOptions{
PerPage: int32(1),
})
require.NoError(t, err)
require.Equal(t, 1, len(result), "expected no evals after last one but got %d: %#v", len(result), result)
// query second page
result, qm, err = e.List(&QueryOptions{
PerPage: int32(1),
NextToken: qm.NextToken,
})
require.NoError(t, err)
require.Equal(t, 1, len(result), "expected no evals after last one but got %d: %#v", len(result), result)
// Query evaluations using a filter.
results, _, err = e.List(&QueryOptions{
Filter: `TriggeredBy == "job-register"`,
})
require.Equal(t, 1, len(result), "expected 1 eval, got %d", len(result))
}
func TestEvaluations_PrefixList(t *testing.T) {
t.Parallel()
c, s := makeClient(t, nil, nil)
defer s.Stop()
e := c.Evaluations()
// Listing when nothing exists returns empty
result, qm, err := e.PrefixList("abcdef")
require.NoError(t, err)
require.Equal(t, uint64(0), qm.LastIndex, "bad index")
require.Equal(t, 0, len(result), "expected 0 evaluations")
// Register a job. This will create an evaluation.
jobs := c.Jobs()
job := testJob()
resp, wm, err := jobs.Register(job, nil)
require.NoError(t, err)
assertWriteMeta(t, wm)
// Check the evaluations again
result, qm, err = e.PrefixList(resp.EvalID[:4])
require.NoError(t, err)
assertQueryMeta(t, qm)
// Check if we have the right list
require.Equal(t, 1, len(result))
require.Equal(t, resp.EvalID, result[0].ID)
}
func TestEvaluations_Info(t *testing.T) {
t.Parallel()
c, s := makeClient(t, nil, nil)
defer s.Stop()
e := c.Evaluations()
// Querying a nonexistent evaluation returns error
_, _, err := e.Info("8E231CF4-CA48-43FF-B694-5801E69E22FA", nil)
require.Error(t, err)
// Register a job. Creates a new evaluation.
jobs := c.Jobs()
job := testJob()
resp, wm, err := jobs.Register(job, nil)
require.NoError(t, err)
assertWriteMeta(t, wm)
// Try looking up by the new eval ID
result, qm, err := e.Info(resp.EvalID, nil)
require.NoError(t, err)
assertQueryMeta(t, qm)
// Check that we got the right result
require.NotNil(t, result)
require.Equal(t, resp.EvalID, result.ID)
}
func TestEvaluations_Allocations(t *testing.T) {
t.Parallel()
c, s := makeClient(t, nil, nil)
defer s.Stop()
e := c.Evaluations()
// Returns empty if no allocations
allocs, qm, err := e.Allocations("8E231CF4-CA48-43FF-B694-5801E69E22FA", nil)
require.NoError(t, err)
require.Equal(t, uint64(0), qm.LastIndex, "bad index")
require.Equal(t, 0, len(allocs), "expected 0 evaluations")
}
func TestEvaluations_Sort(t *testing.T) {
t.Parallel()
evals := []*Evaluation{
{CreateIndex: 2},
{CreateIndex: 1},
{CreateIndex: 5},
}
sort.Sort(EvalIndexSort(evals))
expect := []*Evaluation{
{CreateIndex: 5},
{CreateIndex: 2},
{CreateIndex: 1},
}
require.Equal(t, expect, evals)
}