github.com/tilt-dev/tilt@v0.36.0/integration/tilt_ci_test.go (about) 1 //go:build integration 2 // +build integration 3 4 package integration 5 6 import ( 7 "bytes" 8 "fmt" 9 "os" 10 "strings" 11 "testing" 12 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 ) 16 17 type localResource struct { 18 name string 19 triggerMode string 20 autoInit bool 21 updateCmd string 22 serveCmd string 23 } 24 25 // TestTiltCI covers a variety of different permutations for local_resource + k8s_resource, in particular around 26 // auto_init and absence/presence of update cmd/serve_cmd (for local_resource). 27 // 28 // Critically, it ensures that `tilt ci` does not block indefinitely; there have been several regressions around 29 // this due to the subtlety in various states. 30 func TestTiltCI(t *testing.T) { 31 f := newK8sFixture(t, "tilt_ci") 32 f.SetRestrictedCredentials() 33 34 // dynamically generate a bunch of combinations of local_resource args and write out to 35 // `Tiltfile.generated` which is loaded in by the main/static Tiltfile (which contains 36 // hardcoded K8s definitions as there's fewer combinations there) 37 localResources := generateLocalResources() 38 generateTiltfile(f.fixture, localResources) 39 40 f.TiltCI() 41 42 // NOTE: the assertions don't use assert.Contains because on failure it'll re-print all the logs 43 // which have already been printed and make reading the test failure very difficult 44 logs := f.logs.String() 45 for _, lr := range localResources { 46 if !lr.autoInit { 47 assert.Falsef(t, strings.Contains(logs, lr.name), 48 "Resource %q had auto_init=False and should not have been seen", lr.name) 49 continue 50 } 51 52 if lr.updateCmd != "" { 53 assert.Truef(t, strings.Contains(logs, fmt.Sprintf("update for %s", lr.name)), 54 "Resource %q did not log via update_cmd", lr.name) 55 } 56 57 if lr.serveCmd != "" { 58 assert.Truef(t, strings.Contains(logs, fmt.Sprintf("serve for %s", lr.name)), 59 "Resource %q did not log via serve_cmd", lr.name) 60 } 61 } 62 63 for _, name := range []string{"k8s-server-disabled", "k8s-job-disabled"} { 64 assert.Falsef(t, strings.Contains(logs, fmt.Sprintf("Initial Build • %s", name)), 65 "Resource %q had auto_init=False and should not have been deployed", name) 66 } 67 68 assert.True(t, strings.Contains(logs, "k8s-server-e… │ Initial Build"), 69 "Resource k8s-server-enabled did not deploy") 70 assert.True(t, strings.Contains(logs, "k8s-job-enab… │ Initial Build"), 71 "Resource k8s-job-enabled did not deploy") 72 73 assert.True(t, strings.Contains(logs, "k8s-job-enabled finished"), 74 `Resource "k8s-job-enabled" did not log via container`) 75 76 assert.True(t, strings.Contains(logs, "k8s-server-enabled running"), 77 `Resource "k8s-server-enabled" did not log via container`) 78 } 79 80 func generateTiltfile(f *fixture, localResources []localResource) { 81 var out bytes.Buffer 82 out.WriteString("# AUTOMATICALLY GENERATED - DO NOT EDIT\n") 83 out.WriteString("# Run TestTiltCI to re-generate\n\n") 84 85 for _, lr := range localResources { 86 out.WriteString(lr.String()) 87 out.WriteString("\n") 88 } 89 90 err := os.WriteFile(f.testDirPath("Tiltfile.generated"), out.Bytes(), os.FileMode(0777)) 91 require.NoError(f.t, err, "Failed to write Tiltfile.generated") 92 } 93 94 func generateLocalResources() []localResource { 95 var localResources []localResource 96 for _, tm := range []string{"TRIGGER_MODE_AUTO", "TRIGGER_MODE_MANUAL"} { 97 for _, autoInit := range []bool{false, true} { 98 for _, hasUpdateCmd := range []bool{false, true} { 99 for _, hasServeCmd := range []bool{false, true} { 100 if !hasUpdateCmd && !hasServeCmd { 101 continue 102 } 103 lr := localResource{triggerMode: tm, autoInit: autoInit} 104 lr.name = "local" 105 if lr.triggerMode == "TRIGGER_MODE_MANUAL" { 106 lr.name += "-trigger_manual" 107 } 108 if !autoInit { 109 lr.name += "-no_auto_init" 110 } 111 if hasUpdateCmd { 112 lr.name += "-update_cmd" 113 } 114 if hasServeCmd { 115 lr.name += "-serve_cmd" 116 } 117 118 // we use the names in the commands so need to finish building the name before setting them 119 if hasUpdateCmd { 120 lr.updateCmd = fmt.Sprintf(`echo "update for %s"`, lr.name) 121 } 122 if hasServeCmd { 123 lr.serveCmd = fmt.Sprintf(`while true; do echo "serve for %s"; sleep 5000; done`, lr.name) 124 } 125 126 localResources = append(localResources, lr) 127 } 128 } 129 } 130 } 131 return localResources 132 } 133 134 func (lr localResource) String() string { 135 var args []string 136 args = append(args, fmt.Sprintf("name=%q", lr.name)) 137 var autoInitArgVal string 138 if lr.autoInit { 139 autoInitArgVal = "True" 140 } else { 141 autoInitArgVal = "False" 142 } 143 args = append(args, fmt.Sprintf("auto_init=%s", autoInitArgVal)) 144 args = append(args, fmt.Sprintf("trigger_mode=%s", lr.triggerMode)) 145 if lr.updateCmd != "" { 146 args = append(args, fmt.Sprintf("cmd=%q", lr.updateCmd)) 147 } 148 if lr.serveCmd != "" { 149 args = append(args, fmt.Sprintf("serve_cmd=%q", lr.serveCmd)) 150 } 151 152 var out strings.Builder 153 out.WriteString(`local_resource(`) 154 for i, arg := range args { 155 out.WriteString(arg) 156 if i != len(args)-1 { 157 out.WriteString(", ") 158 } 159 } 160 out.WriteString(")") 161 return out.String() 162 }