github.com/tarrant/terraform@v0.3.8-0.20150402012457-f68c9eee638e/config/interpolate_funcs_test.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "reflect" 8 "testing" 9 10 "github.com/hashicorp/terraform/config/lang" 11 "github.com/hashicorp/terraform/config/lang/ast" 12 ) 13 14 func TestInterpolateFuncConcat(t *testing.T) { 15 testFunction(t, testFunctionConfig{ 16 Cases: []testFunctionCase{ 17 { 18 `${concat("foo", "bar")}`, 19 "foobar", 20 false, 21 }, 22 23 { 24 `${concat("foo")}`, 25 "foo", 26 false, 27 }, 28 29 { 30 `${concat()}`, 31 nil, 32 true, 33 }, 34 }, 35 }) 36 } 37 38 func TestInterpolateFuncFile(t *testing.T) { 39 tf, err := ioutil.TempFile("", "tf") 40 if err != nil { 41 t.Fatalf("err: %s", err) 42 } 43 path := tf.Name() 44 tf.Write([]byte("foo")) 45 tf.Close() 46 defer os.Remove(path) 47 48 testFunction(t, testFunctionConfig{ 49 Cases: []testFunctionCase{ 50 { 51 fmt.Sprintf(`${file("%s")}`, path), 52 "foo", 53 false, 54 }, 55 56 // Invalid path 57 { 58 `${file("/i/dont/exist")}`, 59 nil, 60 true, 61 }, 62 63 // Too many args 64 { 65 `${file("foo", "bar")}`, 66 nil, 67 true, 68 }, 69 }, 70 }) 71 } 72 73 func TestInterpolateFuncFormat(t *testing.T) { 74 testFunction(t, testFunctionConfig{ 75 Cases: []testFunctionCase{ 76 { 77 `${format("hello")}`, 78 "hello", 79 false, 80 }, 81 82 { 83 `${format("hello %s", "world")}`, 84 "hello world", 85 false, 86 }, 87 88 { 89 `${format("hello %d", 42)}`, 90 "hello 42", 91 false, 92 }, 93 94 { 95 `${format("hello %05d", 42)}`, 96 "hello 00042", 97 false, 98 }, 99 100 { 101 `${format("hello %05d", 12345)}`, 102 "hello 12345", 103 false, 104 }, 105 }, 106 }) 107 } 108 109 func TestInterpolateFuncJoin(t *testing.T) { 110 testFunction(t, testFunctionConfig{ 111 Cases: []testFunctionCase{ 112 { 113 `${join(",")}`, 114 nil, 115 true, 116 }, 117 118 { 119 `${join(",", "foo")}`, 120 "foo", 121 false, 122 }, 123 124 /* 125 TODO 126 { 127 `${join(",", "foo", "bar")}`, 128 "foo,bar", 129 false, 130 }, 131 */ 132 133 { 134 fmt.Sprintf(`${join(".", "%s")}`, 135 fmt.Sprintf( 136 "foo%sbar%sbaz", 137 InterpSplitDelim, 138 InterpSplitDelim)), 139 "foo.bar.baz", 140 false, 141 }, 142 }, 143 }) 144 } 145 146 func TestInterpolateFuncReplace(t *testing.T) { 147 testFunction(t, testFunctionConfig{ 148 Cases: []testFunctionCase{ 149 // Regular search and replace 150 { 151 `${replace("hello", "hel", "bel")}`, 152 "bello", 153 false, 154 }, 155 156 // Search string doesn't match 157 { 158 `${replace("hello", "nope", "bel")}`, 159 "hello", 160 false, 161 }, 162 163 // Regular expression 164 { 165 `${replace("hello", "/l/", "L")}`, 166 "heLLo", 167 false, 168 }, 169 170 { 171 `${replace("helo", "/(l)/", "$1$1")}`, 172 "hello", 173 false, 174 }, 175 176 // Bad regexp 177 { 178 `${replace("helo", "/(l/", "$1$1")}`, 179 nil, 180 true, 181 }, 182 }, 183 }) 184 } 185 186 func TestInterpolateFuncSplit(t *testing.T) { 187 testFunction(t, testFunctionConfig{ 188 Cases: []testFunctionCase{ 189 { 190 `${split(",")}`, 191 nil, 192 true, 193 }, 194 195 { 196 `${split(",", "foo")}`, 197 "foo", 198 false, 199 }, 200 201 { 202 `${split(".", "foo.bar.baz")}`, 203 fmt.Sprintf( 204 "foo%sbar%sbaz", 205 InterpSplitDelim, 206 InterpSplitDelim), 207 false, 208 }, 209 }, 210 }) 211 } 212 213 func TestInterpolateFuncLookup(t *testing.T) { 214 testFunction(t, testFunctionConfig{ 215 Vars: map[string]ast.Variable{ 216 "var.foo.bar": ast.Variable{ 217 Value: "baz", 218 Type: ast.TypeString, 219 }, 220 }, 221 Cases: []testFunctionCase{ 222 { 223 `${lookup("foo", "bar")}`, 224 "baz", 225 false, 226 }, 227 228 // Invalid key 229 { 230 `${lookup("foo", "baz")}`, 231 nil, 232 true, 233 }, 234 235 // Too many args 236 { 237 `${lookup("foo", "bar", "baz")}`, 238 nil, 239 true, 240 }, 241 }, 242 }) 243 } 244 245 func TestInterpolateFuncElement(t *testing.T) { 246 testFunction(t, testFunctionConfig{ 247 Cases: []testFunctionCase{ 248 { 249 fmt.Sprintf(`${element("%s", "1")}`, 250 "foo"+InterpSplitDelim+"baz"), 251 "baz", 252 false, 253 }, 254 255 { 256 `${element("foo", "0")}`, 257 "foo", 258 false, 259 }, 260 261 // Invalid index should wrap vs. out-of-bounds 262 { 263 fmt.Sprintf(`${element("%s", "2")}`, 264 "foo"+InterpSplitDelim+"baz"), 265 "foo", 266 false, 267 }, 268 269 // Too many args 270 { 271 fmt.Sprintf(`${element("%s", "0", "2")}`, 272 "foo"+InterpSplitDelim+"baz"), 273 nil, 274 true, 275 }, 276 }, 277 }) 278 } 279 280 type testFunctionConfig struct { 281 Cases []testFunctionCase 282 Vars map[string]ast.Variable 283 } 284 285 type testFunctionCase struct { 286 Input string 287 Result interface{} 288 Error bool 289 } 290 291 func testFunction(t *testing.T, config testFunctionConfig) { 292 for i, tc := range config.Cases { 293 ast, err := lang.Parse(tc.Input) 294 if err != nil { 295 t.Fatalf("%d: err: %s", i, err) 296 } 297 298 out, _, err := lang.Eval(ast, langEvalConfig(config.Vars)) 299 if (err != nil) != tc.Error { 300 t.Fatalf("%d: err: %s", i, err) 301 } 302 303 if !reflect.DeepEqual(out, tc.Result) { 304 t.Fatalf( 305 "%d: bad output for input: %s\n\nOutput: %#v", 306 i, tc.Input, out) 307 } 308 } 309 }