github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/controllers/core/tiltfile/filewatch_test.go (about)

     1  package tiltfile
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  	"testing"
     7  
     8  	ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
     9  
    10  	"github.com/tilt-dev/tilt/pkg/apis"
    11  	"github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1"
    12  
    13  	"github.com/google/go-cmp/cmp"
    14  	"github.com/moby/buildkit/frontend/dockerfile/dockerignore"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  	"k8s.io/apimachinery/pkg/api/equality"
    18  
    19  	"github.com/tilt-dev/tilt/internal/container"
    20  	"github.com/tilt-dev/tilt/internal/controllers/fake"
    21  	"github.com/tilt-dev/tilt/internal/k8s/testyaml"
    22  	"github.com/tilt-dev/tilt/internal/store"
    23  	"github.com/tilt-dev/tilt/internal/testutils"
    24  	"github.com/tilt-dev/tilt/internal/testutils/manifestbuilder"
    25  	"github.com/tilt-dev/tilt/internal/testutils/tempdir"
    26  	"github.com/tilt-dev/tilt/pkg/model"
    27  )
    28  
    29  func TestFileWatch_basic(t *testing.T) {
    30  	f := newFWFixture(t)
    31  
    32  	target := model.LocalTarget{
    33  		Name: "foo",
    34  		Deps: []string{"."},
    35  	}
    36  	f.SetManifestLocalTarget(target)
    37  
    38  	f.RequireFileWatchSpecEqual(target.ID(), v1alpha1.FileWatchSpec{WatchedPaths: []string{"."}})
    39  }
    40  
    41  func TestFileWatch_disabledOnCIMode(t *testing.T) {
    42  	f := newFWFixture(t)
    43  
    44  	f.inputs.EngineMode = store.EngineModeCI
    45  
    46  	target := model.LocalTarget{
    47  		Name: "foo",
    48  		Deps: []string{"."},
    49  	}
    50  	f.SetManifestLocalTarget(target)
    51  	m := model.Manifest{Name: "foo"}.WithDeployTarget(target)
    52  	f.SetManifest(m)
    53  
    54  	actualSet := ToFileWatchObjects(f.inputs, make(disableSourceMap))
    55  	assert.Empty(t, actualSet)
    56  }
    57  
    58  func TestFileWatch_IgnoreOutputsImageRefs(t *testing.T) {
    59  	f := newFWFixture(t)
    60  
    61  	target := model.MustNewImageTarget(container.MustParseSelector("img")).
    62  		WithBuildDetails(model.CustomBuild{
    63  			CmdImageSpec: v1alpha1.CmdImageSpec{OutputsImageRefTo: f.JoinPath("ref.txt")},
    64  			Deps:         []string{f.Path()},
    65  		})
    66  
    67  	m := manifestbuilder.New(f, "sancho").
    68  		WithK8sYAML(testyaml.SanchoYAML).
    69  		WithImageTarget(target).
    70  		Build()
    71  	f.SetManifest(m)
    72  
    73  	f.RequireFileWatchSpecEqual(target.ID(), v1alpha1.FileWatchSpec{
    74  		WatchedPaths: []string{f.Path()},
    75  		Ignores: []v1alpha1.IgnoreDef{
    76  			{BasePath: f.Path(), Patterns: []string{"ref.txt"}},
    77  		},
    78  	})
    79  }
    80  
    81  func TestFileWatch_ConfigFiles(t *testing.T) {
    82  	f := newFWFixture(t)
    83  
    84  	f.SetTiltIgnoreContents("**/foo")
    85  	f.inputs.ConfigFiles = append(f.inputs.ConfigFiles, "path_to_watch", "stop")
    86  
    87  	id := model.TargetID{Type: model.TargetTypeConfigs, Name: model.TargetName(model.MainTiltfileManifestName)}
    88  	f.RequireFileWatchSpecEqual(id, v1alpha1.FileWatchSpec{
    89  		WatchedPaths: []string{"path_to_watch", "stop"},
    90  		Ignores: []v1alpha1.IgnoreDef{
    91  			{BasePath: f.Path(), Patterns: []string{"**/foo"}},
    92  		},
    93  	})
    94  }
    95  
    96  func TestFileWatch_IgnoreTiltIgnore(t *testing.T) {
    97  	f := newFWFixture(t)
    98  
    99  	target := model.LocalTarget{
   100  		Name: "foo",
   101  		Deps: []string{"."},
   102  	}
   103  	f.SetManifestLocalTarget(target)
   104  	f.SetTiltIgnoreContents("**/foo")
   105  	f.RequireFileWatchSpecEqual(target.ID(), v1alpha1.FileWatchSpec{
   106  		WatchedPaths: []string{"."},
   107  		Ignores: []v1alpha1.IgnoreDef{
   108  			{BasePath: f.Path(), Patterns: []string{"**/foo"}},
   109  		},
   110  	})
   111  }
   112  
   113  func TestFileWatch_IgnoreWatchSettings(t *testing.T) {
   114  	f := newFWFixture(t)
   115  
   116  	target := model.LocalTarget{
   117  		Name: "foo",
   118  		Deps: []string{"."},
   119  	}
   120  	f.SetManifestLocalTarget(target)
   121  
   122  	f.inputs.WatchSettings.Ignores = append(f.inputs.WatchSettings.Ignores, model.Dockerignore{
   123  		LocalPath: f.Path(),
   124  		Patterns:  []string{"**/foo"},
   125  	})
   126  
   127  	f.RequireFileWatchSpecEqual(target.ID(), v1alpha1.FileWatchSpec{
   128  		WatchedPaths: []string{"."},
   129  		Ignores: []v1alpha1.IgnoreDef{
   130  			{BasePath: f.Path(), Patterns: []string{"**/foo"}},
   131  		},
   132  	})
   133  }
   134  
   135  func TestFileWatch_PickUpTiltIgnoreChanges(t *testing.T) {
   136  	f := newFWFixture(t)
   137  
   138  	target := model.LocalTarget{
   139  		Name: "foo",
   140  		Deps: []string{"."},
   141  	}
   142  	f.SetManifestLocalTarget(target)
   143  	f.SetTiltIgnoreContents("**/foo")
   144  	f.RequireFileWatchSpecEqual(target.ID(), v1alpha1.FileWatchSpec{
   145  		WatchedPaths: []string{"."},
   146  		Ignores: []v1alpha1.IgnoreDef{
   147  			{BasePath: f.Path(), Patterns: []string{"**/foo"}},
   148  		},
   149  	})
   150  
   151  	f.SetTiltIgnoreContents("**foo\n!bar/baz/foo")
   152  	f.RequireFileWatchSpecEqual(target.ID(), v1alpha1.FileWatchSpec{
   153  		WatchedPaths: []string{"."},
   154  		Ignores: []v1alpha1.IgnoreDef{
   155  			{BasePath: f.Path(), Patterns: []string{"**foo", "!bar/baz/foo"}},
   156  		},
   157  	})
   158  }
   159  
   160  type fwFixture struct {
   161  	t   testing.TB
   162  	ctx context.Context
   163  	cli ctrlclient.Client
   164  	*tempdir.TempDirFixture
   165  	inputs WatchInputs
   166  }
   167  
   168  func newFWFixture(t *testing.T) *fwFixture {
   169  	cli := fake.NewFakeTiltClient()
   170  
   171  	ctx, _, _ := testutils.CtxAndAnalyticsForTest()
   172  	ctx, cancel := context.WithCancel(ctx)
   173  
   174  	tmpdir := tempdir.NewTempDirFixture(t)
   175  	tmpdir.Chdir()
   176  
   177  	f := &fwFixture{
   178  		t:              t,
   179  		ctx:            ctx,
   180  		cli:            cli,
   181  		TempDirFixture: tmpdir,
   182  		inputs:         WatchInputs{TiltfileManifestName: model.MainTiltfileManifestName},
   183  	}
   184  
   185  	t.Cleanup(func() {
   186  		cancel()
   187  	})
   188  
   189  	return f
   190  }
   191  
   192  type fileWatchDiffer struct {
   193  	expected v1alpha1.FileWatchSpec
   194  	actual   v1alpha1.FileWatchSpec
   195  }
   196  
   197  func (f fileWatchDiffer) String() string {
   198  	return cmp.Diff(f.expected, f.actual)
   199  }
   200  
   201  func (f *fwFixture) RequireFileWatchSpecEqual(targetID model.TargetID, spec v1alpha1.FileWatchSpec) {
   202  	f.t.Helper()
   203  
   204  	actualSet := ToFileWatchObjects(f.inputs, make(disableSourceMap))
   205  	actual, ok := actualSet[apis.SanitizeName(targetID.String())]
   206  	require.True(f.T(), ok, "No filewatch found for %s", targetID)
   207  	fwd := &fileWatchDiffer{expected: spec, actual: actual.GetSpec().(v1alpha1.FileWatchSpec)}
   208  	require.True(f.T(), equality.Semantic.DeepEqual(actual.GetSpec(), spec), "FileWatch spec was not equal: %v", fwd)
   209  }
   210  
   211  func (f *fwFixture) SetManifestLocalTarget(target model.LocalTarget) {
   212  	m := model.Manifest{Name: "foo"}.WithDeployTarget(target)
   213  	f.SetManifest(m)
   214  }
   215  
   216  func (f *fwFixture) SetManifest(m model.Manifest) {
   217  	for i, original := range f.inputs.Manifests {
   218  		if original.Name == m.Name {
   219  			f.inputs.Manifests[i] = m
   220  			return
   221  		}
   222  	}
   223  	f.inputs.Manifests = append(f.inputs.Manifests, m)
   224  }
   225  
   226  func (f *fwFixture) SetTiltIgnoreContents(s string) {
   227  	patterns, err := dockerignore.ReadAll(strings.NewReader(s))
   228  	assert.NoError(f.T(), err)
   229  	f.inputs.Tiltignore = model.Dockerignore{
   230  		LocalPath: f.Path(),
   231  		Patterns:  patterns,
   232  	}
   233  }