go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/internal/clustering/state/testutils.go (about) 1 // Copyright 2022 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package state 16 17 import ( 18 "context" 19 "crypto/sha256" 20 "encoding/binary" 21 "encoding/hex" 22 "fmt" 23 "time" 24 25 "go.chromium.org/luci/server/span" 26 27 "go.chromium.org/luci/analysis/internal/clustering" 28 ) 29 30 const testProject = "myproject" 31 32 // EntryBuilder provides methods to build a new Entry. 33 type EntryBuilder struct { 34 entry *Entry 35 } 36 37 // NewEntry creates a new entry builder with the given uniqifier. 38 // The uniqifier affects the ChunkID, AlgorithmVersion, RulesVersion 39 // and Algorithms. 40 func NewEntry(uniqifier int) *EntryBuilder { 41 // Generate a 128-bit chunkID from the uniqifier. 42 // Using a hash function ensures they will be approximately uniformly 43 // distributed through the keyspace. 44 var b [8]byte 45 binary.BigEndian.PutUint64(b[:], uint64(uniqifier)) 46 sum := sha256.Sum256(b[:]) 47 48 entry := &Entry{ 49 Project: testProject, 50 ChunkID: hex.EncodeToString(sum[0:16]), 51 PartitionTime: time.Date(2030, 1, 1, 1, 1, 1, uniqifier, time.UTC), 52 ObjectID: "abcdef1234567890abcdef1234567890", 53 Clustering: clustering.ClusterResults{ 54 AlgorithmsVersion: int64(uniqifier + 1), 55 ConfigVersion: time.Date(2025, 2, 1, 1, 1, 1, uniqifier, time.UTC), 56 RulesVersion: time.Date(2025, 1, 1, 1, 1, 1, uniqifier, time.UTC), 57 Algorithms: map[string]struct{}{ 58 fmt.Sprintf("alg-%v-v1", uniqifier): {}, 59 "alg-extra-v1": {}, 60 }, 61 Clusters: [][]clustering.ClusterID{ 62 { 63 { 64 Algorithm: fmt.Sprintf("alg-%v-v1", uniqifier), 65 ID: "00112233445566778899aabbccddeeff", 66 }, 67 }, 68 { 69 { 70 Algorithm: fmt.Sprintf("alg-%v-v1", uniqifier), 71 ID: "00112233445566778899aabbccddeeff", 72 }, 73 { 74 Algorithm: fmt.Sprintf("alg-%v-v1", uniqifier), 75 ID: "22", 76 }, 77 }, 78 }, 79 }, 80 } 81 return &EntryBuilder{entry} 82 } 83 84 // WithChunkIDPrefix specifies the start of the ChunkID to use. The remaining 85 // ChunkID will be derived from the uniqifier. 86 func (b *EntryBuilder) WithChunkIDPrefix(prefix string) *EntryBuilder { 87 b.entry.ChunkID = prefix + b.entry.ChunkID[len(prefix):] 88 return b 89 } 90 91 // WithProject specifies the LUCI project for the entry. 92 func (b *EntryBuilder) WithProject(project string) *EntryBuilder { 93 b.entry.Project = project 94 return b 95 } 96 97 // WithAlgorithmsVersion specifies the algorithms version for the entry. 98 func (b *EntryBuilder) WithAlgorithmsVersion(version int64) *EntryBuilder { 99 b.entry.Clustering.AlgorithmsVersion = version 100 return b 101 } 102 103 // WithConfigVersion specifies the config version for the entry. 104 func (b *EntryBuilder) WithConfigVersion(version time.Time) *EntryBuilder { 105 b.entry.Clustering.ConfigVersion = version 106 return b 107 } 108 109 // WithRulesVersion specifies the rules version for the entry. 110 func (b *EntryBuilder) WithRulesVersion(version time.Time) *EntryBuilder { 111 b.entry.Clustering.RulesVersion = version 112 return b 113 } 114 115 // Build returns the built entry. 116 func (b *EntryBuilder) Build() *Entry { 117 return b.entry 118 } 119 120 // CreateEntriesForTesting creates the given entries, for testing. 121 func CreateEntriesForTesting(ctx context.Context, entries []*Entry) (commitTimestamp time.Time, err error) { 122 return span.ReadWriteTransaction(ctx, func(ctx context.Context) error { 123 for _, e := range entries { 124 if err := Create(ctx, e); err != nil { 125 return err 126 } 127 } 128 return nil 129 }) 130 } 131 132 // ReadAllForTesting reads all state entries in the given project 133 // (up to 1 million records) for testing. 134 func ReadAllForTesting(ctx context.Context, project string) ([]*Entry, error) { 135 return readWhere(span.Single(ctx), project, "TRUE", nil, 1000*1000) 136 }