<commit_before>package main

import (
	"bytes"
	"reflect"
	"sort"
	"strings"
	"testing"
)

func TestDependencies_empty(t *testing.T) {
	inTemplate := createTempfile(nil, t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}
	dependencies := template.Dependencies()

	if num := len(dependencies); num != 0 {
		t.Errorf("expected 0 Dependency, got: %d", num)
	}
}

func TestDependencies_funcs(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range service "release.webapp" }}{{ end }}
    {{ key "service/redis/maxconns" }}
    {{ range keyPrefix "service/redis/config" }}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}
	dependencies := template.Dependencies()

	if num := len(dependencies); num != 3 {
		t.Fatalf("expected 3 dependencies, got: %d", num)
	}
}

func TestDependencies_funcsDuplicates(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range service "release.webapp" }}{{ end }}
    {{ range service "release.webapp" }}{{ end }}
    {{ range service "release.webapp" }}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}
	dependencies := template.Dependencies()

	if num := len(dependencies); num != 1 {
		t.Fatalf("expected 1 Dependency, got: %d", num)
	}

	dependency, expected := dependencies[0], "release.webapp"
	if dependency.Key() != expected {
		t.Errorf("expected %q to equal %q", dependency.Key(), expected)
	}
}

func TestDependencies_funcsError(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range service "totally&not&a&valid&service" }}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	_, err := NewTemplate(inTemplate.Name())
	if err == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expected := "error calling service:"
	if !strings.Contains(err.Error(), expected) {
		t.Errorf("expected %q to contain %q", err.Error(), expected)
	}
}

func TestExecute_noTemplateContext(t *testing.T) {
	inTemplate := createTempfile(nil, t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	_, executeErr := template.Execute(nil)
	if executeErr == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expected := "templateContext must be given"
	if !strings.Contains(executeErr.Error(), expected) {
		t.Errorf("expected %q to contain %q", executeErr.Error(), expected)
	}
}

func TestExecute_dependenciesError(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range not_a_valid "template" }}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	_, err := NewTemplate(inTemplate.Name())
	if err == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expected := `template: out:2: function "not_a_valid" not defined`
	if !strings.Contains(err.Error(), expected) {
		t.Errorf("expected %q to contain %q", err.Error(), expected)
	}
}

func TestExecute_JSON(t *testing.T) {
	inTemplate := createTempfile([]byte(`{{(file "data.json" | json).foo}}`), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	context := &TemplateContext{
		File: map[string]string{
			"data.json": `{"foo":"bar"}`,
		},
	}

	data, err := template.Execute(context)

	if err != nil {
		t.Fatal(err)
	}

	if string(data) != "bar" {
		t.Errorf("expected %q to equal %q", string(data), "bar")
	}
}

func TestExecute_deepJSON(t *testing.T) {
	inTemplate := createTempfile([]byte(`
		{{ with $d := file "data.json" | json }}
		{{ $d.foo.bar.zip }}
		{{ end }}
	`), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	context := &TemplateContext{
		File: map[string]string{
			"data.json": `{
				"foo": {
					"bar": {
						"zip": "zap"
					}
				}
			}`,
		},
	}

	data, err := template.Execute(context)

	if err != nil {
		t.Fatal(err)
	}

	result, expected := strings.TrimSpace(string(data)), "zap"
	if result != expected {
		t.Errorf("expected %q to equal %q", result, expected)
	}
}

func TestExecute_byTag(t *testing.T) {
	inTemplate := createTempfile([]byte(`
{{ range $t, $s := service "webapp" | byTag }}{{ $t }}
{{ range $s }}	server {{.Name}} {{.Address}}:{{.Port}}
{{ end }}{{end}}
`), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	serviceWeb1 := &Service{
		Node:    "nyc-api-1",
		Address: "127.0.0.1",
		ID:      "web1",
		Name:    "web1",
		Port:    1234,
		Tags:    []string{"auth", "search"},
	}

	serviceWeb2 := &Service{
		Node:    "nyc-api-2",
		Address: "127.0.0.2",
		ID:      "web2",
		Name:    "web2",
		Port:    5678,
		Tags:    []string{"search"},
	}

	serviceWeb3 := &Service{
		Node:    "nyc-api-3",
		Address: "127.0.0.3",
		ID:      "web3",
		Name:    "web3",
		Port:    9012,
		Tags:    []string{"metric"},
	}

	context := &TemplateContext{
		Services: map[string][]*Service{
			"webapp": []*Service{serviceWeb1, serviceWeb2, serviceWeb3},
		},
	}

	contents, err := template.Execute(context)
	if err != nil {
		t.Fatal(err)
	}

	expected := bytes.TrimSpace([]byte(`
auth
	server web1 127.0.0.1:1234
metric
	server web3 127.0.0.3:9012
search
	server web1 127.0.0.1:1234
	server web2 127.0.0.2:5678
`))

	if !bytes.Equal(bytes.TrimSpace(contents), expected) {
		t.Errorf("expected \n%q\n to equal \n%q\n", bytes.TrimSpace(contents), expected)
	}
}

func TestExecute_missingService(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range service "release.webapp" }}{{ end }}
    {{ range service "production.webapp" }}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	context := &TemplateContext{
		Services: map[string][]*Service{
			"release.webapp": []*Service{},
		},
	}

	_, executeErr := template.Execute(context)
	if executeErr == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expected := "templateContext missing service `production.webapp'"
	if !strings.Contains(executeErr.Error(), expected) {
		t.Errorf("expected %q to contain %q", executeErr.Error(), expected)
	}
}

func TestExecute_missingKey(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ key "service/redis/maxconns" }}
    {{ key "service/redis/online" }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	context := &TemplateContext{
		Keys: map[string]string{
			"service/redis/maxconns": "3",
		},
	}

	_, executeErr := template.Execute(context)
	if executeErr == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expected := "templateContext missing key `service/redis/online'"
	if !strings.Contains(executeErr.Error(), expected) {
		t.Errorf("expected %q to contain %q", executeErr.Error(), expected)
	}
}

func TestExecute_missingKeyPrefix(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range keyPrefix "service/redis/config" }}{{ end }}
    {{ range keyPrefix "service/nginx/config" }}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	context := &TemplateContext{
		KeyPrefixes: map[string][]*KeyPair{
			"service/redis/config": []*KeyPair{},
		},
	}

	_, executeErr := template.Execute(context)
	if executeErr == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expected := "templateContext missing keyPrefix `service/nginx/config'"
	if !strings.Contains(executeErr.Error(), expected) {
		t.Errorf("expected %q to contain %q", executeErr.Error(), expected)
	}
}

func TestExecute_rendersServices(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range service "release.webapp" }}
    server {{.Name}} {{.Address}}:{{.Port}}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	serviceWeb1 := &Service{
		Node:    "nyc-worker-1",
		Address: "172.16.31.10",
		ID:      "web1",
		Name:    "web1",
		Port:    1234,
	}

	serviceWeb2 := &Service{
		Node:    "nyc-worker-2",
		Address: "456.456.456.456",
		ID:      "web2",
		Name:    "web2",
		Port:    5678,
	}

	context := &TemplateContext{
		Services: map[string][]*Service{
			"release.webapp": []*Service{serviceWeb1, serviceWeb2},
		},
	}

	contents, err := template.Execute(context)
	if err != nil {
		t.Fatal(err)
	}

	expected := bytes.TrimSpace([]byte(`
    server web1 172.16.31.10:1234
    server web2 456.456.456.456:5678
  `))

	if !bytes.Equal(bytes.TrimSpace(contents), expected) {
		t.Errorf("expected \n%q\n to equal \n%q\n", bytes.TrimSpace(contents), expected)
	}
}

func TestExecute_rendersKeys(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    minconns: {{ key "service/redis/minconns" }}
    maxconns: {{ key "service/redis/maxconns" }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	context := &TemplateContext{
		Keys: map[string]string{
			"service/redis/minconns": "2",
			"service/redis/maxconns": "11",
		},
	}

	contents, err := template.Execute(context)
	if err != nil {
		t.Fatal(err)
	}

	expected := []byte(`
    minconns: 2
    maxconns: 11
  `)
	if !bytes.Equal(contents, expected) {
		t.Errorf("expected \n%q\n to equal \n%q\n", contents, expected)
	}
}

func TestExecute_rendersKeyPrefixes(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range keyPrefix "service/redis/config" }}
    {{.Key}} {{.Value}}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	minconnsConfig := &KeyPair{
		Key:   "minconns",
		Value: "2",
	}

	maxconnsConfig := &KeyPair{
		Key:   "maxconns",
		Value: "11",
	}

	context := &TemplateContext{
		KeyPrefixes: map[string][]*KeyPair{
			"service/redis/config": []*KeyPair{minconnsConfig, maxconnsConfig},
		},
	}

	contents, err := template.Execute(context)
	if err != nil {
		t.Fatal(err)
	}

	expected := bytes.TrimSpace([]byte(`
    minconns 2
    maxconns 11
  `))
	if !bytes.Equal(bytes.TrimSpace(contents), expected) {
		t.Errorf("expected \n%q\n to equal \n%q\n", bytes.TrimSpace(contents), expected)
	}
}

func TestHashCode_returnsValue(t *testing.T) {
	template := &Template{path: "/foo/bar/blitz.ctmpl"}

	expected := "Template|/foo/bar/blitz.ctmpl"
	if template.HashCode() != expected {
		t.Errorf("expected %q to equal %q", template.HashCode(), expected)
	}
}

func TestServiceList_sorts(t *testing.T) {
	a := ServiceList{
		&Service{Node: "frontend01", ID: "1"},
		&Service{Node: "frontend01", ID: "2"},
		&Service{Node: "frontend02", ID: "1"},
	}
	b := ServiceList{
		&Service{Node: "frontend02", ID: "1"},
		&Service{Node: "frontend01", ID: "2"},
		&Service{Node: "frontend01", ID: "1"},
	}
	c := ServiceList{
		&Service{Node: "frontend01", ID: "2"},
		&Service{Node: "frontend01", ID: "1"},
		&Service{Node: "frontend02", ID: "1"},
	}

	sort.Stable(a)
	sort.Stable(b)
	sort.Stable(c)

	expected := ServiceList{
		&Service{Node: "frontend01", ID: "1"},
		&Service{Node: "frontend01", ID: "2"},
		&Service{Node: "frontend02", ID: "1"},
	}

	if !reflect.DeepEqual(a, expected) {
		t.Fatal("invalid sort")
	}

	if !reflect.DeepEqual(b, expected) {
		t.Fatal("invalid sort")
	}

	if !reflect.DeepEqual(c, expected) {
		t.Fatal("invalid sort")
	}
}
<commit_msg>Fix indentation in template tests<commit_after>package main

import (
	"bytes"
	"reflect"
	"sort"
	"strings"
	"testing"
)

func TestDependencies_empty(t *testing.T) {
	inTemplate := createTempfile(nil, t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}
	dependencies := template.Dependencies()

	if num := len(dependencies); num != 0 {
		t.Errorf("expected 0 Dependency, got: %d", num)
	}
}

func TestDependencies_funcs(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range service "release.webapp" }}{{ end }}
    {{ key "service/redis/maxconns" }}
    {{ range keyPrefix "service/redis/config" }}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}
	dependencies := template.Dependencies()

	if num := len(dependencies); num != 3 {
		t.Fatalf("expected 3 dependencies, got: %d", num)
	}
}

func TestDependencies_funcsDuplicates(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range service "release.webapp" }}{{ end }}
    {{ range service "release.webapp" }}{{ end }}
    {{ range service "release.webapp" }}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}
	dependencies := template.Dependencies()

	if num := len(dependencies); num != 1 {
		t.Fatalf("expected 1 Dependency, got: %d", num)
	}

	dependency, expected := dependencies[0], "release.webapp"
	if dependency.Key() != expected {
		t.Errorf("expected %q to equal %q", dependency.Key(), expected)
	}
}

func TestDependencies_funcsError(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range service "totally&not&a&valid&service" }}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	_, err := NewTemplate(inTemplate.Name())
	if err == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expected := "error calling service:"
	if !strings.Contains(err.Error(), expected) {
		t.Errorf("expected %q to contain %q", err.Error(), expected)
	}
}

func TestExecute_noTemplateContext(t *testing.T) {
	inTemplate := createTempfile(nil, t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	_, executeErr := template.Execute(nil)
	if executeErr == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expected := "templateContext must be given"
	if !strings.Contains(executeErr.Error(), expected) {
		t.Errorf("expected %q to contain %q", executeErr.Error(), expected)
	}
}

func TestExecute_dependenciesError(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range not_a_valid "template" }}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	_, err := NewTemplate(inTemplate.Name())
	if err == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expected := `template: out:2: function "not_a_valid" not defined`
	if !strings.Contains(err.Error(), expected) {
		t.Errorf("expected %q to contain %q", err.Error(), expected)
	}
}

func TestExecute_JSON(t *testing.T) {
	inTemplate := createTempfile([]byte(`{{(file "data.json" | json).foo}}`), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	context := &TemplateContext{
		File: map[string]string{
			"data.json": `{"foo":"bar"}`,
		},
	}

	data, err := template.Execute(context)

	if err != nil {
		t.Fatal(err)
	}

	if string(data) != "bar" {
		t.Errorf("expected %q to equal %q", string(data), "bar")
	}
}

func TestExecute_deepJSON(t *testing.T) {
	inTemplate := createTempfile([]byte(`
		{{ with $d := file "data.json" | json }}
		{{ $d.foo.bar.zip }}
		{{ end }}
	`), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	context := &TemplateContext{
		File: map[string]string{
			"data.json": `{
				"foo": {
					"bar": {
						"zip": "zap"
					}
				}
			}`,
		},
	}

	data, err := template.Execute(context)

	if err != nil {
		t.Fatal(err)
	}

	result, expected := strings.TrimSpace(string(data)), "zap"
	if result != expected {
		t.Errorf("expected %q to equal %q", result, expected)
	}
}

func TestExecute_byTag(t *testing.T) {
	inTemplate := createTempfile([]byte(`
		{{range $t, $s := service "webapp" | byTag}}{{$t}}
		{{range $s}}	server {{.Name}} {{.Address}}:{{.Port}}
		{{end}}{{end}}
	`), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	serviceWeb1 := &Service{
		Node:    "nyc-api-1",
		Address: "127.0.0.1",
		ID:      "web1",
		Name:    "web1",
		Port:    1234,
		Tags:    []string{"auth", "search"},
	}

	serviceWeb2 := &Service{
		Node:    "nyc-api-2",
		Address: "127.0.0.2",
		ID:      "web2",
		Name:    "web2",
		Port:    5678,
		Tags:    []string{"search"},
	}

	serviceWeb3 := &Service{
		Node:    "nyc-api-3",
		Address: "127.0.0.3",
		ID:      "web3",
		Name:    "web3",
		Port:    9012,
		Tags:    []string{"metric"},
	}

	context := &TemplateContext{
		Services: map[string][]*Service{
			"webapp": []*Service{serviceWeb1, serviceWeb2, serviceWeb3},
		},
	}

	contents, err := template.Execute(context)
	if err != nil {
		t.Fatal(err)
	}

	expected := bytes.TrimSpace([]byte(`
		auth
			server web1 127.0.0.1:1234
		metric
			server web3 127.0.0.3:9012
		search
			server web1 127.0.0.1:1234
			server web2 127.0.0.2:5678
	`))

	if !bytes.Equal(bytes.TrimSpace(contents), expected) {
		t.Errorf("expected \n%q\n to equal \n%q\n", bytes.TrimSpace(contents), expected)
	}
}

func TestExecute_missingService(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range service "release.webapp" }}{{ end }}
    {{ range service "production.webapp" }}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	context := &TemplateContext{
		Services: map[string][]*Service{
			"release.webapp": []*Service{},
		},
	}

	_, executeErr := template.Execute(context)
	if executeErr == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expected := "templateContext missing service `production.webapp'"
	if !strings.Contains(executeErr.Error(), expected) {
		t.Errorf("expected %q to contain %q", executeErr.Error(), expected)
	}
}

func TestExecute_missingKey(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ key "service/redis/maxconns" }}
    {{ key "service/redis/online" }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	context := &TemplateContext{
		Keys: map[string]string{
			"service/redis/maxconns": "3",
		},
	}

	_, executeErr := template.Execute(context)
	if executeErr == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expected := "templateContext missing key `service/redis/online'"
	if !strings.Contains(executeErr.Error(), expected) {
		t.Errorf("expected %q to contain %q", executeErr.Error(), expected)
	}
}

func TestExecute_missingKeyPrefix(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range keyPrefix "service/redis/config" }}{{ end }}
    {{ range keyPrefix "service/nginx/config" }}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	context := &TemplateContext{
		KeyPrefixes: map[string][]*KeyPair{
			"service/redis/config": []*KeyPair{},
		},
	}

	_, executeErr := template.Execute(context)
	if executeErr == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expected := "templateContext missing keyPrefix `service/nginx/config'"
	if !strings.Contains(executeErr.Error(), expected) {
		t.Errorf("expected %q to contain %q", executeErr.Error(), expected)
	}
}

func TestExecute_rendersServices(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range service "release.webapp" }}
    server {{.Name}} {{.Address}}:{{.Port}}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	serviceWeb1 := &Service{
		Node:    "nyc-worker-1",
		Address: "172.16.31.10",
		ID:      "web1",
		Name:    "web1",
		Port:    1234,
	}

	serviceWeb2 := &Service{
		Node:    "nyc-worker-2",
		Address: "456.456.456.456",
		ID:      "web2",
		Name:    "web2",
		Port:    5678,
	}

	context := &TemplateContext{
		Services: map[string][]*Service{
			"release.webapp": []*Service{serviceWeb1, serviceWeb2},
		},
	}

	contents, err := template.Execute(context)
	if err != nil {
		t.Fatal(err)
	}

	expected := bytes.TrimSpace([]byte(`
    server web1 172.16.31.10:1234
    server web2 456.456.456.456:5678
  `))

	if !bytes.Equal(bytes.TrimSpace(contents), expected) {
		t.Errorf("expected \n%q\n to equal \n%q\n", bytes.TrimSpace(contents), expected)
	}
}

func TestExecute_rendersKeys(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    minconns: {{ key "service/redis/minconns" }}
    maxconns: {{ key "service/redis/maxconns" }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	context := &TemplateContext{
		Keys: map[string]string{
			"service/redis/minconns": "2",
			"service/redis/maxconns": "11",
		},
	}

	contents, err := template.Execute(context)
	if err != nil {
		t.Fatal(err)
	}

	expected := []byte(`
    minconns: 2
    maxconns: 11
  `)
	if !bytes.Equal(contents, expected) {
		t.Errorf("expected \n%q\n to equal \n%q\n", contents, expected)
	}
}

func TestExecute_rendersKeyPrefixes(t *testing.T) {
	inTemplate := createTempfile([]byte(`
    {{ range keyPrefix "service/redis/config" }}
    {{.Key}} {{.Value}}{{ end }}
  `), t)
	defer deleteTempfile(inTemplate, t)

	template, err := NewTemplate(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	minconnsConfig := &KeyPair{
		Key:   "minconns",
		Value: "2",
	}

	maxconnsConfig := &KeyPair{
		Key:   "maxconns",
		Value: "11",
	}

	context := &TemplateContext{
		KeyPrefixes: map[string][]*KeyPair{
			"service/redis/config": []*KeyPair{minconnsConfig, maxconnsConfig},
		},
	}

	contents, err := template.Execute(context)
	if err != nil {
		t.Fatal(err)
	}

	expected := bytes.TrimSpace([]byte(`
    minconns 2
    maxconns 11
  `))
	if !bytes.Equal(bytes.TrimSpace(contents), expected) {
		t.Errorf("expected \n%q\n to equal \n%q\n", bytes.TrimSpace(contents), expected)
	}
}

func TestHashCode_returnsValue(t *testing.T) {
	template := &Template{path: "/foo/bar/blitz.ctmpl"}

	expected := "Template|/foo/bar/blitz.ctmpl"
	if template.HashCode() != expected {
		t.Errorf("expected %q to equal %q", template.HashCode(), expected)
	}
}

func TestServiceList_sorts(t *testing.T) {
	a := ServiceList{
		&Service{Node: "frontend01", ID: "1"},
		&Service{Node: "frontend01", ID: "2"},
		&Service{Node: "frontend02", ID: "1"},
	}
	b := ServiceList{
		&Service{Node: "frontend02", ID: "1"},
		&Service{Node: "frontend01", ID: "2"},
		&Service{Node: "frontend01", ID: "1"},
	}
	c := ServiceList{
		&Service{Node: "frontend01", ID: "2"},
		&Service{Node: "frontend01", ID: "1"},
		&Service{Node: "frontend02", ID: "1"},
	}

	sort.Stable(a)
	sort.Stable(b)
	sort.Stable(c)

	expected := ServiceList{
		&Service{Node: "frontend01", ID: "1"},
		&Service{Node: "frontend01", ID: "2"},
		&Service{Node: "frontend02", ID: "1"},
	}

	if !reflect.DeepEqual(a, expected) {
		t.Fatal("invalid sort")
	}

	if !reflect.DeepEqual(b, expected) {
		t.Fatal("invalid sort")
	}

	if !reflect.DeepEqual(c, expected) {
		t.Fatal("invalid sort")
	}
}

{'fraction_non_alphanumeric': 0.18130553656869447, 'fraction_numerical': 0.01845522898154477, 'mean_word_length': 4.013707431998286, 'pattern_counts': {'":': 28, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 18, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1951752', 'n_tokens_mistral': 9270, 'n_tokens_neox': 8508, 'n_words': 1834}