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