github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/engine/analytics/analytics_reporter_test.go (about)

     1  package analytics
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  
    12  	"github.com/tilt-dev/clusterid"
    13  	tiltanalytics "github.com/tilt-dev/tilt/internal/analytics"
    14  	"github.com/tilt-dev/tilt/internal/container"
    15  	"github.com/tilt-dev/tilt/internal/feature"
    16  	"github.com/tilt-dev/tilt/internal/k8s"
    17  	"github.com/tilt-dev/tilt/internal/store"
    18  	"github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1"
    19  	"github.com/tilt-dev/tilt/pkg/model"
    20  	"github.com/tilt-dev/wmclient/pkg/analytics"
    21  )
    22  
    23  var (
    24  	lu = v1alpha1.LiveUpdateSpec{Syncs: []v1alpha1.LiveUpdateSync{{ContainerPath: "/"}}} // non-empty LiveUpdate
    25  
    26  	imgTargDB       = model.ImageTarget{BuildDetails: model.DockerBuild{}}
    27  	imgTargDBWithLU = model.ImageTarget{LiveUpdateSpec: lu, BuildDetails: model.DockerBuild{}}
    28  
    29  	kTarg    = model.K8sTarget{}
    30  	dTarg    = model.DockerComposeTarget{}
    31  	lTarg    = model.LocalTarget{}
    32  	lSrvTarg = model.LocalTarget{ServeCmd: model.Cmd{Argv: []string{"echo", "hi"}}}
    33  )
    34  
    35  var (
    36  	r1 = "gcr.io/some-project-162817/one"
    37  	r2 = "gcr.io/some-project-162817/two"
    38  	r3 = "gcr.io/some-project-162817/three"
    39  	r4 = "gcr.io/some-project-162817/four"
    40  
    41  	iTargWithRef1     = iTargetForRef(r1).WithLiveUpdateSpec("one", lu).WithBuildDetails(model.DockerBuild{})
    42  	iTargWithRef2     = iTargetForRef(r2).WithLiveUpdateSpec("two", lu).WithBuildDetails(model.DockerBuild{})
    43  	iTargWithRef3     = iTargetForRef(r3).WithLiveUpdateSpec("three", lu).WithBuildDetails(model.DockerBuild{})
    44  	iTargWithRef4NoLU = iTargetForRef(r4)
    45  )
    46  
    47  func TestAnalyticsReporter_Everything(t *testing.T) {
    48  	tf := newAnalyticsReporterTestFixture(t)
    49  
    50  	tf.addManifest(
    51  		tf.nextManifest().
    52  			WithLabels(map[string]string{"k8s": "k8s"}).
    53  			WithImageTarget(imgTargDB).
    54  			WithDeployTarget(kTarg)) // k8s
    55  
    56  	tf.addManifest(tf.nextManifest().WithImageTarget(imgTargDBWithLU)) // liveupdate
    57  
    58  	tf.addManifest(
    59  		tf.nextManifest().
    60  			WithLabels(map[string]string{"k8s": "k8s1"}).
    61  			WithDeployTarget(kTarg)) // k8s, unbuilt
    62  	tf.addManifest(
    63  		tf.nextManifest().
    64  			WithLabels(map[string]string{"k8s": "k8s2"}).
    65  			WithDeployTarget(kTarg)) // k8s, unbuilt
    66  	tf.addManifest(
    67  		tf.nextManifest().
    68  			WithLabels(map[string]string{"k8s": "k8s3"}).
    69  			WithDeployTarget(kTarg)) // k8s, unbuilt
    70  
    71  	tf.addManifest(
    72  		tf.nextManifest().
    73  			WithLabels(map[string]string{"dc": "dc1"}).
    74  			WithDeployTarget(dTarg)) // dc
    75  	tf.addManifest(
    76  		tf.nextManifest().
    77  			WithLabels(map[string]string{"dc": "dc2"}).
    78  			WithDeployTarget(dTarg)) // dc
    79  
    80  	tf.addManifest(tf.nextManifest().WithImageTarget(imgTargDBWithLU).WithDeployTarget(dTarg)) // dc, liveupdate
    81  	tf.addManifest(tf.nextManifest().WithImageTargets(
    82  		[]model.ImageTarget{imgTargDBWithLU, imgTargDBWithLU})) // liveupdate, multipleimageliveupdate
    83  
    84  	tf.addManifest(tf.nextManifest().WithDeployTarget(lTarg))
    85  	tf.addManifest(tf.nextManifest().WithDeployTarget(lTarg))
    86  	tf.addManifest(tf.nextManifest().WithDeployTarget(lSrvTarg))
    87  	tf.addManifest(tf.nextManifest(), func(mt *store.ManifestTarget) {
    88  		mt.State.DisableState = v1alpha1.DisableStateDisabled
    89  	})
    90  
    91  	state := tf.st.LockMutableStateForTesting()
    92  	state.TiltStartTime = time.Now()
    93  
    94  	state.CompletedBuildCount = 3
    95  
    96  	tf.st.UnlockMutableState()
    97  	tf.kClient.Registry = &v1alpha1.RegistryHosting{
    98  		Host:                     "localhost:5000",
    99  		HostFromContainerRuntime: "registry:5000",
   100  	}
   101  
   102  	tf.run()
   103  
   104  	expectedTags := map[string]string{
   105  		"builds.completed_count":                              "3",
   106  		"resource.count":                                      "13",
   107  		"resource.dockercompose.count":                        "3",
   108  		"resource.unbuiltresources.count":                     "3",
   109  		"resource.liveupdate.count":                           "3",
   110  		"resource.k8s.count":                                  "4",
   111  		"resource.sameimagemultiplecontainerliveupdate.count": "0", // tests for this below
   112  		"resource.multipleimageliveupdate.count":              "1",
   113  		"resource.local.count":                                "3",
   114  		"resource.localserve.count":                           "1",
   115  		"resource.enabled.count":                              "12",
   116  		"tiltfile.error":                                      "false",
   117  		"up.starttime":                                        state.TiltStartTime.Format(time.RFC3339),
   118  		"env":                                                 k8s.AnalyticsEnv(clusterid.ProductDockerDesktop),
   119  		"term_mode":                                           "0",
   120  		"k8s.runtime":                                         "docker",
   121  		"k8s.registry.host":                                   "1",
   122  		"k8s.registry.hostFromCluster":                        "1",
   123  		"label.count":                                         "2",
   124  		"feature.testflag_enabled.enabled":                    "true",
   125  	}
   126  
   127  	tf.assertStats(t, expectedTags)
   128  }
   129  
   130  func TestAnalyticsReporter_SameImageMultiContainer(t *testing.T) {
   131  	tf := newAnalyticsReporterTestFixture(t)
   132  
   133  	injectCountsA := map[string]int{
   134  		r1: 1,
   135  		r2: 2,
   136  	}
   137  	k8sTargA := kTarg.WithRefInjectCounts(injectCountsA)
   138  	tf.addManifest(tf.nextManifest().
   139  		WithImageTarget(iTargWithRef1).
   140  		WithImageTarget(iTargWithRef2).
   141  		WithDeployTarget(k8sTargA))
   142  
   143  	injectCountsB := map[string]int{
   144  		r2: 2,
   145  		r3: 3,
   146  	}
   147  	k8sTargB := kTarg.WithRefInjectCounts(injectCountsB)
   148  	tf.addManifest(tf.nextManifest().
   149  		WithImageTarget(iTargWithRef2).
   150  		WithImageTarget(iTargWithRef3).
   151  		WithDeployTarget(k8sTargB))
   152  
   153  	tf.run()
   154  
   155  	assert.Equal(t, "2", tf.ma.Counts[0].Tags["resource.sameimagemultiplecontainerliveupdate.count"])
   156  }
   157  
   158  func TestAnalyticsReporter_SameImageMultiContainer_NoIncr(t *testing.T) {
   159  	tf := newAnalyticsReporterTestFixture(t)
   160  
   161  	injectCounts := map[string]int{
   162  		r1: 1,
   163  		r4: 2,
   164  	}
   165  	k8sTarg := kTarg.WithRefInjectCounts(injectCounts)
   166  	tf.addManifest(tf.nextManifest().
   167  		WithImageTarget(iTargWithRef1).
   168  		WithImageTarget(iTargWithRef4NoLU). // injects multiple times, but no LU so won't record stat for it
   169  		WithDeployTarget(k8sTarg))
   170  
   171  	tf.run()
   172  
   173  	assert.Equal(t, "0", tf.ma.Counts[0].Tags["resource.sameimagemultiplecontainerliveupdate.count"])
   174  }
   175  
   176  func TestAnalyticsReporter_TiltfileError(t *testing.T) {
   177  	tf := newAnalyticsReporterTestFixture(t)
   178  
   179  	tf.addManifest(tf.nextManifest().WithImageTarget(model.ImageTarget{BuildDetails: model.DockerBuild{}}))
   180  	tf.addManifest(tf.nextManifest().WithDeployTarget(model.K8sTarget{}))
   181  	tf.addManifest(tf.nextManifest().WithDeployTarget(model.K8sTarget{}))
   182  	tf.addManifest(tf.nextManifest().WithDeployTarget(model.K8sTarget{}))
   183  	tf.addManifest(tf.nextManifest().WithDeployTarget(model.DockerComposeTarget{}))
   184  	tf.addManifest(tf.nextManifest().WithDeployTarget(model.DockerComposeTarget{}))
   185  	tf.addManifest(tf.nextManifest().WithDeployTarget(model.DockerComposeTarget{}))
   186  	tf.addManifest(tf.nextManifest().WithDeployTarget(model.DockerComposeTarget{}))
   187  
   188  	state := tf.st.LockMutableStateForTesting()
   189  	state.TiltStartTime = time.Now()
   190  
   191  	state.CompletedBuildCount = 3
   192  
   193  	state.TiltfileStates[model.MainTiltfileManifestName].AddCompletedBuild(model.BuildRecord{Error: errors.New("foo")})
   194  
   195  	tf.st.UnlockMutableState()
   196  
   197  	tf.run()
   198  
   199  	expectedTags := map[string]string{
   200  		"builds.completed_count":           "3",
   201  		"tiltfile.error":                   "true",
   202  		"up.starttime":                     state.TiltStartTime.Format(time.RFC3339),
   203  		"env":                              k8s.AnalyticsEnv(clusterid.ProductDockerDesktop),
   204  		"term_mode":                        "0",
   205  		"k8s.runtime":                      "docker",
   206  		"feature.testflag_enabled.enabled": "true",
   207  	}
   208  
   209  	tf.assertStats(t, expectedTags)
   210  }
   211  
   212  type analyticsReporterTestFixture struct {
   213  	manifestCount int
   214  	ar            *AnalyticsReporter
   215  	ma            *analytics.MemoryAnalytics
   216  	kClient       *k8s.FakeK8sClient
   217  	st            *store.TestingStore
   218  }
   219  
   220  func newAnalyticsReporterTestFixture(t testing.TB) *analyticsReporterTestFixture {
   221  	st := store.NewTestingStore()
   222  	opter := tiltanalytics.NewFakeOpter(analytics.OptIn)
   223  	ma, a := tiltanalytics.NewMemoryTiltAnalyticsForTest(opter)
   224  	kClient := k8s.NewFakeK8sClient(t)
   225  	features := feature.Defaults{
   226  		"testflag_disabled":     feature.Value{Enabled: false},
   227  		"testflag_enabled":      feature.Value{Enabled: true},
   228  		"obsoleteflag_enabled":  feature.Value{Status: feature.Obsolete, Enabled: true},
   229  		"obsoleteflag_disabled": feature.Value{Status: feature.Obsolete, Enabled: false},
   230  	}
   231  
   232  	state := st.LockMutableStateForTesting()
   233  	state.Features = feature.FromDefaults(features).ToEnabled()
   234  	st.UnlockMutableState()
   235  
   236  	ar := ProvideAnalyticsReporter(a, st, kClient, clusterid.ProductDockerDesktop, features)
   237  	return &analyticsReporterTestFixture{
   238  		manifestCount: 0,
   239  		ar:            ar,
   240  		ma:            ma,
   241  		kClient:       kClient,
   242  		st:            st,
   243  	}
   244  }
   245  
   246  type addManifestOpt func(mt *store.ManifestTarget)
   247  
   248  func (artf *analyticsReporterTestFixture) addManifest(m model.Manifest, opts ...addManifestOpt) {
   249  	state := artf.st.LockMutableStateForTesting()
   250  	mt := store.NewManifestTarget(m)
   251  	mt.State.DisableState = v1alpha1.DisableStateEnabled
   252  	for _, opt := range opts {
   253  		opt(mt)
   254  	}
   255  	state.UpsertManifestTarget(mt)
   256  	artf.st.UnlockMutableState()
   257  }
   258  
   259  func (artf *analyticsReporterTestFixture) nextManifest() model.Manifest {
   260  	artf.manifestCount++
   261  	return model.Manifest{Name: model.ManifestName(fmt.Sprintf("manifest%d", artf.manifestCount))}
   262  }
   263  
   264  func (artf *analyticsReporterTestFixture) run() {
   265  	artf.ar.report(context.Background())
   266  
   267  	artf.ar.a.Flush(500 * time.Second)
   268  }
   269  
   270  func (artf *analyticsReporterTestFixture) assertStats(t *testing.T, expectedTags map[string]string) {
   271  	expectedCounts := []analytics.CountEvent{{Name: "up.running", N: 1, Tags: expectedTags}}
   272  	assert.Equal(t, expectedCounts, artf.ma.Counts)
   273  }
   274  
   275  func iTargetForRef(ref string) model.ImageTarget {
   276  	named := container.MustParseNamed(ref)
   277  	selector := container.NameSelector(named)
   278  	return model.MustNewImageTarget(selector)
   279  }