github.com/tilt-dev/tilt@v0.36.0/internal/controllers/core/extensionrepo/reconciler_test.go (about)

     1  package extensionrepo
     2  
     3  import (
     4  	"fmt"
     5  	"io/fs"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/spf13/afero"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  	"k8s.io/apimachinery/pkg/types"
    16  
    17  	"github.com/tilt-dev/tilt/internal/controllers/fake"
    18  	"github.com/tilt-dev/tilt/internal/xdg"
    19  	"github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1"
    20  )
    21  
    22  func TestInvalidRepo(t *testing.T) {
    23  	f := newFixture(t)
    24  	key := types.NamespacedName{Name: "default"}
    25  	repo := v1alpha1.ExtensionRepo{
    26  		ObjectMeta: metav1.ObjectMeta{
    27  			Name: key.Name,
    28  		},
    29  		Spec: v1alpha1.ExtensionRepoSpec{
    30  			URL: "x",
    31  		},
    32  	}
    33  	f.Create(&repo)
    34  	f.MustGet(key, &repo)
    35  	assert.Equal(t, "invalid: URL must start with 'https://': x", repo.Status.Error)
    36  	assert.Equal(t, "extensionrepo default: invalid: URL must start with 'https://': x\n", f.Stdout())
    37  }
    38  
    39  func TestUnknown(t *testing.T) {
    40  	f := newFixture(t)
    41  	f.dlr.downloadError = fmt.Errorf("X")
    42  	key := types.NamespacedName{Name: "default"}
    43  	repo := v1alpha1.ExtensionRepo{
    44  		ObjectMeta: metav1.ObjectMeta{
    45  			Name: key.Name,
    46  		},
    47  		Spec: v1alpha1.ExtensionRepoSpec{
    48  			URL: "https://github.com/tilt-dev/unknown-repo",
    49  		},
    50  	}
    51  	f.Create(&repo)
    52  	f.MustGet(key, &repo)
    53  	require.Contains(t, repo.Status.Error, "download error: waiting 5s before retrying")
    54  }
    55  
    56  func TestDefaultWeb(t *testing.T) {
    57  	f := newFixture(t)
    58  	key := types.NamespacedName{Name: "default"}
    59  	repo := v1alpha1.ExtensionRepo{
    60  		ObjectMeta: metav1.ObjectMeta{
    61  			Name: key.Name,
    62  		},
    63  		Spec: v1alpha1.ExtensionRepoSpec{
    64  			URL: "https://github.com/tilt-dev/tilt-extensions",
    65  		},
    66  	}
    67  	f.Create(&repo)
    68  	f.MustGet(key, &repo)
    69  	require.Equal(t, repo.Status.Error, "")
    70  	require.True(t, strings.HasSuffix(repo.Status.Path, "tilt-extensions"))
    71  	require.Equal(t, repo.Status.CheckoutRef, "fake-head")
    72  
    73  	info, err := os.Stat(repo.Status.Path)
    74  	require.NoError(t, err)
    75  	require.True(t, info.IsDir())
    76  	assert.Equal(t, 1, f.dlr.downloadCount)
    77  	assert.Equal(t, "", f.dlr.lastRefSync)
    78  
    79  	f.MustReconcile(key)
    80  	assert.Equal(t, 1, f.dlr.downloadCount)
    81  
    82  	f.Delete(&repo)
    83  
    84  	_, err = os.Stat(repo.Status.Path)
    85  	require.True(t, os.IsNotExist(err))
    86  }
    87  
    88  func TestDefaultFile(t *testing.T) {
    89  	f := newFixture(t)
    90  
    91  	path := filepath.Join(f.base.Dir, "my-repo")
    92  	_ = os.MkdirAll(path, os.FileMode(0755))
    93  
    94  	key := types.NamespacedName{Name: "default"}
    95  	repo := v1alpha1.ExtensionRepo{
    96  		ObjectMeta: metav1.ObjectMeta{
    97  			Name: key.Name,
    98  		},
    99  		Spec: v1alpha1.ExtensionRepoSpec{
   100  			URL: fmt.Sprintf("file://%s", path),
   101  		},
   102  	}
   103  	f.Create(&repo)
   104  	f.MustGet(key, &repo)
   105  	require.Equal(t, repo.Status.Error, "")
   106  	require.Equal(t, repo.Status.Path, path)
   107  }
   108  
   109  func TestRepoSync(t *testing.T) {
   110  	f := newFixture(t)
   111  
   112  	key := types.NamespacedName{Name: "default"}
   113  	repo := v1alpha1.ExtensionRepo{
   114  		ObjectMeta: metav1.ObjectMeta{
   115  			Name: key.Name,
   116  		},
   117  		Spec: v1alpha1.ExtensionRepoSpec{
   118  			URL: "https://github.com/tilt-dev/tilt-extensions",
   119  			Ref: "other-ref",
   120  		},
   121  	}
   122  	f.Create(&repo)
   123  	f.MustGet(key, &repo)
   124  	require.Equal(t, repo.Status.Error, "")
   125  	assert.Equal(t, 1, f.dlr.downloadCount)
   126  	assert.Equal(t, "other-ref", f.dlr.lastRefSync)
   127  	f.assertSteadyState(&repo)
   128  }
   129  
   130  func TestRepoSyncExisting(t *testing.T) {
   131  	f := newFixture(t)
   132  
   133  	key := types.NamespacedName{Name: "default"}
   134  	repo := v1alpha1.ExtensionRepo{
   135  		ObjectMeta: metav1.ObjectMeta{
   136  			Name: key.Name,
   137  		},
   138  		Spec: v1alpha1.ExtensionRepoSpec{
   139  			URL: "https://github.com/tilt-dev/tilt-extensions",
   140  			Ref: "other-ref",
   141  		},
   142  	}
   143  
   144  	f.dlr.Download("github.com/tilt-dev/tilt-extensions")
   145  	f.dlr.RefSync("github.com/tilt-dev/tilt-extensions", "other-ref")
   146  
   147  	f.Create(&repo)
   148  	f.MustGet(key, &repo)
   149  	require.Equal(t, repo.Status.Error, "")
   150  	assert.Equal(t, 1, f.dlr.downloadCount)
   151  	assert.Equal(t, "other-ref", f.dlr.lastRefSync)
   152  	f.assertSteadyState(&repo)
   153  }
   154  
   155  func TestRepoAlwaysSyncHead(t *testing.T) {
   156  	f := newFixture(t)
   157  
   158  	key := types.NamespacedName{Name: "default"}
   159  	repo := v1alpha1.ExtensionRepo{
   160  		ObjectMeta: metav1.ObjectMeta{
   161  			Name: key.Name,
   162  		},
   163  		Spec: v1alpha1.ExtensionRepoSpec{
   164  			URL: "https://github.com/tilt-dev/tilt-extensions",
   165  			Ref: "HEAD",
   166  		},
   167  	}
   168  
   169  	f.dlr.Download("github.com/tilt-dev/tilt-extensions")
   170  	f.dlr.RefSync("github.com/tilt-dev/tilt-extensions", "HEAD")
   171  
   172  	f.Create(&repo)
   173  	f.MustGet(key, &repo)
   174  	require.Equal(t, repo.Status.Error, "")
   175  	assert.Equal(t, 2, f.dlr.downloadCount)
   176  	assert.Equal(t, "HEAD", f.dlr.lastRefSync)
   177  	f.assertSteadyState(&repo)
   178  }
   179  
   180  func TestStale(t *testing.T) {
   181  	f := newFixture(t)
   182  
   183  	f.dlr.Download("github.com/tilt-dev/stale-repo")
   184  	f.dlr.downloadError = fmt.Errorf("fake error")
   185  
   186  	key := types.NamespacedName{Name: "default"}
   187  	repo := v1alpha1.ExtensionRepo{
   188  		ObjectMeta: metav1.ObjectMeta{
   189  			Name: key.Name,
   190  		},
   191  		Spec: v1alpha1.ExtensionRepoSpec{
   192  			URL: "https://github.com/tilt-dev/stale-repo",
   193  		},
   194  	}
   195  	f.Create(&repo)
   196  	f.MustGet(key, &repo)
   197  	require.Contains(t, repo.Status.Error, "")
   198  	require.Contains(t, repo.Status.StaleReason, "fake error")
   199  }
   200  
   201  type fixture struct {
   202  	*fake.ControllerFixture
   203  	r    *Reconciler
   204  	dlr  *fakeDownloader
   205  	base *xdg.FakeBase
   206  }
   207  
   208  func newFixture(t *testing.T) *fixture {
   209  	cfb := fake.NewControllerFixtureBuilder(t)
   210  	tmpDir := t.TempDir()
   211  	fs := afero.NewOsFs()
   212  	base := xdg.NewFakeBase(tmpDir, fs)
   213  	r, err := NewReconciler(cfb.Client, cfb.Store, base)
   214  	require.NoError(t, err)
   215  
   216  	dlr := &fakeDownloader{base: base, headRef: "fake-head"}
   217  	r.dlr = dlr
   218  
   219  	return &fixture{
   220  		ControllerFixture: cfb.Build(r),
   221  		r:                 r,
   222  		dlr:               dlr,
   223  		base:              base,
   224  	}
   225  }
   226  
   227  func (f *fixture) assertSteadyState(er *v1alpha1.ExtensionRepo) {
   228  	f.T().Helper()
   229  	f.MustReconcile(types.NamespacedName{Name: er.Name})
   230  	var er2 v1alpha1.ExtensionRepo
   231  	f.MustGet(types.NamespacedName{Name: er.Name}, &er2)
   232  	assert.Equal(f.T(), er.ResourceVersion, er2.ResourceVersion)
   233  }
   234  
   235  type fakeDownloader struct {
   236  	base xdg.Base
   237  
   238  	downloadError error
   239  	downloadCount int
   240  	lastRefSync   string
   241  	headRef       string
   242  }
   243  
   244  func (d *fakeDownloader) DestinationPath(pkg string) string {
   245  	result, _ := d.base.DataFile(pkg)
   246  	return result
   247  }
   248  
   249  func (d *fakeDownloader) Download(pkg string) (string, error) {
   250  
   251  	d.downloadCount += 1
   252  	if d.downloadError != nil {
   253  		return "", fmt.Errorf("download error %d: %v", d.downloadCount, d.downloadError)
   254  	}
   255  
   256  	path, err := d.base.DataFile(filepath.Join(pkg, "Tiltfile"))
   257  	if err != nil {
   258  		return "", err
   259  	}
   260  
   261  	_, err = os.Stat(path)
   262  	exists := err == nil
   263  	if exists && d.lastRefSync != "" && d.lastRefSync != "HEAD" {
   264  		// If the current disk state is checked out to a ref, then
   265  		// we expect Download() to fail.
   266  		// https://github.com/tilt-dev/tilt/issues/5508
   267  		return "", fmt.Errorf("You are not currently on a branch.")
   268  	}
   269  
   270  	err = os.WriteFile(path,
   271  		[]byte(fmt.Sprintf("Download count %d", d.downloadCount)), fs.FileMode(0777))
   272  	return "", err
   273  }
   274  
   275  func (d *fakeDownloader) HeadRef(pkg string) (string, error) {
   276  	return d.headRef, nil
   277  }
   278  
   279  func (d *fakeDownloader) RefSync(pkg string, ref string) error {
   280  	d.lastRefSync = ref
   281  	return nil
   282  }