[gold] Add more tests for more complex queries.

Bug: skia:10582
Change-Id: Id49e4440823745bf3a782106d046d9d5f35ee61f
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/401776
Reviewed-by: Leandro Lovisolo <lovisolo@google.com>
diff --git a/golden/go/search/query/query.go b/golden/go/search/query/query.go
index a72d336..4862f05 100644
--- a/golden/go/search/query/query.go
+++ b/golden/go/search/query/query.go
@@ -49,16 +49,16 @@
 	q.TraceValues = validate.QueryFormValue(r, "query")
 	q.RightTraceValues = validate.QueryFormValue(r, "rquery")
 
-	q.Limit = int32(validate.Int64FormValue(r, "limit", 50))
-	q.Offset = int32(validate.Int64FormValue(r, "offset", 0))
-	q.Offset = util.MaxInt32(q.Offset, 0)
+	q.Limit = int(validate.Int64FormValue(r, "limit", 50))
+	q.Offset = int(validate.Int64FormValue(r, "offset", 0))
+	q.Offset = util.MaxInt(q.Offset, 0)
 
 	validate.StrFormValue(r, "metric", &q.Metric, []string{CombinedMetric, PercentMetric, PixelMetric}, CombinedMetric)
 	validate.StrFormValue(r, "sort", &q.Sort, []string{SortDescending, SortAscending}, SortDescending)
 
 	// Parse and validate the filter values.
-	q.RGBAMinFilter = int32(validate.Int64FormValue(r, "frgbamin", 0))
-	q.RGBAMaxFilter = int32(validate.Int64FormValue(r, "frgbamax", 255))
+	q.RGBAMinFilter = int(validate.Int64FormValue(r, "frgbamin", 0))
+	q.RGBAMaxFilter = int(validate.Int64FormValue(r, "frgbamax", 255))
 
 	// Parse out the issue and patchsets.
 	q.Patchsets = validate.Int64SliceFormValue(r, "patchsets", nil)
diff --git a/golden/go/search/query/types.go b/golden/go/search/query/types.go
index 4fe8ba2..4df2cdc 100644
--- a/golden/go/search/query/types.go
+++ b/golden/go/search/query/types.go
@@ -43,13 +43,13 @@
 	IncludeDigestsProducedOnMaster bool `json:"master"`
 
 	// Filtering.
-	RGBAMinFilter              int32 `json:"frgbamin"` // Min RGBA delta
-	RGBAMaxFilter              int32 `json:"frgbamax"` // Max RGBA delta
-	MustIncludeReferenceFilter bool  `json:"fref"`     // Only digests with reference.
+	RGBAMinFilter              int  `json:"frgbamin"` // Min RGBA delta
+	RGBAMaxFilter              int  `json:"frgbamax"` // Max RGBA delta
+	MustIncludeReferenceFilter bool `json:"fref"`     // Only digests with reference.
 
 	// Pagination.
-	Offset int32 `json:"offset"`
-	Limit  int32 `json:"limit"`
+	Offset int `json:"offset"`
+	Limit  int `json:"limit"`
 }
 
 // IgnoreState returns the types.IgnoreState that this
diff --git a/golden/go/search/search.go b/golden/go/search/search.go
index 69d6e11..2a96c5f 100644
--- a/golden/go/search/search.go
+++ b/golden/go/search/search.go
@@ -187,7 +187,7 @@
 
 	// Sort the digests and fill the ones that are going to be displayed with
 	// additional data.
-	displayRet, offset := s.sortAndLimitDigests(ctx, q, results, int(q.Offset), int(q.Limit))
+	displayRet, offset := s.sortAndLimitDigests(ctx, q, results, q.Offset, q.Limit)
 	s.addTriageHistory(ctx, s.makeTriageHistoryGetter(q.CodeReviewSystemID, q.ChangelistID), displayRet)
 	traceComments := s.getTraceComments(ctx)
 	prepareTraceGroups(displayRet, exp, traceComments, isChangelistSearch)
@@ -791,7 +791,7 @@
 				continue
 			}
 
-			rgbaMaxDiff := int32(util.MaxInt(ref.MaxRGBADiffs[:]...))
+			rgbaMaxDiff := util.MaxInt(ref.MaxRGBADiffs[:]...)
 			if (rgbaMaxDiff < q.RGBAMinFilter) || (rgbaMaxDiff > q.RGBAMaxFilter) {
 				continue
 			}
diff --git a/golden/go/search2/search2.go b/golden/go/search2/search2.go
index a6c59f8..c6468b7 100644
--- a/golden/go/search2/search2.go
+++ b/golden/go/search2/search2.go
@@ -3,6 +3,7 @@
 package search2
 
 import (
+	"bytes"
 	"context"
 	"encoding/hex"
 	"sort"
@@ -395,6 +396,7 @@
 	ctx, span := trace.StartSpan(ctx, "search2_Search")
 	defer span.End()
 
+	ctx = context.WithValue(ctx, queryKey, *q)
 	ctx, err := s.addCommitsData(ctx)
 	if err != nil {
 		return nil, skerr.Wrap(err)
@@ -405,13 +407,13 @@
 	}
 
 	// Find all digests and traces that match the given search criteria.
-	traceDigests, err := s.getMatchingDigestsAndTraces(ctx, q)
+	traceDigests, err := s.getMatchingDigestsAndTraces(ctx)
 	if err != nil {
 		return nil, skerr.Wrap(err)
 	}
 	// Lookup the closest diffs to the given digests. This returns a subset according to the
 	// limit and offset in the query.
-	closestDiffs, allClosestLabels, err := s.getClosestDiffs(ctx, traceDigests, q)
+	closestDiffs, allClosestLabels, err := s.getClosestDiffs(ctx, traceDigests)
 	if err != nil {
 		return nil, skerr.Wrap(err)
 	}
@@ -441,7 +443,7 @@
 
 	return &frontend.SearchResponse{
 		Results:        results,
-		Offset:         int(q.Offset),
+		Offset:         q.Offset,
 		Size:           len(allClosestLabels),
 		BulkTriageData: bulkTriageData,
 		Commits:        commits,
@@ -453,26 +455,31 @@
 type searchContextKey string
 
 const (
-	actualWindowLength = searchContextKey("actualWindowLength")
-	commitToIdx        = searchContextKey("commitToIdx")
-	firstCommitID      = searchContextKey("firstCommitID")
-	firstTileID        = searchContextKey("firstTileID")
+	actualWindowLengthKey = searchContextKey("actualWindowLengthKey")
+	commitToIdxKey        = searchContextKey("commitToIdxKey")
+	firstCommitIDKey      = searchContextKey("firstCommitIDKey")
+	firstTileIDKey        = searchContextKey("firstTileIDKey")
+	queryKey              = searchContextKey("query")
 )
 
 func getFirstCommitID(ctx context.Context) schema.CommitID {
-	return ctx.Value(firstCommitID).(schema.CommitID)
+	return ctx.Value(firstCommitIDKey).(schema.CommitID)
 }
 
 func getFirstTileID(ctx context.Context) schema.TileID {
-	return ctx.Value(firstTileID).(schema.TileID)
+	return ctx.Value(firstTileIDKey).(schema.TileID)
 }
 
 func getCommitToIdxMap(ctx context.Context) map[schema.CommitID]int {
-	return ctx.Value(commitToIdx).(map[schema.CommitID]int)
+	return ctx.Value(commitToIdxKey).(map[schema.CommitID]int)
 }
 
 func getActualWindowLength(ctx context.Context) int {
-	return ctx.Value(actualWindowLength).(int)
+	return ctx.Value(actualWindowLengthKey).(int)
+}
+
+func getQuery(ctx context.Context) query.Search {
+	return ctx.Value(queryKey).(query.Search)
 }
 
 // addCommitsData finds the current sliding window of data (The last N commits) and adds the
@@ -501,16 +508,16 @@
 		return nil, skerr.Fmt("No commits with data")
 	}
 	// ids is ordered most recent commit to last commit at this point
-	ctx = context.WithValue(ctx, actualWindowLength, len(ids))
-	ctx = context.WithValue(ctx, firstCommitID, ids[len(ids)-1])
-	ctx = context.WithValue(ctx, firstTileID, firstObservedTile)
+	ctx = context.WithValue(ctx, actualWindowLengthKey, len(ids))
+	ctx = context.WithValue(ctx, firstCommitIDKey, ids[len(ids)-1])
+	ctx = context.WithValue(ctx, firstTileIDKey, firstObservedTile)
 	idToIndex := map[schema.CommitID]int{}
 	idx := 0
 	for i := len(ids) - 1; i >= 0; i-- {
 		idToIndex[ids[i]] = idx
 		idx++
 	}
-	ctx = context.WithValue(ctx, commitToIdx, idToIndex)
+	ctx = context.WithValue(ctx, commitToIdxKey, idToIndex)
 	return ctx, nil
 }
 
@@ -522,7 +529,7 @@
 
 // getMatchingDigestsAndTraces returns the tuples of digest+traceID that match the given query.
 // The order of the result is abitrary.
-func (s *Impl) getMatchingDigestsAndTraces(ctx context.Context, q *query.Search) ([]stageOneResult, error) {
+func (s *Impl) getMatchingDigestsAndTraces(ctx context.Context) ([]stageOneResult, error) {
 	ctx, span := trace.StartSpan(ctx, "getMatchingDigestsAndTraces")
 	defer span.End()
 	const statement = `WITH
@@ -542,6 +549,7 @@
 NonIgnoredTraces ON UntriagedDigests.grouping_id = NonIgnoredTraces.grouping_id AND
   UntriagedDigests.digest = NonIgnoredTraces.digest`
 
+	q := getQuery(ctx)
 	var triageStatuses []schema.ExpectationLabel
 	if q.IncludeUntriagedDigests {
 		triageStatuses = append(triageStatuses, schema.LabelUntriaged)
@@ -589,7 +597,7 @@
 // input. We are able to batch the queries by grouping and do so for better performance.
 // While this returns a subset of data as defined by the query, it also returns sufficient
 // information to bulk-triage all of the inputs.
-func (s *Impl) getClosestDiffs(ctx context.Context, inputs []stageOneResult, q *query.Search) ([]stageTwoResult, map[groupingDigestKey]expectations.Label, error) {
+func (s *Impl) getClosestDiffs(ctx context.Context, inputs []stageOneResult) ([]stageTwoResult, map[groupingDigestKey]expectations.Label, error) {
 	ctx, span := trace.StartSpan(ctx, "getClosestDiffs")
 	defer span.End()
 	byGrouping := map[schema.MD5Hash][]stageOneResult{}
@@ -605,8 +613,6 @@
 		byDigest[dID] = bd
 	}
 
-	bulkTriageData := map[groupingDigestKey]expectations.Label{}
-
 	for groupingID, inputs := range byGrouping {
 		const statement = `WITH
 ObservedDigestsInTile AS (
@@ -620,7 +626,7 @@
 ComparisonBetweenUntriagedAndObserved AS (
     SELECT DiffMetrics.* FROM DiffMetrics
     JOIN ObservedDigestsInTile ON DiffMetrics.right_digest = ObservedDigestsInTile.digest
-    WHERE left_digest = ANY($1) AND max_channel_diff >= $4 AND max_channel_diff <= $5
+    WHERE left_digest = ANY($1)
 )
 -- This will return the right_digest with the smallest combined_metric for each left_digest + label
 SELECT DISTINCT ON (left_digest, label)
@@ -636,7 +642,7 @@
 		for _, input := range inputs {
 			digests = append(digests, input.digest)
 		}
-		rows, err := s.db.Query(ctx, statement, digests, groupingID[:], getFirstTileID(ctx), q.RGBAMinFilter, q.RGBAMaxFilter)
+		rows, err := s.db.Query(ctx, statement, digests, groupingID[:], getFirstTileID(ctx))
 		if err != nil {
 			return nil, nil, skerr.Wrap(err)
 		}
@@ -670,36 +676,65 @@
 			if stageTwo.closestNegative != nil && stageTwo.closestPositive != nil {
 				if stageTwo.closestPositive.CombinedMetric < stageTwo.closestNegative.CombinedMetric {
 					stageTwo.closestDigest = stageTwo.closestPositive
-					bulkTriageData[groupingDigestKey{groupingID: groupingID, digest: leftDigest}] = expectations.Positive
 				} else {
 					stageTwo.closestDigest = stageTwo.closestNegative
-					bulkTriageData[groupingDigestKey{groupingID: groupingID, digest: leftDigest}] = expectations.Negative
 				}
 			} else {
 				// there is only one type of diff, so it defaults to the closest.
 				stageTwo.closestDigest = srdd
-				bulkTriageData[groupingDigestKey{groupingID: groupingID, digest: leftDigest}] = label.ToExpectation()
 			}
 			byDigest[leftDigest] = stageTwo
 		}
 		rows.Close()
 	}
 
-	rv := make([]stageTwoResult, 0, len(byDigest))
+	q := getQuery(ctx)
+	bulkTriageData := map[groupingDigestKey]expectations.Label{}
+	results := make([]stageTwoResult, 0, len(byDigest))
 	for _, s2 := range byDigest {
-		rv = append(rv, s2)
+		// Filter out any results without a closest triaged digest (if that option is selected).
+		if q.MustIncludeReferenceFilter && s2.closestDigest == nil {
+			continue
+		}
+		if s2.closestDigest != nil {
+			// Apply RGBA Filter here - if the closest digest isn't within range, we remove it.
+			maxDiff := util.MaxInt(s2.closestDigest.MaxRGBADiffs[:]...)
+			if maxDiff < q.RGBAMinFilter || maxDiff > q.RGBAMaxFilter {
+				continue
+			}
+			closestLabel := s2.closestDigest.Status
+			key := groupingDigestKey{groupingID: sql.AsMD5Hash(s2.groupingID), digest: sql.AsMD5Hash(s2.leftDigest)}
+			bulkTriageData[key] = closestLabel
+		}
+		results = append(results, s2)
+
 	}
-	sort.Slice(rv, func(i, j int) bool {
-		if rv[i].closestDigest == nil {
+	if q.Offset >= len(results) {
+		return nil, bulkTriageData, nil
+	}
+	sortAsc := q.Sort == query.SortAscending
+	sort.Slice(results, func(i, j int) bool {
+		if results[i].closestDigest == nil {
 			return true // sort results with no reference image to the top
 		}
-		if rv[j].closestDigest == nil {
+		if results[j].closestDigest == nil {
 			return false
 		}
-		return rv[i].closestDigest.CombinedMetric >= rv[j].closestDigest.CombinedMetric
+		if results[i].closestDigest.CombinedMetric == results[j].closestDigest.CombinedMetric {
+			// Tiebreak using digest in ascending order.
+			return bytes.Compare(results[i].leftDigest, results[j].leftDigest) < 0
+		}
+		if sortAsc {
+			return results[i].closestDigest.CombinedMetric < results[j].closestDigest.CombinedMetric
+		}
+		return results[i].closestDigest.CombinedMetric > results[j].closestDigest.CombinedMetric
 	})
-	// TODO(kjlubick) use query.limit and offset
-	return rv, bulkTriageData, nil
+
+	if q.Limit <= 0 {
+		return results, bulkTriageData, nil
+	}
+	end := util.MinInt(len(results), q.Offset+q.Limit)
+	return results[q.Offset:end], bulkTriageData, nil
 }
 
 // getParamsetsForDigests fetches all the traces that produced the digests in the data and
@@ -708,12 +743,34 @@
 func (s *Impl) getParamsetsForDigests(ctx context.Context, inputs []stageTwoResult) (map[types.Digest]paramtools.ParamSet, error) {
 	ctx, span := trace.StartSpan(ctx, "getParamsetsForDigests")
 	defer span.End()
-	var digests []schema.DigestBytes
+	var leftDigests []schema.DigestBytes
+	var rightDigests []schema.DigestBytes
 	for _, input := range inputs {
-		digests = append(digests, input.leftDigest)
-		digests = append(digests, input.rightDigests...)
+		leftDigests = append(leftDigests, input.leftDigest)
+		rightDigests = append(rightDigests, input.rightDigests...)
 	}
-	span.AddAttributes(trace.Int64Attribute("num_digests", int64(len(digests))))
+	span.AddAttributes(trace.Int64Attribute("num_digests", int64(len(leftDigests)+len(rightDigests))))
+	digestToTraces, err := s.getLeftParamsets(ctx, leftDigests)
+	if err != nil {
+		return nil, skerr.Wrap(err)
+	}
+	if err := s.addRightParamsets(ctx, digestToTraces, rightDigests); err != nil {
+		return nil, skerr.Wrap(err)
+	}
+	rv, err := s.expandTracesIntoParamsets(ctx, digestToTraces)
+	if err != nil {
+		return nil, skerr.Wrap(err)
+	}
+	return rv, nil
+}
+
+// getLeftParamsets finds the traces that draw the given digests. It will respect the ignore rules
+// setting.
+func (s *Impl) getLeftParamsets(ctx context.Context, digests []schema.DigestBytes) (map[types.Digest][]schema.TraceID, error) {
+	ctx, span := trace.StartSpan(ctx, "getLeftParamsets")
+	defer span.End()
+	span.AddAttributes(trace.Int64Attribute("num_left_digests", int64(len(digests))))
+
 	const statement = `WITH
 DigestsAndTraces AS (
 	SELECT DISTINCT encode(digest, 'hex') as digest, trace_id
@@ -722,9 +779,14 @@
 SELECT DigestsAndTraces.digest, DigestsAndTraces.trace_id
 FROM DigestsAndTraces
 JOIN Traces ON DigestsAndTraces.trace_id = Traces.trace_id
-WHERE matches_any_ignore_rule = FALSE
+WHERE matches_any_ignore_rule = ANY($3)
 `
-	rows, err := s.db.Query(ctx, statement, digests, getFirstTileID(ctx))
+	q := getQuery(ctx)
+	ignoreStatues := []bool{false}
+	if q.IncludeIgnoredTraces {
+		ignoreStatues = append(ignoreStatues, true)
+	}
+	rows, err := s.db.Query(ctx, statement, digests, getFirstTileID(ctx), ignoreStatues)
 	if err != nil {
 		return nil, skerr.Wrap(err)
 	}
@@ -738,12 +800,33 @@
 		}
 		digestToTraces[digest] = append(digestToTraces[digest], traceID)
 	}
+	return digestToTraces, nil
+}
 
-	rv, err := s.expandTracesIntoParamsets(ctx, digestToTraces)
+// addRightParamsets finds the traces that draw the given digests. We do not need to consider the
+// ignore rules because those only apply to the search results (that is, the left side).
+func (s *Impl) addRightParamsets(ctx context.Context, digestToTraces map[types.Digest][]schema.TraceID, digests []schema.DigestBytes) error {
+	ctx, span := trace.StartSpan(ctx, "getLeftParamsets")
+	defer span.End()
+	span.AddAttributes(trace.Int64Attribute("num_right_digests", int64(len(digests))))
+
+	const statement = `SELECT DISTINCT encode(digest, 'hex') as digest, trace_id
+FROM TiledTraceDigests WHERE digest = ANY($1) AND tile_id >= $2
+`
+	rows, err := s.db.Query(ctx, statement, digests, getFirstTileID(ctx))
 	if err != nil {
-		return nil, skerr.Wrap(err)
+		return skerr.Wrap(err)
 	}
-	return rv, nil
+	defer rows.Close()
+	for rows.Next() {
+		var digest types.Digest
+		var traceID schema.TraceID
+		if err := rows.Scan(&digest, &traceID); err != nil {
+			return skerr.Wrap(err)
+		}
+		digestToTraces[digest] = append(digestToTraces[digest], traceID)
+	}
+	return nil
 }
 
 // expandTracesIntoParamsets effectively returns a map detailing "who drew a given digest?". This
@@ -810,7 +893,7 @@
 	const statement = `SELECT keys FROM Traces WHERE trace_id = $1`
 	row := s.db.QueryRow(ctx, statement, traceID)
 	var keys paramtools.Params
-	if err := row.Scan(&traceID, &keys); err != nil {
+	if err := row.Scan(&keys); err != nil {
 		return nil, skerr.Wrap(err)
 	}
 	s.traceCache.Add(string(traceID), keys)
diff --git a/golden/go/search2/search2_test.go b/golden/go/search2/search2_test.go
index 6575755..1246611 100644
--- a/golden/go/search2/search2_test.go
+++ b/golden/go/search2/search2_test.go
@@ -518,10 +518,10 @@
 		IncludePositiveDigests:           false,
 		IncludeNegativeDigests:           false,
 		IncludeUntriagedDigests:          true,
-		Sort:                             "desc",
+		Sort:                             query.SortDescending,
 		IncludeIgnoredTraces:             false,
 		TraceValues: paramtools.ParamSet{
-			types.CorpusField: []string{"round"},
+			types.CorpusField: []string{dks.RoundCorpus},
 		},
 		RGBAMinFilter: 0,
 		RGBAMaxFilter: 255,
@@ -534,7 +534,7 @@
 			Status: expectations.Untriaged,
 			ParamSet: paramtools.ParamSet{
 				dks.ColorModeKey:      []string{dks.GreyColorMode, dks.RGBColorMode},
-				types.CorpusField:     []string{"round"},
+				types.CorpusField:     []string{dks.RoundCorpus},
 				dks.DeviceKey:         []string{dks.IPadDevice, dks.IPhoneDevice},
 				dks.OSKey:             []string{dks.IOS}, // Note: Android + Taimen are ignored
 				types.PrimaryKeyField: []string{dks.CircleTest},
@@ -545,7 +545,7 @@
 					DigestIndices: []int{-1, 2, -1, -1, 2, -1, -1, 0, -1, -1},
 					Params: paramtools.Params{
 						dks.ColorModeKey:      dks.GreyColorMode,
-						types.CorpusField:     "round",
+						types.CorpusField:     dks.RoundCorpus,
 						dks.DeviceKey:         dks.IPhoneDevice,
 						dks.OSKey:             dks.IOS,
 						types.PrimaryKeyField: dks.CircleTest,
@@ -555,7 +555,7 @@
 					DigestIndices: []int{1, -1, 1, -1, 1, -1, 1, -1, 0, -1},
 					Params: paramtools.Params{
 						dks.ColorModeKey:      dks.RGBColorMode,
-						types.CorpusField:     "round",
+						types.CorpusField:     dks.RoundCorpus,
 						dks.DeviceKey:         dks.IPhoneDevice,
 						dks.OSKey:             dks.IOS,
 						types.PrimaryKeyField: dks.CircleTest,
@@ -565,7 +565,7 @@
 					DigestIndices: []int{1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
 					Params: paramtools.Params{
 						dks.ColorModeKey:      dks.RGBColorMode,
-						types.CorpusField:     "round",
+						types.CorpusField:     dks.RoundCorpus,
 						dks.DeviceKey:         dks.IPadDevice,
 						dks.OSKey:             dks.IOS,
 						types.PrimaryKeyField: dks.CircleTest,
@@ -575,7 +575,7 @@
 					DigestIndices: []int{2, 2, 2, 2, 2, 2, 2, 0, 0, 0},
 					Params: paramtools.Params{
 						dks.ColorModeKey:      dks.GreyColorMode,
-						types.CorpusField:     "round",
+						types.CorpusField:     dks.RoundCorpus,
 						dks.DeviceKey:         dks.IPadDevice,
 						dks.OSKey:             dks.IOS,
 						types.PrimaryKeyField: dks.CircleTest,
@@ -597,7 +597,7 @@
 					Status:       expectations.Positive,
 					ParamSet: paramtools.ParamSet{
 						dks.ColorModeKey:      []string{dks.RGBColorMode},
-						types.CorpusField:     []string{"round"},
+						types.CorpusField:     []string{dks.RoundCorpus},
 						dks.DeviceKey:         []string{dks.QuadroDevice, dks.IPadDevice, dks.IPhoneDevice, dks.WalleyeDevice},
 						dks.OSKey:             []string{dks.AndroidOS, dks.Windows10dot2OS, dks.IOS},
 						types.PrimaryKeyField: []string{dks.CircleTest},
@@ -612,7 +612,7 @@
 			Status: expectations.Untriaged,
 			ParamSet: paramtools.ParamSet{
 				dks.ColorModeKey:      []string{dks.RGBColorMode},
-				types.CorpusField:     []string{"round"},
+				types.CorpusField:     []string{dks.RoundCorpus},
 				dks.DeviceKey:         []string{dks.QuadroDevice},
 				dks.OSKey:             []string{dks.Windows10dot3OS},
 				types.PrimaryKeyField: []string{dks.CircleTest},
@@ -623,7 +623,7 @@
 					DigestIndices: []int{-1, -1, -1, 0, 0, 0, 0, 0, 0, 0},
 					Params: paramtools.Params{
 						dks.ColorModeKey:      dks.RGBColorMode,
-						types.CorpusField:     "round",
+						types.CorpusField:     dks.RoundCorpus,
 						dks.DeviceKey:         dks.QuadroDevice,
 						dks.OSKey:             dks.Windows10dot3OS,
 						types.PrimaryKeyField: dks.CircleTest,
@@ -643,7 +643,7 @@
 					Status:       expectations.Positive,
 					ParamSet: paramtools.ParamSet{
 						dks.ColorModeKey:      []string{dks.RGBColorMode},
-						types.CorpusField:     []string{"round"},
+						types.CorpusField:     []string{dks.RoundCorpus},
 						dks.DeviceKey:         []string{dks.QuadroDevice, dks.IPadDevice, dks.IPhoneDevice, dks.WalleyeDevice},
 						dks.OSKey:             []string{dks.AndroidOS, dks.Windows10dot2OS, dks.IOS},
 						types.PrimaryKeyField: []string{dks.CircleTest},
@@ -658,7 +658,7 @@
 			Status: expectations.Untriaged,
 			ParamSet: paramtools.ParamSet{
 				dks.ColorModeKey:      []string{dks.GreyColorMode},
-				types.CorpusField:     []string{"round"},
+				types.CorpusField:     []string{dks.RoundCorpus},
 				dks.DeviceKey:         []string{dks.QuadroDevice},
 				dks.OSKey:             []string{dks.Windows10dot3OS},
 				types.PrimaryKeyField: []string{dks.CircleTest},
@@ -669,7 +669,7 @@
 					DigestIndices: []int{-1, -1, -1, 0, 0, 0, 0, 0, 0, -1},
 					Params: paramtools.Params{
 						dks.ColorModeKey:      dks.GreyColorMode,
-						types.CorpusField:     "round",
+						types.CorpusField:     dks.RoundCorpus,
 						dks.DeviceKey:         dks.QuadroDevice,
 						dks.OSKey:             dks.Windows10dot3OS,
 						types.PrimaryKeyField: dks.CircleTest,
@@ -689,7 +689,7 @@
 					Status:       expectations.Positive,
 					ParamSet: paramtools.ParamSet{
 						dks.ColorModeKey:      []string{dks.GreyColorMode},
-						types.CorpusField:     []string{"round"},
+						types.CorpusField:     []string{dks.RoundCorpus},
 						dks.DeviceKey:         []string{dks.QuadroDevice, dks.IPadDevice, dks.IPhoneDevice, dks.WalleyeDevice},
 						dks.OSKey:             []string{dks.AndroidOS, dks.Windows10dot2OS, dks.IOS},
 						types.PrimaryKeyField: []string{dks.CircleTest},
@@ -712,17 +712,428 @@
 	}, res)
 }
 
+func TestSearch_IncludeIgnoredAtHead_Success(t *testing.T) {
+	unittest.LargeTest(t)
+
+	ctx := context.Background()
+	db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
+	require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, dks.Build()))
+
+	s := New(db, 100)
+	res, err := s.Search(ctx, &query.Search{
+		OnlyIncludeDigestsProducedAtHead: true,
+		IncludePositiveDigests:           false,
+		IncludeNegativeDigests:           false,
+		IncludeUntriagedDigests:          true,
+		Sort:                             query.SortDescending,
+		IncludeIgnoredTraces:             true,
+		TraceValues: paramtools.ParamSet{
+			types.CorpusField: []string{dks.RoundCorpus},
+		},
+		RGBAMinFilter: 0,
+		RGBAMaxFilter: 255,
+	})
+	require.NoError(t, err)
+	require.Len(t, res.Results, 3)
+	assert.Equal(t, frontend.TraceGroup{
+		Traces: []frontend.Trace{{
+			ID:            "0b61c8d85467fc95b1306128ceb2ef6d",
+			DigestIndices: []int{-1, 2, -1, -1, 2, -1, -1, 0, -1, -1},
+			Params: paramtools.Params{
+				dks.ColorModeKey:      dks.GreyColorMode,
+				types.CorpusField:     dks.RoundCorpus,
+				dks.DeviceKey:         dks.IPhoneDevice,
+				dks.OSKey:             dks.IOS,
+				types.PrimaryKeyField: dks.CircleTest,
+			},
+		}, {
+			ID:            "22b530e029c22e396c5a24c0900c9ed5",
+			DigestIndices: []int{1, -1, 1, -1, 1, -1, 1, -1, 0, -1},
+			Params: paramtools.Params{
+				dks.ColorModeKey:      dks.RGBColorMode,
+				types.CorpusField:     dks.RoundCorpus,
+				dks.DeviceKey:         dks.IPhoneDevice,
+				dks.OSKey:             dks.IOS,
+				types.PrimaryKeyField: dks.CircleTest,
+			},
+		}, {
+			ID:            "273119ca291863331e906fe71bde0e7d",
+			DigestIndices: []int{1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
+			Params: paramtools.Params{
+				dks.ColorModeKey:      dks.RGBColorMode,
+				types.CorpusField:     dks.RoundCorpus,
+				dks.DeviceKey:         dks.IPadDevice,
+				dks.OSKey:             dks.IOS,
+				types.PrimaryKeyField: dks.CircleTest,
+			},
+		}, {
+			ID:            "3b44c31afc832ef9d1a2d25a5b873152",
+			DigestIndices: []int{2, 2, 2, 2, 2, 2, 2, 0, 0, 0},
+			Params: paramtools.Params{
+				dks.ColorModeKey:      dks.GreyColorMode,
+				types.CorpusField:     dks.RoundCorpus,
+				dks.DeviceKey:         dks.IPadDevice,
+				dks.OSKey:             dks.IOS,
+				types.PrimaryKeyField: dks.CircleTest,
+			},
+		}, {
+			// This trace matches an ignore rule, but should be visible due to the search
+			// terms
+			ID:            "902ac9eee937cd11b4ccc81d535ff33f",
+			DigestIndices: []int{-1, -1, -1, -1, -1, -1, 0, 0, 0, 0},
+			Params: paramtools.Params{
+				dks.ColorModeKey:      dks.RGBColorMode,
+				types.CorpusField:     dks.RoundCorpus,
+				dks.DeviceKey:         dks.TaimenDevice,
+				dks.OSKey:             dks.AndroidOS,
+				types.PrimaryKeyField: dks.CircleTest,
+			},
+		}},
+		Digests: []frontend.DigestStatus{
+			{Digest: dks.DigestC05Unt, Status: expectations.Untriaged},
+			{Digest: dks.DigestC01Pos, Status: expectations.Positive},
+			{Digest: dks.DigestC02Pos, Status: expectations.Positive},
+		},
+		TotalDigests: 3,
+	}, res.Results[0].TraceGroup)
+	assert.Equal(t, paramtools.ParamSet{
+		dks.ColorModeKey:      []string{dks.GreyColorMode, dks.RGBColorMode},
+		types.CorpusField:     []string{dks.RoundCorpus},
+		dks.DeviceKey:         []string{dks.IPadDevice, dks.IPhoneDevice, dks.TaimenDevice},
+		dks.OSKey:             []string{dks.AndroidOS, dks.IOS},
+		types.PrimaryKeyField: []string{dks.CircleTest},
+	}, res.Results[0].ParamSet)
+	// Other two results should be the same as they didn't have any ignored data. We spot-check
+	// that here.
+	assert.Equal(t, paramtools.ParamSet{
+		dks.ColorModeKey:      []string{dks.RGBColorMode},
+		types.CorpusField:     []string{dks.RoundCorpus},
+		dks.DeviceKey:         []string{dks.QuadroDevice},
+		dks.OSKey:             []string{dks.Windows10dot3OS},
+		types.PrimaryKeyField: []string{dks.CircleTest},
+	}, res.Results[1].ParamSet)
+	assert.Equal(t, paramtools.ParamSet{
+		dks.ColorModeKey:      []string{dks.GreyColorMode},
+		types.CorpusField:     []string{dks.RoundCorpus},
+		dks.DeviceKey:         []string{dks.QuadroDevice},
+		dks.OSKey:             []string{dks.Windows10dot3OS},
+		types.PrimaryKeyField: []string{dks.CircleTest},
+	}, res.Results[2].ParamSet)
+}
+
+func TestSearch_RespectMinMaxRGBAFilter_Success(t *testing.T) {
+	unittest.LargeTest(t)
+
+	ctx := context.Background()
+	db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
+	require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, dks.Build()))
+
+	s := New(db, 100)
+	res, err := s.Search(ctx, &query.Search{
+		OnlyIncludeDigestsProducedAtHead: true,
+		IncludePositiveDigests:           false,
+		IncludeNegativeDigests:           false,
+		IncludeUntriagedDigests:          true,
+		Sort:                             query.SortDescending,
+		IncludeIgnoredTraces:             false,
+		TraceValues: paramtools.ParamSet{
+			types.CorpusField: []string{dks.RoundCorpus},
+		},
+		RGBAMinFilter: 4,
+		RGBAMaxFilter: 20,
+	})
+	require.NoError(t, err)
+	// The other two results are removed because they are above or below the filter.
+	assert.Equal(t, &frontend.SearchResponse{
+		Results: []*frontend.SearchResult{{
+			Digest: dks.DigestC03Unt,
+			Test:   dks.CircleTest,
+			Status: expectations.Untriaged,
+			ParamSet: paramtools.ParamSet{
+				dks.ColorModeKey:      []string{dks.RGBColorMode},
+				types.CorpusField:     []string{dks.RoundCorpus},
+				dks.DeviceKey:         []string{dks.QuadroDevice},
+				dks.OSKey:             []string{dks.Windows10dot3OS},
+				types.PrimaryKeyField: []string{dks.CircleTest},
+			},
+			TraceGroup: frontend.TraceGroup{
+				Traces: []frontend.Trace{{
+					ID:            "9156c4774e7d90db488b6aadf416ff8e",
+					DigestIndices: []int{-1, -1, -1, 0, 0, 0, 0, 0, 0, 0},
+					Params: paramtools.Params{
+						dks.ColorModeKey:      dks.RGBColorMode,
+						types.CorpusField:     dks.RoundCorpus,
+						dks.DeviceKey:         dks.QuadroDevice,
+						dks.OSKey:             dks.Windows10dot3OS,
+						types.PrimaryKeyField: dks.CircleTest,
+					},
+				}},
+				Digests: []frontend.DigestStatus{{
+					Digest: dks.DigestC03Unt, Status: expectations.Untriaged,
+				}},
+				TotalDigests: 1,
+			},
+			RefDiffs: map[common.RefClosest]*frontend.SRDiffDigest{
+				common.PositiveRef: {
+					CombinedMetric: 0.89245414, QueryMetric: 0.89245414, PixelDiffPercent: 50, NumDiffPixels: 32,
+					MaxRGBADiffs: [4]int{1, 7, 4, 0},
+					DimDiffer:    false,
+					Digest:       dks.DigestC01Pos,
+					Status:       expectations.Positive,
+					ParamSet: paramtools.ParamSet{
+						dks.ColorModeKey:      []string{dks.RGBColorMode},
+						types.CorpusField:     []string{dks.RoundCorpus},
+						dks.DeviceKey:         []string{dks.QuadroDevice, dks.IPadDevice, dks.IPhoneDevice, dks.WalleyeDevice},
+						dks.OSKey:             []string{dks.AndroidOS, dks.Windows10dot2OS, dks.IOS},
+						types.PrimaryKeyField: []string{dks.CircleTest},
+					},
+				},
+				common.NegativeRef: nil,
+			},
+			ClosestRef: common.PositiveRef,
+		}},
+		Offset:  0,
+		Size:    1,
+		Commits: kitchenSinkCommits,
+		BulkTriageData: web_frontend.TriageRequestData{
+			dks.CircleTest: {
+				dks.DigestC03Unt: expectations.Positive,
+			},
+		},
+	}, res)
+}
+
+func TestSearch_RespectLimitOffsetOrder_Success(t *testing.T) {
+	unittest.LargeTest(t)
+
+	ctx := context.Background()
+	db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
+	require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, dks.Build()))
+
+	s := New(db, 100)
+	res, err := s.Search(ctx, &query.Search{
+		OnlyIncludeDigestsProducedAtHead: true,
+		IncludePositiveDigests:           true,
+		IncludeNegativeDigests:           false,
+		IncludeUntriagedDigests:          false,
+		Sort:                             query.SortAscending,
+		IncludeIgnoredTraces:             false,
+		TraceValues: paramtools.ParamSet{
+			types.CorpusField: []string{dks.CornersCorpus},
+		},
+		RGBAMinFilter: 0,
+		RGBAMaxFilter: 255,
+		Offset:        3, // Carefully selected to return one result from square and triangle each.
+		Limit:         2,
+	})
+	require.NoError(t, err)
+	assert.Equal(t, &frontend.SearchResponse{
+		Results: []*frontend.SearchResult{{
+			Digest: dks.DigestA08Pos,
+			Test:   dks.SquareTest,
+			Status: expectations.Positive,
+			ParamSet: paramtools.ParamSet{
+				dks.ColorModeKey:      []string{dks.RGBColorMode},
+				types.CorpusField:     []string{dks.CornersCorpus},
+				dks.DeviceKey:         []string{dks.WalleyeDevice},
+				dks.OSKey:             []string{dks.AndroidOS},
+				types.PrimaryKeyField: []string{dks.SquareTest},
+			},
+			TraceGroup: frontend.TraceGroup{
+				Traces: []frontend.Trace{{
+					ID:            "4686a4134535ad178b67325f5f2f613a",
+					DigestIndices: []int{-1, -1, -1, -1, -1, 4, 3, 2, 1, 0},
+					Params: paramtools.Params{
+						dks.ColorModeKey:      dks.RGBColorMode,
+						types.CorpusField:     dks.CornersCorpus,
+						dks.DeviceKey:         dks.WalleyeDevice,
+						dks.OSKey:             dks.AndroidOS,
+						types.PrimaryKeyField: dks.SquareTest,
+					},
+				}},
+				Digests: []frontend.DigestStatus{
+					{Digest: dks.DigestA08Pos, Status: expectations.Positive},
+					{Digest: dks.DigestA07Pos, Status: expectations.Positive},
+					{Digest: dks.DigestA06Unt, Status: expectations.Untriaged},
+					{Digest: dks.DigestA01Pos, Status: expectations.Positive},
+					{Digest: dks.DigestA05Unt, Status: expectations.Untriaged},
+				},
+				TotalDigests: 5,
+			},
+			RefDiffs: map[common.RefClosest]*frontend.SRDiffDigest{
+				common.PositiveRef: {
+					CombinedMetric: 0.15655607, QueryMetric: 0.15655607, PixelDiffPercent: 3.125, NumDiffPixels: 2,
+					MaxRGBADiffs: [4]int{4, 0, 0, 0},
+					DimDiffer:    false,
+					Digest:       dks.DigestA01Pos,
+					Status:       expectations.Positive,
+					ParamSet: paramtools.ParamSet{
+						dks.ColorModeKey:      []string{dks.RGBColorMode},
+						types.CorpusField:     []string{dks.CornersCorpus},
+						dks.DeviceKey:         []string{dks.QuadroDevice, dks.IPadDevice, dks.IPhoneDevice, dks.TaimenDevice, dks.WalleyeDevice},
+						dks.OSKey:             []string{dks.AndroidOS, dks.Windows10dot2OS, dks.Windows10dot3OS, dks.IOS},
+						types.PrimaryKeyField: []string{dks.SquareTest},
+					},
+				},
+				common.NegativeRef: {
+					CombinedMetric: 10, QueryMetric: 10, PixelDiffPercent: 100, NumDiffPixels: 64,
+					MaxRGBADiffs: [4]int{255, 255, 255, 255},
+					DimDiffer:    false,
+					Digest:       dks.DigestA09Neg,
+					Status:       expectations.Negative,
+					// Even though this is ignored, we are free to show it on the right side
+					// (just not a part of the actual results).
+					ParamSet: paramtools.ParamSet{
+						dks.ColorModeKey:      []string{dks.RGBColorMode},
+						types.CorpusField:     []string{dks.CornersCorpus},
+						dks.DeviceKey:         []string{dks.TaimenDevice},
+						dks.OSKey:             []string{dks.AndroidOS},
+						types.PrimaryKeyField: []string{dks.SquareTest},
+					},
+				},
+			},
+			ClosestRef: common.PositiveRef,
+		}, {
+			Digest: dks.DigestB01Pos,
+			Test:   dks.TriangleTest,
+			Status: expectations.Positive,
+			ParamSet: paramtools.ParamSet{
+				dks.ColorModeKey:  []string{dks.RGBColorMode},
+				types.CorpusField: []string{dks.CornersCorpus},
+				// Of note - this test is *not* ignored for the taimen device
+				dks.DeviceKey:         []string{dks.QuadroDevice, dks.IPadDevice, dks.IPhoneDevice, dks.TaimenDevice, dks.WalleyeDevice},
+				dks.OSKey:             []string{dks.AndroidOS, dks.Windows10dot2OS, dks.Windows10dot3OS, dks.IOS},
+				types.PrimaryKeyField: []string{dks.TriangleTest},
+			},
+			TraceGroup: frontend.TraceGroup{
+				Traces: []frontend.Trace{{
+					ID:            "1a16cbc8805378f0a6ef654a035d86c4",
+					DigestIndices: []int{-1, -1, -1, -1, -1, -1, 0, 0, 0, 0},
+					Params: paramtools.Params{
+						dks.ColorModeKey:      dks.RGBColorMode,
+						types.CorpusField:     dks.CornersCorpus,
+						dks.DeviceKey:         dks.TaimenDevice,
+						dks.OSKey:             dks.AndroidOS,
+						types.PrimaryKeyField: dks.TriangleTest,
+					},
+				}, {
+					ID:            "555f149dfe944816076a57c633578dbc",
+					DigestIndices: []int{-1, -1, -1, 0, 0, 0, 0, 0, 0, 0},
+					Params: paramtools.Params{
+						dks.ColorModeKey:      dks.RGBColorMode,
+						types.CorpusField:     dks.CornersCorpus,
+						dks.DeviceKey:         dks.QuadroDevice,
+						dks.OSKey:             dks.Windows10dot3OS,
+						types.PrimaryKeyField: dks.TriangleTest,
+					},
+				}, {
+					ID:            "7346d80b7d5d1087fd61ae40098f4277",
+					DigestIndices: []int{2, 0, 0, -1, -1, -1, -1, -1, -1, -1},
+					Params: paramtools.Params{
+						dks.ColorModeKey:      dks.RGBColorMode,
+						types.CorpusField:     dks.CornersCorpus,
+						dks.DeviceKey:         dks.QuadroDevice,
+						dks.OSKey:             dks.Windows10dot2OS,
+						types.PrimaryKeyField: dks.TriangleTest,
+					},
+				}, {
+					ID:            "760c2db998331eafd3023f4b6d135b06",
+					DigestIndices: []int{1, -1, 2, -1, 2, -1, 1, -1, 0, -1},
+					Params: paramtools.Params{
+						dks.ColorModeKey:      dks.RGBColorMode,
+						types.CorpusField:     dks.CornersCorpus,
+						dks.DeviceKey:         dks.IPhoneDevice,
+						dks.OSKey:             dks.IOS,
+						types.PrimaryKeyField: dks.TriangleTest,
+					},
+				}, {
+					ID:            "8fe41dfab19e0a291f37964416432128",
+					DigestIndices: []int{1, 1, 2, 1, 1, 2, 1, 0, 0, 0},
+					Params: paramtools.Params{
+						dks.ColorModeKey:      dks.RGBColorMode,
+						types.CorpusField:     dks.CornersCorpus,
+						dks.DeviceKey:         dks.IPadDevice,
+						dks.OSKey:             dks.IOS,
+						types.PrimaryKeyField: dks.TriangleTest,
+					},
+				}, {
+					ID:            "ab734e10b7aed9d06a91f46d14746270",
+					DigestIndices: []int{-1, -1, -1, -1, -1, 0, 0, 0, 0, 0},
+					Params: paramtools.Params{
+						dks.ColorModeKey:      dks.RGBColorMode,
+						types.CorpusField:     dks.CornersCorpus,
+						dks.DeviceKey:         dks.WalleyeDevice,
+						dks.OSKey:             dks.AndroidOS,
+						types.PrimaryKeyField: dks.TriangleTest,
+					},
+				}},
+				Digests: []frontend.DigestStatus{
+					{Digest: dks.DigestB01Pos, Status: expectations.Positive},
+					{Digest: dks.DigestB03Neg, Status: expectations.Negative},
+					{Digest: dks.DigestBlank, Status: expectations.Untriaged},
+				},
+				TotalDigests: 3,
+			},
+			RefDiffs: map[common.RefClosest]*frontend.SRDiffDigest{
+				common.PositiveRef: {
+					CombinedMetric: 1.9362538, QueryMetric: 1.9362538, PixelDiffPercent: 43.75, NumDiffPixels: 28,
+					MaxRGBADiffs: [4]int{11, 5, 42, 0},
+					DimDiffer:    false,
+					Digest:       dks.DigestB02Pos,
+					Status:       expectations.Positive,
+					ParamSet: paramtools.ParamSet{
+						dks.ColorModeKey:      []string{dks.GreyColorMode},
+						types.CorpusField:     []string{dks.CornersCorpus},
+						dks.DeviceKey:         []string{dks.QuadroDevice, dks.IPadDevice, dks.IPhoneDevice, dks.WalleyeDevice},
+						dks.OSKey:             []string{dks.AndroidOS, dks.Windows10dot2OS, dks.Windows10dot3OS, dks.IOS},
+						types.PrimaryKeyField: []string{dks.TriangleTest},
+					},
+				},
+				common.NegativeRef: {
+					CombinedMetric: 2.9445405, QueryMetric: 2.9445405, PixelDiffPercent: 10.9375, NumDiffPixels: 7,
+					MaxRGBADiffs: [4]int{250, 244, 197, 51},
+					DimDiffer:    false,
+					Digest:       dks.DigestB03Neg,
+					Status:       expectations.Negative,
+					ParamSet: paramtools.ParamSet{
+						dks.ColorModeKey:      []string{dks.RGBColorMode},
+						types.CorpusField:     []string{dks.CornersCorpus},
+						dks.DeviceKey:         []string{dks.IPadDevice, dks.IPhoneDevice},
+						dks.OSKey:             []string{dks.IOS},
+						types.PrimaryKeyField: []string{dks.TriangleTest},
+					},
+				},
+			},
+			ClosestRef: common.PositiveRef,
+		}},
+		Offset:  3,
+		Size:    6,
+		Commits: kitchenSinkCommits,
+		BulkTriageData: web_frontend.TriageRequestData{
+			dks.SquareTest: {
+				dks.DigestA01Pos: expectations.Positive,
+				dks.DigestA02Pos: expectations.Positive,
+				dks.DigestA03Pos: expectations.Positive,
+				dks.DigestA08Pos: expectations.Positive,
+			}, dks.TriangleTest: {
+				dks.DigestB02Pos: expectations.Positive,
+				dks.DigestB01Pos: expectations.Positive,
+			},
+		},
+	}, res)
+}
+
 func TestMakeTraceGroup_TwoMostlyStableTraces_Success(t *testing.T) {
 	unittest.SmallTest(t)
 
-	ctx := context.WithValue(context.Background(), commitToIdx, map[schema.CommitID]int{
+	ctx := context.WithValue(context.Background(), commitToIdxKey, map[schema.CommitID]int{
 		"10": 0,
 		"11": 1,
 		"12": 2,
 		"17": 3,
 		"20": 4,
 	})
-	ctx = context.WithValue(ctx, actualWindowLength, 5)
+	ctx = context.WithValue(ctx, actualWindowLengthKey, 5)
 	inputData := []traceDigestCommit{
 		{traceID: schema.TraceID{0xaa}, commitID: "10", digest: dks.DigestA01Pos},
 		{traceID: schema.TraceID{0xaa}, commitID: "11", digest: dks.DigestA01Pos},
@@ -757,7 +1168,7 @@
 func TestMakeTraceGroup_OneFlakyTrace_PrioritizeShowingMostUsedDigests(t *testing.T) {
 	unittest.SmallTest(t)
 
-	ctx := context.WithValue(context.Background(), commitToIdx, map[schema.CommitID]int{
+	ctx := context.WithValue(context.Background(), commitToIdxKey, map[schema.CommitID]int{
 		"10": 0,
 		"11": 1,
 		"12": 2,
@@ -777,7 +1188,7 @@
 		"31": 16,
 		"32": 17,
 	})
-	ctx = context.WithValue(ctx, actualWindowLength, 18)
+	ctx = context.WithValue(ctx, actualWindowLengthKey, 18)
 	inputData := []traceDigestCommit{
 		{traceID: schema.TraceID{0xaa}, commitID: "10", digest: "dC"},
 		{traceID: schema.TraceID{0xaa}, commitID: "11", digest: "dC"},