Commit 67b99f38 authored by ale's avatar ale

Add a test for configuration parsing

Covers the whole SourceSpec -> Dataset generation pipeline.
parent 998d2ef8
package tabacco
import (
"context"
"errors"
"fmt"
"os"
"strings"
"testing"
)
......@@ -52,3 +56,153 @@ func TestRandomSeed(t *testing.T) {
t.Fatal("failed to persist random seed")
}
}
func TestConfig_Parse(t *testing.T) {
type testdata struct {
config *Config
expectedOK bool
checkFn func(*runtimeAssets, []Dataset) error
}
tdd := []testdata{
// The following tests cover a few ways to generate
// the same two "user account" atoms as outlined in
// the README.
{
&Config{
SourceSpecs: []SourceSpec{
{
Name: "users/account1",
Handler: "file",
Atoms: []Atom{
{RelativePath: "/data/account1"},
},
},
{
Name: "users/account2",
Handler: "file",
Atoms: []Atom{
{RelativePath: "/data/account2"},
},
},
},
HandlerSpecs: []HandlerSpec{
{
Name: "file",
Type: "file",
Params: map[string]interface{}{"path": "/"},
},
},
},
true,
checkTwoUserAccountsAtoms,
},
{
&Config{
SourceSpecs: []SourceSpec{
{
Name: "users",
Handler: "file",
Atoms: []Atom{
{Name: "account1"},
{Name: "account2"},
},
},
},
HandlerSpecs: []HandlerSpec{
{
Name: "file",
Type: "file",
Params: map[string]interface{}{"path": "/data"},
},
},
},
true,
checkTwoUserAccountsAtoms,
},
{
&Config{
SourceSpecs: []SourceSpec{
{
Name: "users",
Handler: "file",
AtomsCommand: "echo account1; echo account2",
},
},
HandlerSpecs: []HandlerSpec{
{
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",
}
ra, err := td.config.parse()
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 {
datasets, err := parseAllSources(ra, td.config.SourceSpecs)
if err != nil {
t.Errorf("failed to parse sources %+v: %v", td.config.SourceSpecs, err)
}
if td.checkFn != nil {
if err := td.checkFn(ra, datasets); err != nil {
t.Errorf("check failed for config %+v: %v", td.config, err)
}
}
}
if ra != nil {
ra.Close()
}
}
}
func parseAllSources(ra *runtimeAssets, specs []SourceSpec) ([]Dataset, error) {
var out []Dataset
for _, spec := range specs {
ds, err := spec.Parse(context.Background())
if err != nil {
return nil, err
}
dsb := ra.handlerMap[ds.Handler].DatasetsForBackup(ds)
out = append(out, dsb...)
}
return out, nil
}
func checkTwoUserAccountsAtoms(ra *runtimeAssets, datasets []Dataset) error {
var numAtoms int
for _, ds := range datasets {
if ds.Name == "" {
return errors.New("empty dataset name")
}
if ds.Handler != "file" {
return fmt.Errorf("expected handler 'file', got '%s'", ds.Handler)
}
for _, atom := range ds.Atoms {
if !strings.HasPrefix(atom.SourcePath, "/data/") {
return fmt.Errorf("bad atom source path: %s", atom.SourcePath)
}
numAtoms++
}
}
if numAtoms != 2 {
return fmt.Errorf("expected 2 atoms across all datasets, got %d atoms across %d datasets", numAtoms, len(datasets))
}
return nil
}
......@@ -36,18 +36,6 @@ func (spec *SourceSpec) Parse(ctx context.Context) (ds Dataset, err error) {
}
}
// Verify that the dataset is syntactically correct: one case
// we want to catch is that of multiple atoms with empty
// RelativePaths.
if len(atoms) > 1 {
for _, atom := range atoms {
if atom.RelativePath == "" {
err = errors.New("can't have atoms with empty relative paths in multi-atom datasets")
return
}
}
}
ds = normalizeDataset(Dataset{
Name: spec.Name,
Handler: spec.Handler,
......@@ -102,5 +90,20 @@ func normalizeDataset(ds Dataset) Dataset {
if len(ds.Atoms) == 0 {
ds.Atoms = []Atom{Atom{}}
}
// If there are multiple atoms, and some (or all) have empty
// RelativePaths, just set their RelativePath equal to their
// Name.
if len(ds.Atoms) > 1 {
var atoms []Atom
for _, atom := range ds.Atoms {
if atom.RelativePath == "" {
atom.RelativePath = atom.Name
}
atoms = append(atoms, atom)
}
ds.Atoms = atoms
}
return ds
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment