github.com/splunk/dan1-qbec@v0.7.3/internal/commands/show_test.go (about) 1 /* 2 Copyright 2019 Splunk Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package commands 18 19 import ( 20 "encoding/base64" 21 "regexp" 22 "strings" 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26 "github.com/stretchr/testify/require" 27 ) 28 29 func TestShowBasic(t *testing.T) { 30 s := newScaffold(t) 31 defer s.reset() 32 err := s.executeCommand("show", "dev") 33 require.Nil(t, err) 34 out, err := s.yamlOutput() 35 require.Nil(t, err) 36 a := assert.New(t) 37 a.True(len(out) > 0) 38 s.assertOutputLineMatch(regexp.MustCompile(`^\s+name: svc2`)) 39 s.assertOutputLineMatch(regexp.MustCompile(`^\s+qbec\.io/application: example1`)) 40 s.assertOutputLineMatch(regexp.MustCompile(`^\s+qbec\.io/component: service2`)) 41 s.assertOutputLineMatch(regexp.MustCompile(`^\s+qbec\.io/environment: dev`)) 42 pos1 := strings.Index(s.stdout(), "name: foo-system") 43 a.True(pos1 > 0) 44 pos2 := strings.Index(s.stdout(), "name: 100-default") 45 a.True(pos2 > 0) 46 a.True(pos1 < pos2) // namespace before psp in std sort 47 } 48 49 func TestShowBasicClean(t *testing.T) { 50 s := newScaffold(t) 51 defer s.reset() 52 err := s.executeCommand("show", "dev", "--clean") 53 require.Nil(t, err) 54 out, err := s.yamlOutput() 55 require.Nil(t, err) 56 a := assert.New(t) 57 a.True(len(out) > 0) 58 s.assertOutputLineMatch(regexp.MustCompile(`\s+name: svc2`)) 59 s.assertOutputLineNoMatch(regexp.MustCompile(`qbec\.io/application`)) 60 s.assertOutputLineNoMatch(regexp.MustCompile(`qbec\.io/component`)) 61 s.assertOutputLineNoMatch(regexp.MustCompile(`qbec\.io/environment`)) 62 pos1 := strings.Index(s.stdout(), "name: foo-system") 63 a.True(pos1 > 0) 64 pos2 := strings.Index(s.stdout(), "name: 100-default") 65 a.True(pos2 > 0) 66 a.True(pos1 < pos2) // namespace before psp in std sort 67 } 68 69 func TestShowApplySort(t *testing.T) { 70 s := newScaffold(t) 71 defer s.reset() 72 err := s.executeCommand("show", "dev", "--sort-apply") 73 require.Nil(t, err) 74 out, err := s.yamlOutput() 75 require.Nil(t, err) 76 assert.True(t, len(out) > 0) 77 pos1 := strings.Index(s.stdout(), "name: foo-system") 78 pos2 := strings.Index(s.stdout(), "name: 100-default") 79 assert.True(t, pos1 > pos2) // namespace after psp in apply sort 80 } 81 82 func TestShowApplySortBaseline(t *testing.T) { 83 s := newScaffold(t) 84 defer s.reset() 85 err := s.executeCommand("show", "_", "--sort-apply") 86 require.Nil(t, err) 87 out, err := s.yamlOutput() 88 require.Nil(t, err) 89 a := assert.New(t) 90 a.True(len(out) > 0) 91 pos1 := strings.Index(s.stdout(), "name: foo-system") 92 pos2 := strings.Index(s.stdout(), "name: 100-default") 93 a.True(pos2 > pos1) // no apply sort 94 a.Contains(s.stderr(), "[warn] cannot sort in apply order for baseline environment") 95 } 96 97 func TestShowBasicJSON(t *testing.T) { 98 s := newScaffold(t) 99 defer s.reset() 100 err := s.executeCommand("show", "dev", "-o", "json") 101 require.Nil(t, err) 102 var data interface{} 103 err = s.jsonOutput(&data) 104 require.Nil(t, err) 105 s.assertOutputLineMatch(regexp.MustCompile(`\s+"name": "svc2-cm"`)) 106 } 107 108 func TestShowObjects(t *testing.T) { 109 s := newScaffold(t) 110 defer s.reset() 111 err := s.executeCommand("show", "dev", "-O") 112 require.Nil(t, err) 113 s.assertOutputLineMatch(regexp.MustCompile(`service2\s+ConfigMap\s+svc2-cm\s+bar-system`)) 114 s.assertOutputLineMatch(regexp.MustCompile(`cluster-objects\s+Namespace\s+bar-system`)) 115 } 116 117 func TestShowObjectsAsYAML(t *testing.T) { 118 s := newScaffold(t) 119 defer s.reset() 120 err := s.executeCommand("show", "dev", "-O", "-o", "yaml") 121 require.Nil(t, err) 122 out, err := s.yamlOutput() 123 require.Nil(t, err) 124 assert.True(t, len(out) > 0) 125 s.assertOutputLineMatch(regexp.MustCompile(`\s+name: svc2`)) 126 } 127 128 func TestShowObjectsAsJSON(t *testing.T) { 129 s := newScaffold(t) 130 defer s.reset() 131 err := s.executeCommand("show", "dev", "-O", "-o", "json") 132 require.Nil(t, err) 133 var data interface{} 134 err = s.jsonOutput(&data) 135 require.Nil(t, err) 136 s.assertOutputLineMatch(regexp.MustCompile(`\s+"name": "svc2-cm"`)) 137 } 138 139 func TestShowObjectsComponentFilter(t *testing.T) { 140 s := newScaffold(t) 141 defer s.reset() 142 err := s.executeCommand("show", "dev", "-c", "cluster-objects") 143 require.Nil(t, err) 144 out, err := s.yamlOutput() 145 require.Nil(t, err) 146 assert.True(t, len(out) > 0) 147 s.assertOutputLineMatch(regexp.MustCompile(`\s+name: bar-system`)) 148 s.assertOutputLineNoMatch(regexp.MustCompile(`\s+name: svc2`)) 149 } 150 151 func TestShowObjectsComponentFilter2(t *testing.T) { 152 s := newScaffold(t) 153 defer s.reset() 154 err := s.executeCommand("show", "dev", "-C", "cluster-objects") 155 require.Nil(t, err) 156 out, err := s.yamlOutput() 157 require.Nil(t, err) 158 assert.True(t, len(out) > 0) 159 s.assertOutputLineNoMatch(regexp.MustCompile(`\s+name: bar-system`)) 160 s.assertOutputLineMatch(regexp.MustCompile(`\s+name: svc2`)) 161 } 162 163 func TestShowObjectsKindFilter(t *testing.T) { 164 s := newScaffold(t) 165 defer s.reset() 166 err := s.executeCommand("show", "dev", "-k", "secret") 167 require.Nil(t, err) 168 out, err := s.yamlOutput() 169 require.Nil(t, err) 170 assert.True(t, len(out) > 0) 171 s.assertOutputLineNoMatch(regexp.MustCompile(`\s+name: bar-system`)) 172 s.assertOutputLineMatch(regexp.MustCompile(`\s+name: svc2-secret`)) 173 } 174 175 func TestShowObjectsKindFilter2(t *testing.T) { 176 s := newScaffold(t) 177 defer s.reset() 178 err := s.executeCommand("show", "dev", "-K", "secret") 179 require.Nil(t, err) 180 out, err := s.yamlOutput() 181 require.Nil(t, err) 182 assert.True(t, len(out) > 0) 183 s.assertOutputLineNoMatch(regexp.MustCompile(`\s+name: svc2-secret`)) 184 s.assertOutputLineMatch(regexp.MustCompile(`\s+name: bar-system`)) 185 } 186 187 func TestShowObjectsKindFilter3(t *testing.T) { 188 s := newScaffold(t) 189 defer s.reset() 190 err := s.executeCommand("show", "dev", "-k", "garbage") 191 require.Nil(t, err) 192 out, err := s.yamlOutput() 193 require.Nil(t, err) 194 assert.True(t, len(out) == 0) 195 assert.Contains(t, s.stderr(), "matches for kind filter, check for typos and abbreviations") 196 } 197 198 func TestShowHiddenSecrets(t *testing.T) { 199 s := newScaffold(t) 200 defer s.reset() 201 secretValue := base64.StdEncoding.EncodeToString([]byte("bar")) 202 redactedValue := base64.RawStdEncoding.EncodeToString([]byte("redacted.")) 203 err := s.executeCommand("show", "dev", "-k", "secret") 204 require.Nil(t, err) 205 s.assertOutputLineMatch(regexp.MustCompile(redactedValue)) 206 s.assertOutputLineNoMatch(regexp.MustCompile(secretValue)) 207 } 208 209 func TestShowOpenSecrets(t *testing.T) { 210 s := newScaffold(t) 211 defer s.reset() 212 secretValue := base64.StdEncoding.EncodeToString([]byte("bar")) 213 redactedValue := base64.RawStdEncoding.EncodeToString([]byte("redacted.")) 214 err := s.executeCommand("show", "dev", "-k", "secret", "-S") 215 require.Nil(t, err) 216 s.assertOutputLineNoMatch(regexp.MustCompile(redactedValue)) 217 s.assertOutputLineMatch(regexp.MustCompile(secretValue)) 218 } 219 220 func TestShowNegative(t *testing.T) { 221 tests := []struct { 222 name string 223 args []string 224 asserter func(s *scaffold, err error) 225 dir string 226 }{ 227 { 228 name: "no env", 229 args: []string{"show"}, 230 asserter: func(s *scaffold, err error) { 231 a := assert.New(s.t) 232 a.True(isUsageError(err)) 233 a.Equal("exactly one environment required", err.Error()) 234 }, 235 }, 236 { 237 name: "2 envs", 238 args: []string{"show", "dev", "prod"}, 239 asserter: func(s *scaffold, err error) { 240 a := assert.New(s.t) 241 a.True(isUsageError(err)) 242 a.Equal("exactly one environment required", err.Error()) 243 }, 244 }, 245 { 246 name: "bad env", 247 args: []string{"show", "foo"}, 248 asserter: func(s *scaffold, err error) { 249 a := assert.New(s.t) 250 a.False(isUsageError(err)) 251 a.Equal("invalid environment \"foo\"", err.Error()) 252 }, 253 }, 254 { 255 name: "bad format", 256 args: []string{"show", "dev", "-o", "table"}, 257 asserter: func(s *scaffold, err error) { 258 a := assert.New(s.t) 259 a.True(isUsageError(err)) 260 a.Equal(`invalid output format: "table"`, err.Error()) 261 }, 262 }, 263 { 264 name: "c and C", 265 args: []string{"show", "dev", "-c", "cluster-objects", "-C", "service2"}, 266 asserter: func(s *scaffold, err error) { 267 a := assert.New(s.t) 268 a.True(isUsageError(err)) 269 a.Equal(`cannot include as well as exclude components, specify one or the other`, err.Error()) 270 }, 271 }, 272 { 273 name: "k and K", 274 args: []string{"show", "dev", "-k", "namespace", "-K", "secret"}, 275 asserter: func(s *scaffold, err error) { 276 a := assert.New(s.t) 277 a.True(isUsageError(err)) 278 a.Equal(`cannot include as well as exclude kinds, specify one or the other`, err.Error()) 279 }, 280 }, 281 { 282 name: "duplicate objects", 283 args: []string{"show", "dev"}, 284 asserter: func(s *scaffold, err error) { 285 a := assert.New(s.t) 286 a.True(IsRuntimeError(err)) 287 a.Equal(`duplicate objects ConfigMap cm1 (component: x) and ConfigMap cm1 (component: y)`, err.Error()) 288 }, 289 dir: "testdata/dups", 290 }, 291 } 292 for _, test := range tests { 293 t.Run(test.name, func(t *testing.T) { 294 s := newCustomScaffold(t, test.dir) 295 defer s.reset() 296 err := s.executeCommand(test.args...) 297 require.NotNil(t, err) 298 test.asserter(s, err) 299 }) 300 } 301 }