| new file mode 100644 |
| index 0000000..b586eee |
| --- /dev/null |
| +++ b/sources_test.go |
| @@ -0,0 +1,102 @@ |
| +package fiesta |
| + |
| +import ( |
| + "encoding/json" |
| + "io/fs" |
| + "os" |
| + "strconv" |
| + "testing" |
| +) |
| + |
| +// TestSourcesIsSourcesJSON: Sources() is hack/seed/sources.json, key |
| +// for key and field for field. The file is read from disk here, not |
| +// through the embed, so the test compares the API with the record |
| +// rather than with itself; a field the record has and Source lacks |
| +// fails by name, as does a key either side lacks. |
| +func TestSourcesIsSourcesJSON(t *testing.T) { |
| + b, err := os.ReadFile("hack/seed/sources.json") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + var record map[string]map[string]json.RawMessage |
| + if err := json.Unmarshal(b, &record); err != nil { |
| + t.Fatalf("hack/seed/sources.json: %v", err) |
| + } |
| + got := Sources() |
| + if len(record) < 160 { |
| + t.Fatalf("hack/seed/sources.json holds %d example states, want the seeded 160 or more; the comparison would be over too little", len(record)) |
| + } |
| + for _, key := range sortedNames(record) { |
| + source, ok := got[key] |
| + if !ok { |
| + t.Errorf("Sources() has no %s, which sources.json records", key) |
| + continue |
| + } |
| + fields := map[string]string{ |
| + "kind": strconv.Quote(source.Kind), |
| + "source": strconv.Quote(source.Source), |
| + "locale": strconv.Quote(source.Locale), |
| + "state": strconv.Itoa(source.State), |
| + } |
| + if source.View != "" { |
| + fields["view"] = strconv.Quote(source.View) |
| + } |
| + for _, field := range sortedNames(record[key]) { |
| + if want := string(record[key][field]); fields[field] != want { |
| + t.Errorf("Sources()[%q] has %s %s, and sources.json %s", key, field, fields[field], want) |
| + } |
| + } |
| + for field := range fields { |
| + if _, ok := record[key][field]; !ok { |
| + t.Errorf("Sources()[%q] has a %s, and sources.json none", key, field) |
| + } |
| + } |
| + } |
| + for _, key := range sortedNames(got) { |
| + if _, ok := record[key]; !ok { |
| + t.Errorf("Sources() has %s, which sources.json does not record", key) |
| + } |
| + } |
| +} |
| + |
| +// TestEverySourceIsAnExampleState: each key Sources() returns is an |
| +// example and one of its states, as examples/examples.json lists them, |
| +// so a caller can open the example a source names. |
| +func TestEverySourceIsAnExampleState(t *testing.T) { |
| + var manifest struct { |
| + Examples []struct { |
| + Name string |
| + States []struct{ ID string } |
| + } |
| + } |
| + b, err := fs.ReadFile(FS(), "examples/examples.json") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := json.Unmarshal(b, &manifest); err != nil { |
| + t.Fatal(err) |
| + } |
| + states := map[string]bool{} |
| + for _, example := range manifest.Examples { |
| + for _, state := range example.States { |
| + states[example.Name+"/"+state.ID] = true |
| + } |
| + } |
| + for _, key := range sortedNames(Sources()) { |
| + if !states[key] { |
| + t.Errorf("Sources() has %s, which is no example state in examples/examples.json", key) |
| + } |
| + } |
| +} |
| + |
| +// TestSourcesIsACopy: a caller that edits the map it was given does not |
| +// edit the next caller's. |
| +func TestSourcesIsACopy(t *testing.T) { |
| + first := Sources() |
| + for key := range first { |
| + delete(first, key) |
| + } |
| + if len(Sources()) == 0 { |
| + t.Error("emptying the map Sources() returned emptied the next call's") |
| + } |
| +} |