-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpromotion_candidates_test.go
More file actions
140 lines (124 loc) · 4.93 KB
/
Copy pathpromotion_candidates_test.go
File metadata and controls
140 lines (124 loc) · 4.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package hopper
import (
"context"
"strings"
"testing"
"time"
)
func TestPromotionCandidates(t *testing.T) {
ctx := context.Background()
db := openTestDB(t)
old := time.Now().Add(-90 * 24 * time.Hour)
recent := time.Now().Add(-1 * time.Hour)
sha := func(prefix byte) string {
return strings.Repeat(string(prefix), 64)
}
// cand builds an eligible candidate unless a field is overridden by the caller.
cand := func(s string, mut func(*Sample)) *Sample {
c := &Sample{
SHA256: s,
Source: "test",
Label: "unknown",
LabelSource: "test",
Path: "pending/foraged/npm/" + s[:8] + ".tgz",
Mtime: &old,
}
if mut != nil {
mut(c)
}
return c
}
// Three eligible candidates, distinct sha bytes so ordering and paging are
// observable.
mustInsert(t, ctx, db, cand(sha('1'), nil))
mustInsert(t, ctx, db, cand(sha('3'), nil))
mustInsert(t, ctx, db, cand(sha('5'), nil))
// Ineligible rows that every filter must reject.
mustInsert(t, ctx, db, cand(sha('6'), func(s *Sample) { s.Label = "good" })) // wrong label
mustInsert(t, ctx, db, cand(sha('7'), func(s *Sample) { s.Parent = sha('1') })) // archive member
mustInsert(t, ctx, db, cand(sha('8'), func(s *Sample) { s.Skip = "missing" })) // training-skipped
mustInsert(t, ctx, db, cand(sha('9'), func(s *Sample) { s.Mtime = &recent })) // too young
mustInsert(t, ctx, db, cand(sha('a'), func(s *Sample) { s.Mtime = nil })) // unknown mtime
mustInsert(t, ctx, db, cand(sha('b'), func(s *Sample) { s.Path = "good/foraged-promote/npm/x.tgz" })) // other pool
mustInsert(t, ctx, db, cand(sha('c'), func(s *Sample) { s.Path = "review/foraged/npm/x.tgz" })) // review queue
const prefix = "pending/foraged/"
cutoff := time.Now().Add(-30 * 24 * time.Hour)
// Full sweep from the keyspace floor returns exactly the three eligible rows,
// in sha order. The review queue must NOT appear — that exclusion is how
// human-review samples self-exclude from re-discovery.
got, err := db.PromotionCandidates(ctx, prefix, cutoff, "", 100)
if err != nil {
t.Fatalf("PromotionCandidates: %v", err)
}
want := []string{sha('1'), sha('3'), sha('5')}
if len(got) != len(want) {
t.Fatalf("got %d candidates, want %d: %v", len(got), len(want), shas(got))
}
for i, s := range got {
if s.SHA256 != want[i] {
t.Errorf("candidate %d = %s, want %s", i, s.SHA256, want[i])
}
}
// Keyset paging: a page of 2, then the cursor past the last sha yields the
// remainder with no overlap.
page1, err := db.PromotionCandidates(ctx, prefix, cutoff, "", 2)
if err != nil {
t.Fatalf("page1: %v", err)
}
if len(page1) != 2 || page1[0].SHA256 != sha('1') || page1[1].SHA256 != sha('3') {
t.Fatalf("page1 = %v, want [1.. 3..]", shas(page1))
}
page2, err := db.PromotionCandidates(ctx, prefix, cutoff, page1[1].SHA256, 2)
if err != nil {
t.Fatalf("page2: %v", err)
}
if len(page2) != 1 || page2[0].SHA256 != sha('5') {
t.Fatalf("page2 = %v, want [5..]", shas(page2))
}
// A cursor at the top of the keyspace returns nothing (drives the sweep's
// wrap-around in the client).
tail, err := db.PromotionCandidates(ctx, prefix, cutoff, sha('5'), 2)
if err != nil {
t.Fatalf("tail: %v", err)
}
if len(tail) != 0 {
t.Fatalf("tail = %v, want empty", shas(tail))
}
}
// TestCandidatesByLabelSighted verifies the generalized candidate query feeds
// promoter's sighted pass: only sighted-labeled rows under sighted/foraged/,
// with unknown rows and other pools excluded.
func TestCandidatesByLabelSighted(t *testing.T) {
ctx := context.Background()
db := openTestDB(t)
old := time.Now().Add(-90 * 24 * time.Hour)
sha := func(prefix byte) string { return strings.Repeat(string(prefix), 64) }
cand := func(s, label, path string) *Sample {
return &Sample{SHA256: s, Source: "test", Label: label, LabelSource: "forager", Path: path, Mtime: &old}
}
mustInsert(t, ctx, db, cand(sha('1'), "sighted", "sighted/foraged/npm/a.tgz"))
mustInsert(t, ctx, db, cand(sha('2'), "sighted", "sighted/foraged/pypi/b.tgz"))
mustInsert(t, ctx, db, cand(sha('3'), "unknown", "unknown/foraged/npm/c.tgz")) // wrong label
mustInsert(t, ctx, db, cand(sha('4'), "bad", "bad/foraged/npm/d.tgz")) // wrong label
mustInsert(t, ctx, db, cand(sha('5'), "sighted", "unknown/foraged/npm/e.tgz")) // wrong pool prefix
got, err := db.CandidatesByLabel(ctx, "sighted", "sighted/foraged/", time.Now(), "", 100)
if err != nil {
t.Fatalf("CandidatesByLabel: %v", err)
}
want := []string{sha('1'), sha('2')}
if len(got) != len(want) {
t.Fatalf("got %d candidates, want %d: %v", len(got), len(want), shas(got))
}
for i, s := range got {
if s.SHA256 != want[i] {
t.Errorf("candidate %d = %s, want %s", i, s.SHA256, want[i])
}
}
}
func shas(samples []*Sample) []string {
out := make([]string, len(samples))
for i, s := range samples {
out[i] = s.SHA256[:4]
}
return out
}