config_test.go 4.86 KB
Newer Older
ale's avatar
ale committed
1 2 3
package tabacco

import (
ale's avatar
ale committed
4 5 6
	"context"
	"errors"
	"fmt"
ale's avatar
ale committed
7
	"log"
ale's avatar
ale committed
8 9
	"os"
	"testing"
ale's avatar
ale committed
10
	"time"
ale's avatar
ale committed
11 12 13 14 15 16 17
)

func TestReadConfig(t *testing.T) {
	conf, err := ReadConfig("testdata/agent.yml")
	if err != nil {
		t.Fatal("ReadConfig()", err)
	}
ale's avatar
ale committed
18 19
	if l := len(conf.SourceSpecs); l != 1 {
		t.Fatalf("couldn't read all SourceSpecs: got %d, expected %d", l, 1)
ale's avatar
ale committed
20
	}
ale's avatar
ale committed
21 22
	if l := len(conf.HandlerSpecs); l != 3 {
		t.Fatalf("couldn't read all HandlerSpecs: got %d, expected %d", l, 3)
ale's avatar
ale committed
23 24 25 26 27 28 29 30
	}
}

func TestConfigManager(t *testing.T) {
	conf, err := ReadConfig("testdata/agent.yml")
	if err != nil {
		t.Fatal("ReadConfig()", err)
	}
ale's avatar
ale committed
31
	log.Printf("loaded %d sources", len(conf.SourceSpecs))
ale's avatar
ale committed
32 33 34 35 36 37 38
	mgr, err := NewConfigManager(conf)
	if err != nil {
		t.Fatal("NewConfigManager()", err)
	}
	defer mgr.Close()

	// Test one of the accessor methods.
ale's avatar
ale committed
39 40
	if s := mgr.current().SourceSpecs(); len(s) != 1 {
		t.Fatalf("current().SourceSpecs() bad result: %+v", s)
ale's avatar
ale committed
41 42 43 44
	}

	// Test the Notify() mechanism by checking that it triggers
	// right away when setting up a new listener.
ale's avatar
ale committed
45 46 47 48 49 50
	tmr := time.NewTimer(1 * time.Second)
	select {
	case <-mgr.Notify():
	case <-tmr.C:
		t.Fatal("Notify() channel did not trigger")
	}
ale's avatar
ale committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
}

func TestRandomSeed(t *testing.T) {
	seedFile := ".test_seed"
	defer os.Remove(seedFile) // nolint

	seed := mustGetSeed(seedFile)
	if seed == 0 {
		t.Fatal("seed is zero")
	}
	seed2 := mustGetSeed(seedFile)
	if seed2 != seed {
		t.Fatal("failed to persist random seed")
	}
}
ale's avatar
ale committed
66 67 68 69 70

func TestConfig_Parse(t *testing.T) {
	type testdata struct {
		config     *Config
		expectedOK bool
ale's avatar
ale committed
71
		checkFn    func([]*Dataset) error
ale's avatar
ale committed
72 73 74 75 76 77 78
	}
	tdd := []testdata{
		// The following tests cover a few ways to generate
		// the same two "user account" atoms as outlined in
		// the README.
		{
			&Config{
ale's avatar
ale committed
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
				SourceSpecs: []*SourceSpec{
					&SourceSpec{
						Name:     "users",
						Handler:  "file",
						Schedule: "@random_every 24h",
						Datasets: []*DatasetSpec{
							&DatasetSpec{
								Atoms: []Atom{
									{Name: "account1"},
								},
							},
							{
								Atoms: []Atom{
									{Name: "account2"},
								},
							},
ale's avatar
ale committed
95 96 97
						},
					},
				},
ale's avatar
ale committed
98 99
				HandlerSpecs: []*HandlerSpec{
					&HandlerSpec{
ale's avatar
ale committed
100 101 102 103 104 105 106 107 108 109 110
						Name:   "file",
						Type:   "file",
						Params: map[string]interface{}{"path": "/"},
					},
				},
			},
			true,
			checkTwoUserAccountsAtoms,
		},
		{
			&Config{
ale's avatar
ale committed
111 112 113 114 115 116 117 118 119 120 121 122
				SourceSpecs: []*SourceSpec{
					&SourceSpec{
						Name:     "users",
						Handler:  "file",
						Schedule: "@random_every 24h",
						Datasets: []*DatasetSpec{
							&DatasetSpec{
								Atoms: []Atom{
									{Name: "account1"},
									{Name: "account2"},
								},
							},
ale's avatar
ale committed
123 124 125
						},
					},
				},
ale's avatar
ale committed
126 127
				HandlerSpecs: []*HandlerSpec{
					&HandlerSpec{
ale's avatar
ale committed
128 129 130 131 132 133 134 135 136 137 138
						Name:   "file",
						Type:   "file",
						Params: map[string]interface{}{"path": "/data"},
					},
				},
			},
			true,
			checkTwoUserAccountsAtoms,
		},
		{
			&Config{
ale's avatar
ale committed
139 140 141 142 143
				SourceSpecs: []*SourceSpec{
					&SourceSpec{
						Name:            "users",
						Handler:         "file",
						Schedule:        "@random_every 24h",
144
						DatasetsCommand: "echo '[{atoms: [{name: account1}, {name: account2}]}]'",
ale's avatar
ale committed
145 146
					},
				},
ale's avatar
ale committed
147 148
				HandlerSpecs: []*HandlerSpec{
					&HandlerSpec{
ale's avatar
ale committed
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
						Name:   "file",
						Type:   "file",
						Params: map[string]interface{}{"path": "/data"},
					},
				},
			},
			true,
			checkTwoUserAccountsAtoms,
		},
	}

	for _, td := range tdd {
		// Set a default repository config.
		td.config.Repository.Name = "default"
		td.config.Repository.Type = "restic"
		td.config.Repository.Params = map[string]interface{}{
			"uri":      "/tmp",
			"password": "hello",
		}

ale's avatar
ale committed
169
		parsed, err := td.config.parse()
ale's avatar
ale committed
170 171 172 173 174
		if err != nil && td.expectedOK {
			t.Errorf("unexpected error for config %+v: %v", td.config, err)
		} else if err == nil && !td.expectedOK {
			t.Errorf("missing error for config %+v", td.config)
		} else {
ale's avatar
ale committed
175
			datasets, err := parseAllSources(parsed.SourceSpecs())
ale's avatar
ale committed
176 177 178 179
			if err != nil {
				t.Errorf("failed to parse sources %+v: %v", td.config.SourceSpecs, err)
			}
			if td.checkFn != nil {
ale's avatar
ale committed
180
				if err := td.checkFn(datasets); err != nil {
ale's avatar
ale committed
181 182 183 184
					t.Errorf("check failed for config %+v: %v", td.config, err)
				}
			}
		}
ale's avatar
ale committed
185 186
		if parsed != nil {
			parsed.Close()
ale's avatar
ale committed
187 188 189 190
		}
	}
}

ale's avatar
ale committed
191
func parseAllSources(specs []*SourceSpec) ([]*Dataset, error) {
ale's avatar
ale committed
192
	var out []*Dataset
ale's avatar
ale committed
193 194 195 196 197
	for _, spec := range specs {
		ds, err := spec.Parse(context.Background())
		if err != nil {
			return nil, err
		}
ale's avatar
ale committed
198
		out = append(out, ds...)
ale's avatar
ale committed
199 200 201 202
	}
	return out, nil
}

ale's avatar
ale committed
203
func checkTwoUserAccountsAtoms(datasets []*Dataset) error {
ale's avatar
ale committed
204 205
	var numAtoms int
	for _, ds := range datasets {
206 207
		if ds.ID == "" {
			return errors.New("empty dataset ID")
ale's avatar
ale committed
208 209
		}
		for _, atom := range ds.Atoms {
210 211 212
			switch atom.Name {
			case "account1", "account2":
			default:
ale's avatar
ale committed
213
				return fmt.Errorf("bad atom name: %s", atom.Name)
ale's avatar
ale committed
214 215 216 217 218 219 220 221 222
			}
			numAtoms++
		}
	}
	if numAtoms != 2 {
		return fmt.Errorf("expected 2 atoms across all datasets, got %d atoms across %d datasets", numAtoms, len(datasets))
	}
	return nil
}