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  }