From bbea9342330ff020b8e3094c849db88719915132 Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Sat, 26 Dec 2020 12:54:42 +0100 Subject: Continued implementing config validation --- config/config.go | 29 +++++----- config/config_test.go | 43 ++++++++++++--- config/game.go | 2 +- config/menu.go | 47 ++++++++++++++++ config/test_data/duplicate_menu.yaml | 89 ++++++++++++++++++++++++++++++ config/test_data/non_existant_chopts.yaml | 80 +++++++++++++++++++++++++++ config/test_data/non_existant_game.yaml | 81 ++++++++++++++++++++++++++++ config/test_data/non_existant_menu.yaml | 72 +++++++++++++++++++++++++ config/test_data/unreachable_game.yaml | 90 +++++++++++++++++++++++++++++++ config/test_data/unreachable_menu.yaml | 87 ++++++++++++++++++++++++++++++ 10 files changed, 596 insertions(+), 24 deletions(-) create mode 100644 config/test_data/duplicate_menu.yaml create mode 100644 config/test_data/non_existant_chopts.yaml create mode 100644 config/test_data/non_existant_game.yaml create mode 100644 config/test_data/non_existant_menu.yaml create mode 100644 config/test_data/unreachable_game.yaml create mode 100644 config/test_data/unreachable_menu.yaml (limited to 'config') diff --git a/config/config.go b/config/config.go index fada1ea..1deaf04 100644 --- a/config/config.go +++ b/config/config.go @@ -17,38 +17,25 @@ type Config struct { } func (c *Config) validate() error { + // App if err := c.App.validate(); err != nil { return err } + // Menus if len(c.Menus) < 2 { return errors.New("A valid configuration needs at least two menu entries named anonymous and logged_in") } - found_anonymous_menu := false - found_logged_in_menu := false for k, v := range c.Menus { if err := v.validate(k); err != nil { return err } - if k == "anonymous" { - found_anonymous_menu = true - } - if k == "logged_in" { - found_logged_in_menu = true - } - } - if !found_anonymous_menu { - return errors.New("No anonymous menu declared") - } - if !found_logged_in_menu { - return errors.New("No logged_in menu declared") } + // Games for k, v := range c.Games { if err := v.validate(k); err != nil { return err } } - // TODO menu existence is tested in global config - // TODO game existence is tested in global config return nil } @@ -64,6 +51,14 @@ func LoadFile(path string) (config Config, err error) { if err = decoder.Decode(&config); err != nil { return } - err = config.validate() + if err = config.validate(); err != nil { + return + } + // If all looks good we validate menu consistency + for _, v := range config.Menus { + if err = v.validateConsistency(&config); err != nil { + return + } + } return } diff --git a/config/config_test.go b/config/config_test.go index 1fcc57c..5679d06 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -19,7 +19,6 @@ func TestLoadFile(t *testing.T) { t.Fatal("invalid_yaml config file failed without error") } - // TODO test non existant menu in action menu entries, and duplicate, and that anonymous and logged_in exist // TODO test non existant game in play actions, and duplicate //menuEntry = MenuEntry{ //Key: "p", @@ -31,26 +30,46 @@ func TestLoadFile(t *testing.T) { //} t.Cleanup(func() { os.RemoveAll("var/") }) - // Invalid App example + // Invalid App if _, err := LoadFile("test_data/invalid_app.yaml"); err == nil { t.Fatal("Invalid App entry should fail to load") } - // Not enough menus example + // Not enough menus if _, err := LoadFile("test_data/not_enough_menus.yaml"); err == nil { t.Fatal("not enough menu entries should fail to load") } - // Invalid Menus example + // Invalid Menus if _, err := LoadFile("test_data/invalid_menus.yaml"); err == nil { t.Fatal("Invalid menu entry should fail to load") } - // no anonymous Menu example + // no anonymous Menu if _, err := LoadFile("test_data/no_anonymous_menu.yaml"); err == nil { t.Fatal("Invalid menu entry should fail to load") } - // no logged_in Menu example + // no logged_in Menu if _, err := LoadFile("test_data/no_logged_in_menu.yaml"); err == nil { t.Fatal("Invalid menu entry should fail to load") } + // duplicate menu + if _, err := LoadFile("test_data/duplicate_menu.yaml"); err == nil { + t.Fatal("duplicate menu should fail to load") + } + // non existant menu action referenced + if _, err := LoadFile("test_data/non_existant_menu.yaml"); err == nil { + t.Fatal("menu entry referencing a non existant menu should fail to load") + } + // non existant game referenced in play action + if _, err := LoadFile("test_data/non_existant_game.yaml"); err == nil { + t.Fatal("menu entry referencing a non existant play action should fail to load") + } + // unreachable menu + if _, err := LoadFile("test_data/unreachable_menu.yaml"); err == nil { + t.Fatal("unreachable menu should fail to load") + } + // unreachable game + if _, err := LoadFile("test_data/unreachable_game.yaml"); err == nil { + t.Fatal("unreachable game should fail to load") + } // Complexe example config, err := LoadFile("../example/complete.yaml") @@ -137,6 +156,18 @@ func TestLoadFile(t *testing.T) { }, }, }, + "options": Menu{ + Banner: "Options%n=======", + XOffset: 5, + YOffset: 2, + MenuEntries: []MenuEntry{ + MenuEntry{ + Key: "z", + Label: "back", + Action: "menu logged_in", + }, + }, + }, }, Games: map[string]Game{ "nethack3.7": Game{ diff --git a/config/game.go b/config/game.go index 5520659..c34c697 100644 --- a/config/game.go +++ b/config/game.go @@ -21,7 +21,7 @@ type Game struct { Env map[string]string `yaml:"Env"` } -func (a *Game) validate(name string) error { +func (g *Game) validate(name string) error { // Game name if ok := reValidGameName.MatchString(name); !ok { return errors.New("Invalid Game name, must match regex `^[\\w\\._]+$` : " + name) diff --git a/config/menu.go b/config/menu.go index 160afa3..004c7fe 100644 --- a/config/menu.go +++ b/config/menu.go @@ -2,6 +2,7 @@ package config import ( "regexp" + "strings" "github.com/pkg/errors" ) @@ -61,6 +62,52 @@ func (m *Menu) validate(name string) error { return nil } +func (m *Menu) validateConsistency(c *Config) error { + // Necessary menus + if _, ok := c.Menus["anonymous"]; !ok { + return errors.New("No anonymous menu declared") + } + if _, ok := c.Menus["logged_in"]; !ok { + return errors.New("No logged_in menu declared") + } + // Validate actions + menus := make(map[string]bool) + menus["anonymous"] = true + menus["logged_in"] = true + playable := make(map[string]bool) + for k, v := range c.Menus { + for _, e := range v.MenuEntries { + tokens := strings.Split(e.Action, " ") + switch tokens[0] { + case "menu": + if _, ok := c.Menus[tokens[1]]; ok { + menus[tokens[1]] = true + } else { + return errors.New("menu action " + tokens[1] + " in menu " + k + " does not exist") + } + case "play": + if _, ok := c.Games[tokens[1]]; ok { + playable[tokens[1]] = true + } else { + return errors.New("play action " + tokens[1] + " in menu " + k + " does not exist") + } + } + } + } + // Check for unreachables + for k, _ := range c.Menus { + if _, ok := menus[k]; !ok { + return errors.New("unreachable menu : " + k) + } + } + for k, _ := range c.Games { + if _, ok := playable[k]; !ok { + return errors.New("unplayable game : " + k) + } + } + return nil +} + func (m *MenuEntry) validate() error { // Key if ok := reValidKey.MatchString(m.Key); !ok { diff --git a/config/test_data/duplicate_menu.yaml b/config/test_data/duplicate_menu.yaml new file mode 100644 index 0000000..d2a00e2 --- /dev/null +++ b/config/test_data/duplicate_menu.yaml @@ -0,0 +1,89 @@ +App: + WorkingDirectory: var/ + MaxUsers: 512 + AllowRegistration: true + MaxNickLen: 15 + MenuMaxIdleTime: 600 + PostLoginCommands: + - mkdir %w/userdata/%u + - mkdir %w/userdata/%u/dumplog + - mkdir %w/userdata/%u/ttyrec + +Menus: + anonymous: + Banner: 'Shell Game Launcher - Anonymous access%n======================================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: t + Label: test + Action: menu test + - Key: l + Label: login + Action: login + - Key: r + Label: register + Action: register + - Key: w + Label: watch + Action: watch_menu + - Key: q + Label: quit + Action: quit + logged_in: + Banner: 'Shell Game Launcher%n===================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: p + Label: play Nethack 3.7 + Action: play nethack3.7 + - Key: o + Label: edit game options + Action: menu options + - Key: w + Label: watch + Action: watch + - Key: r + Label: replay + Action: replay + - Key: c + Label: change password + Action: passwd + - Key: m + Label: change email + Action: chmail + - Key: q + Label: quit + Action: quit + test: + Banner: 'Shell Game Launcher%n===================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: p + Label: play Nethack 3.7 + Action: play nethack3.7 + test: + Banner: 'Duplicate Shell Game Launcher%n===================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: p + Label: play Nethack 3.7 + Action: play nethack3.7 + +Games: + nethack3.7: + ChrootPath: /opt/nethack + FileMode: "0666" + ScoreCommands: + - exec /games/nethack -s all + - wait + Commands: + - cp /games/var/save/%u%n.gz /games/var/save/%u%n.gz.bak + - exec /games/nethack -u %n + Env: + NETHACKOPTIONS: "@%ruserdata/%n/%n.nhrc" + + diff --git a/config/test_data/non_existant_chopts.yaml b/config/test_data/non_existant_chopts.yaml new file mode 100644 index 0000000..d3ec2a5 --- /dev/null +++ b/config/test_data/non_existant_chopts.yaml @@ -0,0 +1,80 @@ +App: + WorkingDirectory: var/ + MaxUsers: 512 + AllowRegistration: true + MaxNickLen: 15 + MenuMaxIdleTime: 600 + PostLoginCommands: + - mkdir %w/userdata/%u + - mkdir %w/userdata/%u/dumplog + - mkdir %w/userdata/%u/ttyrec + +Menus: + anonymous: + Banner: 'Shell Game Launcher - Anonymous access%n======================================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: l + Label: login + Action: login + - Key: r + Label: register + Action: register + - Key: w + Label: watch + Action: watch_menu + - Key: q + Label: quit + Action: quit + logged_in: + Banner: 'Shell Game Launcher%n===================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: p + Label: play Nethack 3.7 + Action: play nethack3.7 + - Key: o + Label: edit game options + Action: menu options + - Key: w + Label: watch + Action: watch + - Key: r + Label: replay + Action: replay + - Key: c + Label: change password + Action: passwd + - Key: m + Label: change email + Action: chmail + - Key: q + Label: quit + Action: quit + options: + Banner: 'Options%n=======' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: a + Label: edit options for Nethack 3.7 + Action: chopts invalid + - Key: z + Label: back + Action: menu logged_in + +Games: + nethack3.7: + ChrootPath: /opt/nethack + FileMode: "0666" + ScoreCommands: + - exec /games/nethack -s all + - wait + Commands: + - cp /games/var/save/%u%n.gz /games/var/save/%u%n.gz.bak + - exec /games/nethack -u %n + Env: + NETHACKOPTIONS: "@%ruserdata/%n/%n.nhrc" + diff --git a/config/test_data/non_existant_game.yaml b/config/test_data/non_existant_game.yaml new file mode 100644 index 0000000..c386e1a --- /dev/null +++ b/config/test_data/non_existant_game.yaml @@ -0,0 +1,81 @@ +App: + WorkingDirectory: var/ + MaxUsers: 512 + AllowRegistration: true + MaxNickLen: 15 + MenuMaxIdleTime: 600 + PostLoginCommands: + - mkdir %w/userdata/%u + - mkdir %w/userdata/%u/dumplog + - mkdir %w/userdata/%u/ttyrec + +Menus: + anonymous: + Banner: 'Shell Game Launcher - Anonymous access%n======================================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: l + Label: login + Action: login + - Key: r + Label: register + Action: register + - Key: w + Label: watch + Action: watch_menu + - Key: q + Label: quit + Action: quit + logged_in: + Banner: 'Shell Game Launcher%n===================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: p + Label: play Nethack 3.7 + Action: play invalid + - Key: o + Label: edit game options + Action: menu options + - Key: w + Label: watch + Action: watch + - Key: r + Label: replay + Action: replay + - Key: c + Label: change password + Action: passwd + - Key: m + Label: change email + Action: chmail + - Key: q + Label: quit + Action: quit + options: + Banner: 'Options%n=======' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: a + Label: edit options for Nethack 3.7 + Action: chopts nethack3.7 + - Key: z + Label: back + Action: menu logged_in + +Games: + nethack3.7: + ChrootPath: /opt/nethack + FileMode: "0666" + ScoreCommands: + - exec /games/nethack -s all + - wait + Commands: + - cp /games/var/save/%u%n.gz /games/var/save/%u%n.gz.bak + - exec /games/nethack -u %n + Env: + NETHACKOPTIONS: "@%ruserdata/%n/%n.nhrc" + + diff --git a/config/test_data/non_existant_menu.yaml b/config/test_data/non_existant_menu.yaml new file mode 100644 index 0000000..2890ee8 --- /dev/null +++ b/config/test_data/non_existant_menu.yaml @@ -0,0 +1,72 @@ +App: + WorkingDirectory: var/ + MaxUsers: 512 + AllowRegistration: true + MaxNickLen: 15 + MenuMaxIdleTime: 600 + PostLoginCommands: + - mkdir %w/userdata/%u + - mkdir %w/userdata/%u/dumplog + - mkdir %w/userdata/%u/ttyrec + +Menus: + anonymous: + Banner: 'Shell Game Launcher - Anonymous access%n======================================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: t + Label: test + Action: menu test + - Key: l + Label: login + Action: login + - Key: r + Label: register + Action: register + - Key: w + Label: watch + Action: watch_menu + - Key: q + Label: quit + Action: quit + logged_in: + Banner: 'Shell Game Launcher%n===================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: p + Label: play Nethack 3.7 + Action: play nethack3.7 + - Key: o + Label: edit game options + Action: menu invalid + - Key: w + Label: watch + Action: watch + - Key: r + Label: replay + Action: replay + - Key: c + Label: change password + Action: passwd + - Key: m + Label: change email + Action: chmail + - Key: q + Label: quit + Action: quit + +Games: + nethack3.7: + ChrootPath: /opt/nethack + FileMode: "0666" + ScoreCommands: + - exec /games/nethack -s all + - wait + Commands: + - cp /games/var/save/%u%n.gz /games/var/save/%u%n.gz.bak + - exec /games/nethack -u %n + Env: + NETHACKOPTIONS: "@%ruserdata/%n/%n.nhrc" + diff --git a/config/test_data/unreachable_game.yaml b/config/test_data/unreachable_game.yaml new file mode 100644 index 0000000..a6a8977 --- /dev/null +++ b/config/test_data/unreachable_game.yaml @@ -0,0 +1,90 @@ +App: + WorkingDirectory: var/ + MaxUsers: 512 + AllowRegistration: true + MaxNickLen: 15 + MenuMaxIdleTime: 600 + PostLoginCommands: + - mkdir %w/userdata/%u + - mkdir %w/userdata/%u/dumplog + - mkdir %w/userdata/%u/ttyrec + +Menus: + anonymous: + Banner: 'Shell Game Launcher - Anonymous access%n======================================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: l + Label: login + Action: login + - Key: r + Label: register + Action: register + - Key: w + Label: watch + Action: watch_menu + - Key: q + Label: quit + Action: quit + logged_in: + Banner: 'Shell Game Launcher%n===================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: p + Label: play Nethack 3.7 + Action: play nethack3.7 + - Key: o + Label: edit game options + Action: menu options + - Key: w + Label: watch + Action: watch + - Key: r + Label: replay + Action: replay + - Key: c + Label: change password + Action: passwd + - Key: m + Label: change email + Action: chmail + - Key: q + Label: quit + Action: quit + options: + Banner: 'Options%n=======' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: a + Label: edit options for Nethack 3.7 + Action: chopts nethack3.7 + - Key: z + Label: back + Action: menu logged_in + +Games: + nethack3.7: + ChrootPath: /opt/nethack + FileMode: "0666" + ScoreCommands: + - exec /games/nethack -s all + - wait + Commands: + - cp /games/var/save/%u%n.gz /games/var/save/%u%n.gz.bak + - exec /games/nethack -u %n + Env: + NETHACKOPTIONS: "@%ruserdata/%n/%n.nhrc" + unreachable: + ChrootPath: /opt/nethack + FileMode: "0666" + ScoreCommands: + - exec /games/nethack -s all + - wait + Commands: + - cp /games/var/save/%u%n.gz /games/var/save/%u%n.gz.bak + - exec /games/nethack -u %n + Env: + NETHACKOPTIONS: "@%ruserdata/%n/%n.nhrc" diff --git a/config/test_data/unreachable_menu.yaml b/config/test_data/unreachable_menu.yaml new file mode 100644 index 0000000..27da191 --- /dev/null +++ b/config/test_data/unreachable_menu.yaml @@ -0,0 +1,87 @@ +App: + WorkingDirectory: var/ + MaxUsers: 512 + AllowRegistration: true + MaxNickLen: 15 + MenuMaxIdleTime: 600 + PostLoginCommands: + - mkdir %w/userdata/%u + - mkdir %w/userdata/%u/dumplog + - mkdir %w/userdata/%u/ttyrec + +Menus: + anonymous: + Banner: 'Shell Game Launcher - Anonymous access%n======================================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: l + Label: login + Action: login + - Key: r + Label: register + Action: register + - Key: w + Label: watch + Action: watch_menu + - Key: q + Label: quit + Action: quit + logged_in: + Banner: 'Shell Game Launcher%n===================' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: p + Label: play Nethack 3.7 + Action: play nethack3.7 + - Key: o + Label: edit game options + Action: menu options + - Key: w + Label: watch + Action: watch + - Key: r + Label: replay + Action: replay + - Key: c + Label: change password + Action: passwd + - Key: m + Label: change email + Action: chmail + - Key: q + Label: quit + Action: quit + options: + Banner: 'Options%n=======' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: a + Label: edit options for Nethack 3.7 + Action: chopts nethack3.7 + - Key: z + Label: back + Action: menu logged_in + unreachable: + Banner: 'Options%n=======' + XOffset: 5 + YOffset: 2 + MenuEntries: + - Key: a + Label: edit options for Nethack 3.7 + Action: chopts nethack3.7 + +Games: + nethack3.7: + ChrootPath: /opt/nethack + FileMode: "0666" + ScoreCommands: + - exec /games/nethack -s all + - wait + Commands: + - cp /games/var/save/%u%n.gz /games/var/save/%u%n.gz.bak + - exec /games/nethack -u %n + Env: + NETHACKOPTIONS: "@%ruserdata/%n/%n.nhrc" -- cgit v1.2.3